Browse Source

Mouse/Gamepad fixes

Introduces a `ControlDevice` global which is distinct from `ControlMode`
in that it is set to `Gamepad` even when simulating a mouse.

This allows us to avoid a number of edge cases related to mode changes.

Fixes #4242
pull/4019/head^2
Gleb Mazovetskiy 4 years ago
parent
commit
79c7eee10a
  1. 10
      Source/DiabloUI/diabloui.cpp
  2. 81
      Source/controls/plrctrls.cpp
  3. 19
      Source/controls/plrctrls.h
  4. 14
      Source/cursor.cpp
  5. 2
      Source/hwcursor.cpp
  6. 3
      Source/miniwin/misc_msg.cpp
  7. 2
      Source/movie.cpp

10
Source/DiabloUI/diabloui.cpp

@ -666,11 +666,7 @@ void LoadBackgroundArt(const char *pszFile, int frames)
fadeTc = 0;
fadeValue = 0;
if (IsHardwareCursorEnabled() && ArtCursor.surface != nullptr && GetCurrentCursorInfo().type() != CursorType::UserInterface) {
#if SDL_VERSION_ATLEAST(2, 0, 0)
SDL_SetSurfacePalette(ArtCursor.surface.get(), Palette.get());
SDL_SetColorKey(ArtCursor.surface.get(), 1, 0);
#endif
if (IsHardwareCursorEnabled() && ArtCursor.surface != nullptr && ControlDevice == ControlTypes::KeyboardAndMouse && GetCurrentCursorInfo().type() != CursorType::UserInterface) {
SetHardwareCursor(CursorInfo::UserInterfaceCursor());
}
@ -757,7 +753,7 @@ void UiPollAndRender(std::function<bool(SDL_Event &)> eventHandler)
// Must happen after the very first UiFadeIn, which sets the cursor.
if (IsHardwareCursor())
SetHardwareCursorVisible(ControlMode == ControlTypes::KeyboardAndMouse);
SetHardwareCursorVisible(ControlDevice == ControlTypes::KeyboardAndMouse);
#ifdef __3DS__
// Keyboard blocks until input is finished
@ -1082,7 +1078,7 @@ bool UiItemMouseEvents(SDL_Event *event, const std::vector<std::unique_ptr<UiIte
void DrawMouse()
{
if (ControlMode != ControlTypes::KeyboardAndMouse || IsHardwareCursor())
if (ControlDevice != ControlTypes::KeyboardAndMouse || IsHardwareCursor())
return;
DrawArt(MousePosition, &ArtCursor);

81
Source/controls/plrctrls.cpp

@ -4,6 +4,10 @@
#include <cstdint>
#include <list>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#endif
#include "automap.h"
#include "control.h"
#include "controls/controller_motion.h"
@ -30,12 +34,14 @@
#include "stores.h"
#include "towners.h"
#include "trigs.h"
#include "utils/log.hpp"
#define SPLICONLENGTH 56
namespace devilution {
ControlTypes ControlMode = ControlTypes::None;
ControlTypes ControlDevice = ControlTypes::None;
int pcurstrig = -1;
Missile *pcursmissile = nullptr;
quest_id pcursquest = Q_INVALID;
@ -1467,7 +1473,7 @@ ControlTypes GetInputTypeFromEvent(const SDL_Event &event)
float rightStickLastMove = 0;
bool ContinueSimulatingMouse(const SDL_Event &event, const ControllerButtonEvent &gamepadEvent)
bool ContinueSimulatedMouseEvent(const SDL_Event &event, const ControllerButtonEvent &gamepadEvent)
{
if (IsAutomapActive())
return false;
@ -1491,12 +1497,51 @@ bool ContinueSimulatingMouse(const SDL_Event &event, const ControllerButtonEvent
return false;
}
bool IsNextMouseButtonClickEventSimulated = false;
void LogControlDeviceAndModeChange(ControlTypes newControlDevice, ControlTypes newControlMode)
{
if (SDL_LOG_PRIORITY_DEBUG < SDL_LogGetPriority(SDL_LOG_CATEGORY_APPLICATION))
return;
if (newControlDevice == ControlDevice && newControlMode == ControlMode)
return;
constexpr auto DebugChange = [](ControlTypes before, ControlTypes after) -> std::string {
if (before == after)
return std::string { ControlTypeToString(before) };
return fmt::format("{} -> {}", ControlTypeToString(before), ControlTypeToString(after));
};
LogDebug("Control: device {}, mode {}", DebugChange(ControlDevice, newControlDevice), DebugChange(ControlMode, newControlMode));
}
} // namespace
void NextMouseButtonClickEventIsSimulated()
{
IsNextMouseButtonClickEventSimulated = true;
}
string_view ControlTypeToString(ControlTypes controlType)
{
switch (controlType) {
case ControlTypes::None:
return "None";
case ControlTypes::KeyboardAndMouse:
return "KeyboardAndMouse";
case ControlTypes::Gamepad:
return "Gamepad";
case ControlTypes::VirtualGamepad:
return "VirtualGamepad";
}
return "Invalid";
}
void DetectInputMethod(const SDL_Event &event, const ControllerButtonEvent &gamepadEvent)
{
ControlTypes inputType = GetInputTypeFromEvent(event);
if (inputType == ControlTypes::None)
return;
#ifdef __vita__
if (inputType == ControlTypes::VirtualGamepad) {
inputType = ControlTypes::Gamepad;
@ -1509,22 +1554,42 @@ void DetectInputMethod(const SDL_Event &event, const ControllerButtonEvent &game
}
#endif
if (ControlMode == ControlTypes::KeyboardAndMouse && inputType == ControlTypes::Gamepad && ContinueSimulatingMouse(event, gamepadEvent)) {
return;
ControlTypes newControlDevice = inputType;
ControlTypes newControlMode = inputType;
const bool isSimulatedMouseButtonEvent = IsNextMouseButtonClickEventSimulated
&& (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 (inputType != ControlTypes::None && inputType != ControlMode) {
ControlMode = inputType;
if (isSimulatedMouseButtonEvent) {
newControlMode = ControlTypes::KeyboardAndMouse;
} else if (ContinueSimulatedMouseEvent(event, gamepadEvent)) {
newControlMode = ControlMode;
}
LogControlDeviceAndModeChange(newControlDevice, newControlMode);
if (newControlDevice != ControlDevice) {
ControlDevice = newControlDevice;
#ifndef USE_SDL1
if (ControlMode != ControlTypes::KeyboardAndMouse) {
if (ControlDevice != ControlTypes::KeyboardAndMouse) {
if (IsHardwareCursor())
SetHardwareCursor(CursorInfo::UnknownCursor());
} else {
ResetCursor();
}
#endif
}
if (newControlMode != ControlMode) {
ControlMode = newControlMode;
CalculatePanelAreas();
}
}
@ -1566,9 +1631,11 @@ void HandleRightStickMotion()
static int lastMouseSetTick = 0;
const int now = SDL_GetTicks();
if (now - lastMouseSetTick > 0) {
ControlMode = ControlTypes::KeyboardAndMouse;
ResetCursor();
SetCursorPos({ x, y });
LogControlDeviceAndModeChange(ControlDevice, ControlTypes::KeyboardAndMouse);
ControlMode = ControlTypes::KeyboardAndMouse;
lastMouseSetTick = now;
}
}

19
Source/controls/plrctrls.h

@ -3,6 +3,8 @@
#include <cstdint>
#include <SDL.h>
#include "controls/controller.h"
#include "player.h"
@ -20,8 +22,25 @@ enum class ControlTypes : uint8_t {
VirtualGamepad,
};
string_view ControlTypeToString(ControlTypes controlType);
/**
* @brief Call this after sending a simulated mouse button click event.
*/
void NextMouseButtonClickEventIsSimulated();
extern ControlTypes ControlMode;
/**
* @brief Controlling device type.
*
* While simulating a mouse, `ControlMode` is set to `KeyboardAndMouse`,
* even though a gamepad is used to control it.
*
* This value is always set to the actual active device type.
*/
extern ControlTypes ControlDevice;
// Runs every frame.
// Handles menu movement.
void plrctrls_every_frame();

14
Source/cursor.cpp

@ -7,6 +7,8 @@
#include <fmt/format.h>
#include "DiabloUI/art.h"
#include "DiabloUI/diabloui.h"
#include "control.h"
#include "controls/plrctrls.h"
#include "doom.h"
@ -183,8 +185,16 @@ void NewCursor(int cursId)
pcurs = cursId;
cursSize = GetInvItemSize(cursId);
SetICursor(cursId);
if (IsHardwareCursorEnabled() && ControlMode == ControlTypes::KeyboardAndMouse && GetCurrentCursorInfo() != CursorInfo::GameCursor(cursId) && cursId != CURSOR_NONE) {
SetHardwareCursor(CursorInfo::GameCursor(cursId));
if (IsHardwareCursorEnabled() && ControlDevice == ControlTypes::KeyboardAndMouse) {
if (ArtCursor.surface == nullptr && cursId == CURSOR_NONE)
return;
const CursorInfo newCursor = ArtCursor.surface == nullptr
? CursorInfo::GameCursor(cursId)
: CursorInfo::UserInterfaceCursor();
if (newCursor != GetCurrentCursorInfo())
SetHardwareCursor(newCursor);
}
}

2
Source/hwcursor.cpp

@ -145,6 +145,8 @@ void SetHardwareCursor(CursorInfo cursorInfo)
// ArtCursor is null while loading the game on the progress screen,
// called via palette fade from ShowProgress.
if (ArtCursor.surface != nullptr) {
SDL_SetSurfacePalette(ArtCursor.surface.get(), Palette.get());
SDL_SetColorKey(ArtCursor.surface.get(), 1, 0);
CurrentCursorInfo.SetEnabled(
IsCursorSizeAllowed(Size { ArtCursor.surface->w, ArtCursor.surface->h })
&& SetHardwareCursor(ArtCursor.surface.get(), HotpointPosition::TopLeft));

3
Source/miniwin/misc_msg.cpp

@ -68,7 +68,7 @@ void SetMouseButtonEvent(SDL_Event &event, uint32_t type, uint8_t button, Point
void SetCursorPos(Point position)
{
if (ControlMode != ControlTypes::KeyboardAndMouse) {
if (ControlDevice != ControlTypes::KeyboardAndMouse) {
MousePosition = position;
return;
}
@ -398,6 +398,7 @@ void ProcessGamepadEvents(GameAction &action)
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;
}

2
Source/movie.cpp

@ -31,7 +31,7 @@ void play_movie(const char *pszMovie, bool userCanClose)
stream_stop();
effects_play_sound("Sfx\\Misc\\blank.wav");
if (IsHardwareCursorEnabled() && ControlMode == ControlTypes::KeyboardAndMouse) {
if (IsHardwareCursorEnabled() && ControlDevice == ControlTypes::KeyboardAndMouse) {
SetHardwareCursorVisible(false);
}

Loading…
Cancel
Save