diff --git a/Source/DiabloUI/diabloui.cpp b/Source/DiabloUI/diabloui.cpp index d61e04e0e..c090c2d27 100644 --- a/Source/DiabloUI/diabloui.cpp +++ b/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 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 #include +#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; } } diff --git a/Source/controls/plrctrls.h b/Source/controls/plrctrls.h index 6da51becb..34543b1ca 100644 --- a/Source/controls/plrctrls.h +++ b/Source/controls/plrctrls.h @@ -3,6 +3,8 @@ #include +#include + #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(); diff --git a/Source/cursor.cpp b/Source/cursor.cpp index e63d673b3..8d2ca036f 100644 --- a/Source/cursor.cpp +++ b/Source/cursor.cpp @@ -7,6 +7,8 @@ #include +#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); } } diff --git a/Source/hwcursor.cpp b/Source/hwcursor.cpp index 5f7c38072..8ef286b07 100644 --- a/Source/hwcursor.cpp +++ b/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)); diff --git a/Source/miniwin/misc_msg.cpp b/Source/miniwin/misc_msg.cpp index ddf0ec081..73f8e9b76 100644 --- a/Source/miniwin/misc_msg.cpp +++ b/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; } diff --git a/Source/movie.cpp b/Source/movie.cpp index e9ec6ba6d..5ef3191d5 100644 --- a/Source/movie.cpp +++ b/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); }