Skip to content

Commit fc71ca4

Browse files
committed
feat(mouse): Implement Cursor Capture functionality (#1369)
The Mouse Cursor Capture has 4 modes "None": Game window does not capture the cursor "InGame": Game window captures the cursor when playing and observing "Always": Game window captures the cursor always in menus and game "Auto": Applies mode "InGame" when Windowed, "Always" when Fullscreen Adds new option "CursorCaptureMode" to Options.ini which uses the above option strings.
1 parent 5856a3c commit fc71ca4

File tree

24 files changed

+642
-103
lines changed

24 files changed

+642
-103
lines changed

Generals/Code/GameEngine/Include/Common/UserPreferences.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
//-----------------------------------------------------------------------------
3939
#include "Common/STLTypedefs.h"
4040

41+
enum CursorCaptureMode CPP_11(: Int);
42+
4143
//-----------------------------------------------------------------------------
4244
// PUBLIC TYPES ///////////////////////////////////////////////////////////////
4345
//-----------------------------------------------------------------------------
@@ -90,6 +92,7 @@ class OptionPreferences : public UserPreferences
9092
void setOnlineIPAddress(UnsignedInt IP); // convenience function
9193
Bool getAlternateMouseModeEnabled(void); // convenience function
9294
Real getScrollFactor(void); // convenience function
95+
CursorCaptureMode getCursorCaptureMode() const;
9396
Bool getSendDelay(void); // convenience function
9497
Int getFirewallBehavior(void); // convenience function
9598
Short getFirewallPortAllocationDelta(void); // convenience function

Generals/Code/GameEngine/Include/GameClient/Mouse.h

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@
5959

6060
// FORWARD REFERENCES /////////////////////////////////////////////////////////
6161

62+
enum GameMode CPP_11(: Int);
63+
6264
// TYPE DEFINES ///////////////////////////////////////////////////////////////
6365

6466
enum MouseButtonState CPP_11(: Int)
@@ -139,13 +141,43 @@ class CursorInfo
139141
Int numDirections; //number of directions for cursors like scrolling/panning.
140142
};
141143

144+
enum CursorCaptureMode CPP_11(: Int)
145+
{
146+
CursorCaptureMode_None, // Does not capture the cursor
147+
CursorCaptureMode_InGame, // Captures the cursor when playing and observing
148+
CursorCaptureMode_Always, // Captures the cursor always in menus and game
149+
CursorCaptureMode_Auto, // Applies mode "InGame" when Windowed, "Always" when Fullscreen
150+
151+
CursorCaptureMode_Count,
152+
CursorCaptureMode_Default = CursorCaptureMode_Auto,
153+
};
154+
155+
extern const char* const TheCursorCaptureModeNames[];
156+
142157
// Mouse ----------------------------------------------------------------------
143-
/** Class interface for working with a mouse pointing device */
158+
// Class interface for working with a mouse pointing device
159+
//
160+
// TheSuperHackers @feature xezon 26/07/2025 Implements mouse cursor capture
161+
// functionality. The Mouse class handles most of the logic for it internally.
144162
//-----------------------------------------------------------------------------
145163
class Mouse : public SubsystemInterface
146164
{
147165

148-
public: // enumerations and types
166+
// enumerations and types
167+
168+
typedef UnsignedInt CursorCaptureBlockReasonInt;
169+
170+
enum CursorCaptureBlockReason
171+
{
172+
CursorCaptureBlockReason_NoInit,
173+
CursorCaptureBlockReason_NoGame,
174+
CursorCaptureBlockReason_Paused,
175+
CursorCaptureBlockReason_Unfocused,
176+
177+
CursorCaptureBlockReason_Count
178+
};
179+
180+
public:
149181

150182
// ----------------------------------------------------------------------------------------------
151183
/** If you update this enum make sure you update CursorININames[] */
@@ -228,10 +260,12 @@ class Mouse : public SubsystemInterface
228260
RM_W3D, //W3D model tied to frame rate.
229261
RM_POLYGON, //alpha blended polygon tied to frame rate.
230262
RM_DX8, //hardware cursor independent of frame rate.
263+
231264
RM_MAX // keep this last.
232265
};
233266

234-
static const char *RedrawModeName[RM_MAX];
267+
static const char *const CursorCaptureBlockReasonNames[];
268+
static const char *const RedrawModeName[];
235269

236270
CursorInfo m_cursorInfo[NUM_MOUSE_CURSORS];
237271

@@ -255,8 +289,8 @@ class Mouse : public SubsystemInterface
255289
virtual void setPosition( Int x, Int y ); ///< set the mouse position
256290
virtual void setCursor( MouseCursor cursor ) = 0; ///< set mouse cursor
257291

258-
virtual void capture( void ) = 0; ///< capture the mouse
259-
virtual void releaseCapture( void ) = 0; ///< release mouse capture
292+
void setCursorCaptureMode(CursorCaptureMode mode); ///< set the rules for the mouse capture
293+
void refreshCursorCapture(); ///< refresh the mouse capture
260294

261295
// access methods for the mouse data
262296
const MouseIO *getMouseStatus( void ) { return &m_currMouse; } ///< get current mouse status
@@ -278,7 +312,12 @@ class Mouse : public SubsystemInterface
278312
Int getCursorIndex( const AsciiString& name );
279313
void resetTooltipDelay( void );
280314

315+
virtual void loseFocus();
316+
virtual void regainFocus();
317+
281318
void mouseNotifyResolutionChange(void);
319+
void onGameModeChanged(GameMode prev, GameMode next);
320+
void onGamePaused(Bool paused);
282321

283322
Bool isClick(const ICoord2D *anchor, const ICoord2D *dest, UnsignedInt previousMouseClick, UnsignedInt currentMouseClick);
284323

@@ -308,6 +347,14 @@ class Mouse : public SubsystemInterface
308347

309348
protected:
310349

350+
void initCapture();
351+
Bool canCapture() const;
352+
void unblockCapture(CursorCaptureBlockReason reason);
353+
void blockCapture(CursorCaptureBlockReason reason);
354+
355+
virtual void capture( void ) = 0; ///< capture the mouse
356+
virtual void releaseCapture( void ) = 0; ///< release mouse capture
357+
311358
/// you must implement getting a buffered mouse event from you device here
312359
virtual UnsignedByte getMouseEvent( MouseIO *result, Bool flush ) = 0;
313360

@@ -324,7 +371,7 @@ class Mouse : public SubsystemInterface
324371

325372
UnsignedByte m_numButtons; ///< number of buttons on this mouse
326373
UnsignedByte m_numAxes; ///< number of axes this mouse has
327-
Bool m_forceFeedback; ///< set to TRUE if mouse supprots force feedback
374+
Bool m_forceFeedback; ///< set to TRUE if mouse supports force feedback
328375

329376
UnicodeString m_tooltipString; ///< tooltip text
330377
DisplayString *m_tooltipDisplayString; ///< tooltipDisplayString
@@ -368,6 +415,9 @@ class Mouse : public SubsystemInterface
368415

369416
Int m_eventsThisFrame;
370417

418+
CursorCaptureMode m_cursorCaptureMode;
419+
CursorCaptureBlockReasonInt m_captureBlockReasonBits;
420+
371421
}; // end class Mouse
372422

373423
// TheSuperHackers @feature helmutbuhler 17/05/2025

Generals/Code/GameEngine/Include/GameLogic/GameLogic.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ class GameLogic : public SubsystemInterface, public Snapshot
173173
Bool isInInternetGame( void );
174174
Bool isInShellGame( void );
175175
Bool isInMultiplayerGame( void );
176+
177+
static Bool isInInteractiveGame(GameMode mode) { return mode != GAME_NONE && mode != GAME_SHELL; }
178+
176179
Bool isLoadingGame();
177180
void enableScoring(Bool score) { m_isScoringEnabled = score; }
178181
Bool isScoringEnabled() const { return m_isScoringEnabled; }

Generals/Code/GameEngine/Source/Common/GameEngine.cpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -366,11 +366,6 @@ void GameEngine::init()
366366
initSubsystem(TheWritableGlobalData, "TheWritableGlobalData", TheWritableGlobalData, &xferCRC, "Data\\INI\\Default\\GameData.ini", "Data\\INI\\GameData.ini");
367367
TheWritableGlobalData->parseCustomDefinition();
368368

369-
// TheSuperHackers @bugfix helmutbuhler 14/04/2025
370-
// Pump messages during startup to ensure that the application window is correctly
371-
// positioned on slower computers and in debug builds by a later call to SetWindowPos.
372-
// It is unclear what the issue with SetWindowPos is when it fails to reposition the window.
373-
serviceWindowsOS();
374369

375370

376371
#if defined(RTS_DEBUG)

Generals/Code/GameEngine/Source/GameClient/GUI/GUICallbacks/Menus/OptionsMenu.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,24 @@ Real OptionPreferences::getScrollFactor(void)
346346
return factor/100.0f;
347347
}
348348

349+
CursorCaptureMode OptionPreferences::getCursorCaptureMode() const
350+
{
351+
CursorCaptureMode mode = CursorCaptureMode_Default;
352+
OptionPreferences::const_iterator it = find("CursorCaptureMode");
353+
if (it != end())
354+
{
355+
for (Int i = 0; i < CursorCaptureMode_Count; ++i)
356+
{
357+
if (stricmp(it->second.str(), TheCursorCaptureModeNames[i]) == 0)
358+
{
359+
mode = static_cast<CursorCaptureMode>(i);
360+
break;
361+
}
362+
}
363+
}
364+
return mode;
365+
}
366+
349367
Bool OptionPreferences::usesSystemMapDir(void)
350368
{
351369
OptionPreferences::const_iterator it = find("UseSystemMapDir");
@@ -1140,6 +1158,13 @@ static void saveOptions( void )
11401158
TheWritableGlobalData->m_useAlternateMouse = GadgetCheckBoxIsChecked(checkAlternateMouse);
11411159
(*pref)["UseAlternateMouse"] = TheWritableGlobalData->m_useAlternateMouse ? AsciiString("yes") : AsciiString("no");
11421160

1161+
// TheSuperHackers @todo Add combo box ?
1162+
{
1163+
CursorCaptureMode mode = pref->getCursorCaptureMode();
1164+
(*pref)["CursorCaptureMode"] = TheCursorCaptureModeNames[mode];
1165+
TheMouse->setCursorCaptureMode(mode);
1166+
}
1167+
11431168
//-------------------------------------------------------------------------------------------------
11441169
// scroll speed val
11451170
val = GadgetSliderGetPosition(sliderScrollSpeed);

Generals/Code/GameEngine/Source/GameClient/GameClient.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ GameClient::~GameClient()
183183
delete TheFontLibrary;
184184
TheFontLibrary = NULL;
185185

186+
TheMouse->reset();
186187
delete TheMouse;
187188
TheMouse = NULL;
188189

Generals/Code/GameEngine/Source/GameClient/InGameUI.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2810,7 +2810,6 @@ void InGameUI::setScrolling( Bool isScrolling )
28102810

28112811
if (isScrolling)
28122812
{
2813-
TheMouse->capture();
28142813
setMouseCursor( Mouse::SCROLL );
28152814

28162815
// break any camera locks
@@ -2820,7 +2819,6 @@ void InGameUI::setScrolling( Bool isScrolling )
28202819
else
28212820
{
28222821
setMouseCursor( Mouse::ARROW );
2823-
TheMouse->releaseCapture();
28242822
}
28252823

28262824
m_isScrolling = isScrolling;
@@ -3019,9 +3017,6 @@ void InGameUI::placeBuildAvailable( const ThingTemplate *build, Drawable *buildD
30193017

30203018
Drawable *draw;
30213019

3022-
// capture the mouse for our window, windows is lame and changes it if we don't
3023-
TheMouse->capture();
3024-
30253020
// hack for changing cursor
30263021
setMouseCursor( Mouse::CROSS );
30273022

@@ -3070,7 +3065,6 @@ void InGameUI::placeBuildAvailable( const ThingTemplate *build, Drawable *buildD
30703065
m_mouseModeCursor = Mouse::ARROW;
30713066
}
30723067

3073-
TheMouse->releaseCapture();
30743068
setMouseCursor( Mouse::ARROW );
30753069
setPlacementStart( NULL );
30763070

0 commit comments

Comments
 (0)