Browse Source

Controller mouse emulation fixes

1. Do not interrupt mouse mode on virtual clicks.
2. Handle virtual clicks directly instead of sending an SDL event.
3. Fix D-Pad mouse emulation state handling.
4. Hides the modifier hints during D-Pad mouse emulation.
pull/4373/head
Gleb Mazovetskiy 4 years ago committed by Anders Jenbo
parent
commit
4c2fa76e49
  1. 69
      Source/controls/controller_motion.cpp
  2. 3
      Source/controls/controller_motion.h
  3. 14
      Source/controls/game_controls.cpp
  4. 2
      Source/controls/game_controls.h
  5. 5
      Source/controls/modifier_hints.cpp
  6. 36
      Source/controls/plrctrls.cpp
  7. 5
      Source/controls/plrctrls.h
  8. 13
      Source/miniwin/misc_msg.cpp

69
Source/controls/controller_motion.cpp

@ -12,9 +12,12 @@
#include "controls/plrctrls.h" #include "controls/plrctrls.h"
#include "controls/touch/gamepad.h" #include "controls/touch/gamepad.h"
#include "options.h" #include "options.h"
#include "utils/log.hpp"
namespace devilution { namespace devilution {
bool SimulatingMouseWithSelectAndDPad;
namespace { namespace {
void ScaleJoystickAxes(float *x, float *y, float deadzone) void ScaleJoystickAxes(float *x, float *y, float deadzone)
@ -63,38 +66,78 @@ void ScaleJoystickAxes(float *x, float *y, float deadzone)
} }
} }
void SetSimulatingMouseWithDpad(bool value)
{
if (SimulatingMouseWithSelectAndDPad == value)
return;
SimulatingMouseWithSelectAndDPad = value;
if (value) {
LogDebug("Control: begin simulating mouse with D-Pad");
} else {
LogDebug("Control: end simulating mouse with D-Pad");
}
}
// SELECT + D-Pad to simulate right stick movement. // SELECT + D-Pad to simulate right stick movement.
bool SimulateRightStickWithDpad(ControllerButtonEvent ctrlEvent) bool SimulateRightStickWithDpad(ControllerButtonEvent ctrlEvent)
{ {
if (sgOptions.Controller.bDpadHotkeys) if (sgOptions.Controller.bDpadHotkeys)
return false; return false;
static bool simulating = false; if (ctrlEvent.button == ControllerButton_NONE || ctrlEvent.button == ControllerButton_IGNORE)
return false;
if (ctrlEvent.button == ControllerButton_BUTTON_BACK) { if (ctrlEvent.button == ControllerButton_BUTTON_BACK) {
if (ctrlEvent.up && simulating) { if (SimulatingMouseWithSelectAndDPad) {
rightStickX = rightStickY = 0; if (ctrlEvent.up) {
simulating = false; rightStickX = rightStickY = 0;
}
return true;
} }
return false; return false;
} }
if (!IsControllerButtonPressed(ControllerButton_BUTTON_BACK))
if (!IsControllerButtonPressed(ControllerButton_BUTTON_BACK)) {
SetSimulatingMouseWithDpad(false);
return false; return false;
}
switch (ctrlEvent.button) { switch (ctrlEvent.button) {
case ControllerButton_BUTTON_DPAD_LEFT: case ControllerButton_BUTTON_DPAD_LEFT:
rightStickX = ctrlEvent.up ? 0.F : -1.F; if (ctrlEvent.up) {
rightStickX = 0;
} else {
rightStickX = -1.F;
SetSimulatingMouseWithDpad(true);
}
break; break;
case ControllerButton_BUTTON_DPAD_RIGHT: case ControllerButton_BUTTON_DPAD_RIGHT:
rightStickX = ctrlEvent.up ? 0.F : 1.F; if (ctrlEvent.up) {
rightStickX = 0;
} else {
rightStickX = 1.F;
SetSimulatingMouseWithDpad(true);
}
break; break;
case ControllerButton_BUTTON_DPAD_UP: case ControllerButton_BUTTON_DPAD_UP:
rightStickY = ctrlEvent.up ? 0.F : 1.F; if (ctrlEvent.up) {
rightStickY = 0;
} else {
rightStickY = 1.F;
SetSimulatingMouseWithDpad(true);
}
break; break;
case ControllerButton_BUTTON_DPAD_DOWN: case ControllerButton_BUTTON_DPAD_DOWN:
rightStickY = ctrlEvent.up ? 0.F : -1.F; if (ctrlEvent.up) {
rightStickY = 0;
} else {
rightStickY = -1.F;
SetSimulatingMouseWithDpad(true);
}
break; break;
default: default:
if (!IsSimulatedMouseClickBinding(ctrlEvent)) {
SetSimulatingMouseWithDpad(false);
}
return false; return false;
} }
simulating = !(rightStickX == 0 && rightStickY == 0);
return true; return true;
} }
@ -136,17 +179,21 @@ bool ProcessControllerMotion(const SDL_Event &event, ControllerButtonEvent ctrlE
GameController *const controller = GameController::Get(event); GameController *const controller = GameController::Get(event);
if (controller != nullptr && devilution::GameController::ProcessAxisMotion(event)) { if (controller != nullptr && devilution::GameController::ProcessAxisMotion(event)) {
ScaleJoysticks(); ScaleJoysticks();
SetSimulatingMouseWithDpad(false);
return true; return true;
} }
#endif #endif
Joystick *const joystick = Joystick::Get(event); Joystick *const joystick = Joystick::Get(event);
if (joystick != nullptr && devilution::Joystick::ProcessAxisMotion(event)) { if (joystick != nullptr && devilution::Joystick::ProcessAxisMotion(event)) {
ScaleJoysticks(); ScaleJoysticks();
SetSimulatingMouseWithDpad(false);
return true; return true;
} }
#if HAS_KBCTRL == 1 #if HAS_KBCTRL == 1
if (ProcessKbCtrlAxisMotion(event)) if (ProcessKbCtrlAxisMotion(event)) {
SetSimulatingMouseWithDpad(false);
return true; return true;
}
#endif #endif
return SimulateRightStickWithDpad(ctrlEvent); return SimulateRightStickWithDpad(ctrlEvent);
} }

3
Source/controls/controller_motion.h

@ -9,6 +9,9 @@
namespace devilution { namespace devilution {
// Whether we're currently simulating the mouse with SELECT + D-Pad.
extern bool SimulatingMouseWithSelectAndDPad;
// Raw axis values. // Raw axis values.
extern float leftStickXUnscaled, leftStickYUnscaled, rightStickXUnscaled, rightStickYUnscaled; extern float leftStickXUnscaled, leftStickYUnscaled, rightStickXUnscaled, rightStickYUnscaled;

14
Source/controls/game_controls.cpp

@ -441,6 +441,20 @@ bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrlEvent, Game
return false; return false;
} }
bool IsSimulatedMouseClickBinding(ControllerButtonEvent ctrlEvent)
{
switch (ctrlEvent.button) {
case ControllerButton_BUTTON_LEFTSTICK:
case ControllerButton_BUTTON_LEFTSHOULDER:
case ControllerButton_BUTTON_RIGHTSHOULDER:
return select_modifier_active;
case ControllerButton_BUTTON_RIGHTSTICK:
return true;
default:
return false;
}
}
AxisDirection GetMoveDirection() AxisDirection GetMoveDirection()
{ {
return GetLeftStickOrDpadDirection(/*allowDpad=*/!sgOptions.Controller.bDpadHotkeys); return GetLeftStickOrDpadDirection(/*allowDpad=*/!sgOptions.Controller.bDpadHotkeys);

2
Source/controls/game_controls.h

@ -71,6 +71,8 @@ struct GameAction {
bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrlEvent, GameAction *action); bool GetGameAction(const SDL_Event &event, ControllerButtonEvent ctrlEvent, GameAction *action);
bool IsSimulatedMouseClickBinding(ControllerButtonEvent ctrlEvent);
AxisDirection GetMoveDirection(); AxisDirection GetMoveDirection();
extern bool start_modifier_active; extern bool start_modifier_active;

5
Source/controls/modifier_hints.cpp

@ -5,8 +5,9 @@
#include "DiabloUI/art_draw.h" #include "DiabloUI/art_draw.h"
#include "DiabloUI/ui_flags.hpp" #include "DiabloUI/ui_flags.hpp"
#include "control.h" #include "control.h"
#include "controls/controller.h" #include "controls/controller_motion.h"
#include "controls/game_controls.h" #include "controls/game_controls.h"
#include "controls/plrctrls.h"
#include "engine/load_cel.hpp" #include "engine/load_cel.hpp"
#include "engine/render/text_render.hpp" #include "engine/render/text_render.hpp"
#include "options.h" #include "options.h"
@ -155,7 +156,7 @@ void DrawStartModifierMenu(const Surface &out)
void DrawSelectModifierMenu(const Surface &out) void DrawSelectModifierMenu(const Surface &out)
{ {
if (!select_modifier_active) if (!select_modifier_active || SimulatingMouseWithSelectAndDPad)
return; return;
if (sgOptions.Controller.bDpadHotkeys) { if (sgOptions.Controller.bDpadHotkeys) {

36
Source/controls/plrctrls.cpp

@ -1443,7 +1443,7 @@ struct RightStickAccumulator {
float hiresDY; float hiresDY;
}; };
bool IsStickMovmentSignificant() bool IsStickMovementSignificant()
{ {
return leftStickX >= 0.5 || leftStickX <= -0.5 return leftStickX >= 0.5 || leftStickX <= -0.5
|| leftStickY >= 0.5 || leftStickY <= -0.5 || leftStickY >= 0.5 || leftStickY <= -0.5
@ -1466,14 +1466,16 @@ ControlTypes GetInputTypeFromEvent(const SDL_Event &event)
return event.wheel.which == SDL_TOUCH_MOUSEID ? ControlTypes::VirtualGamepad : ControlTypes::KeyboardAndMouse; return event.wheel.which == SDL_TOUCH_MOUSEID ? ControlTypes::VirtualGamepad : ControlTypes::KeyboardAndMouse;
if (IsAnyOf(event.type, SDL_FINGERDOWN, SDL_FINGERUP, SDL_FINGERMOTION)) if (IsAnyOf(event.type, SDL_FINGERDOWN, SDL_FINGERUP, SDL_FINGERMOTION))
return ControlTypes::VirtualGamepad; return ControlTypes::VirtualGamepad;
if (event.type == SDL_CONTROLLERAXISMOTION && IsStickMovmentSignificant()) if (event.type == SDL_CONTROLLERAXISMOTION
&& (event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT
|| IsStickMovementSignificant()))
return ControlTypes::Gamepad; return ControlTypes::Gamepad;
if (event.type >= SDL_CONTROLLERBUTTONDOWN && event.type <= SDL_CONTROLLERDEVICEREMAPPED) if (event.type >= SDL_CONTROLLERBUTTONDOWN && event.type <= SDL_CONTROLLERDEVICEREMAPPED)
return ControlTypes::Gamepad; return ControlTypes::Gamepad;
if (IsAnyOf(event.type, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED)) if (IsAnyOf(event.type, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED))
return ControlTypes::Gamepad; return ControlTypes::Gamepad;
#endif #endif
if (event.type == SDL_JOYAXISMOTION && IsStickMovmentSignificant()) if (event.type == SDL_JOYAXISMOTION && IsStickMovementSignificant())
return ControlTypes::Gamepad; return ControlTypes::Gamepad;
if (event.type >= SDL_JOYBALLMOTION && event.type <= SDL_JOYBUTTONUP) if (event.type >= SDL_JOYBALLMOTION && event.type <= SDL_JOYBUTTONUP)
return ControlTypes::Gamepad; return ControlTypes::Gamepad;
@ -1500,15 +1502,9 @@ bool ContinueSimulatedMouseEvent(const SDL_Event &event, const ControllerButtonE
return true; return true;
} }
if (IsAnyOf(gamepadEvent.button, ControllerButton_BUTTON_RIGHTSTICK, ControllerButton_BUTTON_BACK)) { return SimulatingMouseWithSelectAndDPad || IsSimulatedMouseClickBinding(gamepadEvent);
return true;
}
return false;
} }
bool IsNextMouseButtonClickEventSimulated = false;
void LogControlDeviceAndModeChange(ControlTypes newControlDevice, ControlTypes newControlMode) void LogControlDeviceAndModeChange(ControlTypes newControlDevice, ControlTypes newControlMode)
{ {
if (SDL_LOG_PRIORITY_DEBUG < SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION)) if (SDL_LOG_PRIORITY_DEBUG < SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION))
@ -1525,11 +1521,6 @@ void LogControlDeviceAndModeChange(ControlTypes newControlDevice, ControlTypes n
} // namespace } // namespace
void NextMouseButtonClickEventIsSimulated()
{
IsNextMouseButtonClickEventSimulated = true;
}
string_view ControlTypeToString(ControlTypes controlType) string_view ControlTypeToString(ControlTypes controlType)
{ {
switch (controlType) { switch (controlType) {
@ -1566,20 +1557,7 @@ void DetectInputMethod(const SDL_Event &event, const ControllerButtonEvent &game
ControlTypes newControlDevice = inputType; ControlTypes newControlDevice = inputType;
ControlTypes newControlMode = inputType; ControlTypes newControlMode = inputType;
const bool isSimulatedMouseButtonEvent = IsNextMouseButtonClickEventSimulated if (ContinueSimulatedMouseEvent(event, gamepadEvent)) {
&& (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN)
&& ControlDevice != ControlTypes::KeyboardAndMouse;
if (isSimulatedMouseButtonEvent) {
IsNextMouseButtonClickEventSimulated = false;
// `inputType` here will be `KeyboardAndMouse` because this is a (simulated) mouse event.
// Restore it to the original control device.
newControlDevice = ControlDevice;
}
if (isSimulatedMouseButtonEvent) {
newControlMode = ControlTypes::KeyboardAndMouse;
} else if (ContinueSimulatedMouseEvent(event, gamepadEvent)) {
newControlMode = ControlMode; newControlMode = ControlMode;
} }

5
Source/controls/plrctrls.h

@ -24,11 +24,6 @@ enum class ControlTypes : uint8_t {
string_view ControlTypeToString(ControlTypes controlType); string_view ControlTypeToString(ControlTypes controlType);
/**
* @brief Call this after sending a simulated mouse button click event.
*/
void NextMouseButtonClickEventIsSimulated();
extern ControlTypes ControlMode; extern ControlTypes ControlMode;
/** /**

13
Source/miniwin/misc_msg.cpp

@ -394,13 +394,6 @@ void ProcessGamepadEvents(GameAction &action)
sbookflag = !sbookflag; sbookflag = !sbookflag;
} }
break; break;
case GameActionType_SEND_MOUSE_CLICK:
Uint8 simulatedButton = action.send_mouse_click.button == GameActionSendMouseClick::LEFT ? SDL_BUTTON_LEFT : SDL_BUTTON_RIGHT;
SDL_Event clickEvent;
SetMouseButtonEvent(clickEvent, action.send_mouse_click.up ? SDL_MOUSEBUTTONUP : SDL_MOUSEBUTTONDOWN, simulatedButton, MousePosition);
NextMouseButtonClickEventIsSimulated();
SDL_PushEvent(&clickEvent);
break;
} }
} }
@ -483,6 +476,12 @@ bool FetchMessage_Real(tagMSG *lpMsg)
} else if (action.type == GameActionType_SEND_KEY) { } else if (action.type == GameActionType_SEND_KEY) {
lpMsg->message = action.send_key.up ? DVL_WM_KEYUP : DVL_WM_KEYDOWN; lpMsg->message = action.send_key.up ? DVL_WM_KEYUP : DVL_WM_KEYDOWN;
lpMsg->wParam = action.send_key.vk_code; lpMsg->wParam = action.send_key.vk_code;
} else if (action.type == GameActionType_SEND_MOUSE_CLICK) {
lpMsg->message = action.send_mouse_click.up
? (action.send_mouse_click.button == GameActionSendMouseClick::LEFT ? DVL_WM_LBUTTONUP : DVL_WM_RBUTTONUP)
: (action.send_mouse_click.button == GameActionSendMouseClick::LEFT ? DVL_WM_LBUTTONDOWN : DVL_WM_RBUTTONDOWN);
lpMsg->wParam = 0;
lpMsg->lParam = (static_cast<int16_t>(MousePosition.y) << 16) | static_cast<int16_t>(MousePosition.x);
} else { } else {
ProcessGamepadEvents(action); ProcessGamepadEvents(action);
} }

Loading…
Cancel
Save