Skip to content

bugfix(mouse): Fix bad drag tolerances with high scroll speed factors#2823

Open
xezon wants to merge 2 commits into
TheSuperHackers:mainfrom
xezon:xezon/fix-drag-tolerance
Open

bugfix(mouse): Fix bad drag tolerances with high scroll speed factors#2823
xezon wants to merge 2 commits into
TheSuperHackers:mainfrom
xezon:xezon/fix-drag-tolerance

Conversation

@xezon

@xezon xezon commented Jun 22, 2026

Copy link
Copy Markdown

Merge with Rebase

This change fixes the bad drag tolerances with high scroll speed factors, which was introduced by #1501 and is especially pronounced in this Project because players are encouraged to set way higher scroll factors after #1026 when higher frame rates no longer increase the camera movement.

@xezon xezon added Bug Something is not working right, typically is user facing Critical Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour Input labels Jun 22, 2026
@greptile-apps

greptile-apps Bot commented Jun 22, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes click-detection becoming too permissive at high keyboard scroll speed factors by scaling the drag tolerance inversely with the scroll factor ratio (defaultFactor / currentFactor) and switching from a rectangular to a circular pixel-distance check. It also cleans up dead code: the 3D camera-position check in SelectionXlat was computing a delta that was never actually passed into isClick, so it is removed along with its backing field.

  • Mouse::isClick now calls getDragToleranceAdjustedForScrollFactor() and correctly uses the returned Real dragTolerance in the condition, replacing both the raw m_dragTolerance rectangle check and the unused camera-position guard.
  • BaseType.h gains operator+, operator-, +=, -=, set, and lengthSqr() on all four coordinate structs (Coord2D, ICoord2D, Coord3D, ICoord3D), enabling the cleaner call-site arithmetic throughout.
  • All isClick call-sites in CommandXlat and SelectionXlat are updated from raw-pointer to const-reference arguments, matching the new signature.

Confidence Score: 5/5

Safe to merge — the core tolerance fix is straightforward and well-scoped, dead code is cleanly removed, and no data paths are altered.

The adjusted drag tolerance is now correctly computed and applied in the circular distance check. The removed camera-position delta in SelectionXlat was computed but never forwarded to isClick, confirming it was dead code with no functional impact on deselection logic. The BaseType.h additions are arithmetic helpers with no surprising behavior for the screen-coordinate ranges in which they are used.

Mouse.cpp has a minor inverted comment; all other files are straightforward.

Important Files Changed

Filename Overview
Core/GameEngine/Source/GameClient/Input/Mouse.cpp Core fix: isClick now calls getDragToleranceAdjustedForScrollFactor() and compares circular distance; the adjusted tolerance is correctly used in the condition. A two-line comment at the if-guard has inverted wording that contradicts the actual greater-than checks.
Core/GameEngine/Include/GameClient/Mouse.h isClick signature updated from raw pointers to const references; getDragToleranceAdjustedForScrollFactor() declared as a new const getter. Both changes are clean.
Core/Libraries/Include/Lib/BaseType.h Adds arithmetic operators (+, -, +=, -=), set/add/sub helpers, and lengthSqr() to ICoord2D, Coord2D, Coord3D, and ICoord3D. All implementations look correct for typical screen-coordinate values.
Core/GameEngine/Source/GameClient/MessageStream/SelectionXlat.cpp Removes the 3D camera-position check (m_deselectDownCameraPosition) that was computed but never actually passed to isClick, making it dead code; now calls isClick with references instead of pointers.
Core/GameEngine/Include/GameClient/SelectionXlat.h Removes the m_deselectDownCameraPosition field that is no longer needed after the dead camera-position check was deleted.
Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp Two call-sites updated to pass ICoord2D by const reference instead of pointer; no logic change.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Caller as CommandXlat / SelectionXlat
    participant Mouse
    participant GlobalData as TheGlobalData

    Caller->>Mouse: isClick(anchor0, anchor1, t0, t1)
    Mouse->>Mouse: "delta = anchor1 - anchor0"
    Mouse->>Mouse: "timeDelta = t1 - t0"
    Mouse->>Mouse: getDragToleranceAdjustedForScrollFactor()
    Mouse->>GlobalData: m_keyboardDefaultScrollFactor
    Mouse->>GlobalData: m_keyboardScrollFactor
    GlobalData-->>Mouse: "ratio = default / current"
    Mouse->>Mouse: "dragTolerance = m_dragTolerance * ratio"
    Mouse->>Mouse: "reject if timeDelta > m_dragToleranceMS"
    Mouse->>Mouse: "reject if delta.lengthSqr() > sqr(dragTolerance)"
    Mouse-->>Caller: TRUE (click) / FALSE (drag)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Caller as CommandXlat / SelectionXlat
    participant Mouse
    participant GlobalData as TheGlobalData

    Caller->>Mouse: isClick(anchor0, anchor1, t0, t1)
    Mouse->>Mouse: "delta = anchor1 - anchor0"
    Mouse->>Mouse: "timeDelta = t1 - t0"
    Mouse->>Mouse: getDragToleranceAdjustedForScrollFactor()
    Mouse->>GlobalData: m_keyboardDefaultScrollFactor
    Mouse->>GlobalData: m_keyboardScrollFactor
    GlobalData-->>Mouse: "ratio = default / current"
    Mouse->>Mouse: "dragTolerance = m_dragTolerance * ratio"
    Mouse->>Mouse: "reject if timeDelta > m_dragToleranceMS"
    Mouse->>Mouse: "reject if delta.lengthSqr() > sqr(dragTolerance)"
    Mouse-->>Caller: TRUE (click) / FALSE (drag)
Loading

Reviews (5): Last reviewed commit: "bugfix(mouse): Fix bad drag tolerances w..." | Re-trigger Greptile

Comment thread Core/GameEngine/Source/GameClient/Input/Mouse.cpp
@Caball009

Caball009 commented Jun 22, 2026

Copy link
Copy Markdown

#1501 Seems related.

Maybe using DragTolerance3D (or the idea behind it: distance in the actual game world) makes more sense than accounting for scroll factor?

@xezon

xezon commented Jun 22, 2026

Copy link
Copy Markdown
Author

Maybe using DragTolerance3D (or the idea behind it: distance in the actual game world) makes more sense than accounting for scroll factor?

In principle yes, but that would require

DragTolerance3D = 30 ; How many feet in world space should we allow before it is a drag?

to be a reasonable value and the arguments of the isClick function to take in world positions instead of mouse positions.

#1501 Seems related.

It looks related yes.

@Skyaero42 Skyaero42 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think for now this is a sufficient fix.

@Caball009 Caball009 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GeneralsOnline devs are going to include this in a test build for the affected users to test. Let's wait a bit for the results.

@xezon

xezon commented Jun 23, 2026

Copy link
Copy Markdown
Author

#1501 Seems related.

Maybe using DragTolerance3D (or the idea behind it: distance in the actual game world) makes more sense than accounting for scroll factor?

Here is the fix using DragTolerance3D: #2826

Comment thread Core/GameEngine/Source/GameClient/Input/Mouse.cpp Outdated
@xezon xezon force-pushed the xezon/fix-drag-tolerance branch from 2cecc31 to a2f38ca Compare June 24, 2026 18:41
Comment thread Core/GameEngine/Source/GameClient/Input/Mouse.cpp Outdated
@xezon xezon force-pushed the xezon/fix-drag-tolerance branch from a2f38ca to 8ca8f81 Compare June 24, 2026 19:13
Comment thread Core/GameEngine/Source/GameClient/Input/Mouse.cpp Outdated
@xezon xezon force-pushed the xezon/fix-drag-tolerance branch from 8ca8f81 to b7b643d Compare June 24, 2026 21:06

Real toAngle() const; ///< turn 2D vector into angle (where angle 0 is down the +x axis)

void add( const Coord2D *a )

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do these function takes the argument by pointer?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because that is the current standard in these classes here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit skeptical about leaning into a worse option for consistency. Seems to me like it'd just create more work for the eventual refactor.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes is not ideal. Do you want the refactor to happen before adding new functions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Critical Severity: Minor < Major < Critical < Blocker Gen Relates to Generals Input ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Army gets deselected when moving camera with drag right click

3 participants