#include "controls/touch/event_handlers.h" #ifdef USE_SDL3 #include #include #else #include #endif #include "control/control.hpp" #include "controls/plrctrls.h" #include "cursor.h" #include "diablo.h" #include "engine/render/primitive_render.hpp" #include "engine/render/scrollrt.h" #include "game_mode.hpp" #include "gmenu.h" #include "inv.h" #include "panels/spell_book.hpp" #include "panels/spell_list.hpp" #include "qol/stash.h" #include "stores.h" #include "utils/is_of.hpp" #include "utils/sdl_compat.h" #include "utils/ui_fwd.h" namespace devilution { namespace { #if SDL_VERSION_ATLEAST(2, 0, 0) using SdlEventType = uint16_t; #else using SdlEventType = uint8_t; #endif VirtualGamepadEventHandler Handler(&VirtualGamepadState); Point ScaleToScreenCoordinates(float x, float y) { return Point { (int)round(x * gnScreenWidth), (int)round(y * gnScreenHeight) }; } constexpr bool IsFingerDown(const SDL_Event &event) { return event.type == SDL_EVENT_FINGER_DOWN; } constexpr bool IsFingerUp(const SDL_Event &event) { return event.type == SDL_EVENT_FINGER_UP; } constexpr bool IsFingerMotion(const SDL_Event &event) { return event.type == SDL_EVENT_FINGER_MOTION; } constexpr SDL_FingerID FingerId(const SDL_TouchFingerEvent &event) { #ifdef USE_SDL3 return event.fingerID; #else return event.fingerId; #endif } void SimulateMouseMovement(const SDL_Event &event) { const Point position = ScaleToScreenCoordinates(event.tfinger.x, event.tfinger.y); const bool isInMainPanel = GetMainPanel().contains(position); const bool isInLeftPanel = GetLeftPanel().contains(position); const bool isInRightPanel = GetRightPanel().contains(position); if (IsStashOpen) { if (!SpellSelectFlag && !isInMainPanel && !isInLeftPanel && !isInRightPanel) return; } else if (invflag) { if (!SpellSelectFlag && !isInMainPanel && !isInRightPanel) return; } MousePosition = position; SetPointAndClick(true); InvalidateInventorySlot(); } bool HandleGameMenuInteraction(const SDL_Event &event) { if (!gmenu_is_active()) return false; if (IsFingerDown(event) && gmenu_left_mouse(true)) return true; if (IsFingerMotion(event) && gmenu_on_mouse_move()) return true; return IsFingerUp(event) && gmenu_left_mouse(false); } bool HandleStoreInteraction(const SDL_Event &event) { if (!IsPlayerInStore()) return false; if (IsFingerDown(event)) CheckStoreBtn(); return true; } void HandleSpellBookInteraction(const SDL_Event &event) { if (!SpellbookFlag) return; if (IsFingerUp(event)) CheckSBook(); } bool HandleSpeedBookInteraction(const SDL_Event &event) { if (!SpellSelectFlag) return false; if (IsFingerUp(event)) SetSpell(); return true; } void HandleBottomPanelInteraction(const SDL_Event &event) { if (!gbRunGame || !MyPlayer->HoldItem.isEmpty()) return; ResetMainPanelButtons(); if (!IsFingerUp(event)) { SpellSelectFlag = true; CheckMainPanelButton(); SpellSelectFlag = false; } else { CheckMainPanelButton(); if (MainPanelButtonDown) CheckMainPanelButtonUp(); } } void HandleCharacterPanelInteraction(const SDL_Event &event) { if (!CharFlag) return; if (IsFingerDown(event)) CheckChrBtns(); else if (IsFingerUp(event) && CharPanelButtonActive) ReleaseChrBtns(false); } void HandleStashPanelInteraction(const SDL_Event &event) { if (!IsStashOpen || !MyPlayer->HoldItem.isEmpty()) return; if (!IsFingerUp(event)) { CheckStashButtonPress(MousePosition); } else { CheckStashButtonRelease(MousePosition); } } SdlEventType GetDeactivateEventType() { static const SdlEventType customEventType = SDL_RegisterEvents(1); return customEventType; } bool IsDeactivateEvent(const SDL_Event &event) { return event.type == GetDeactivateEventType(); } } // namespace void HandleTouchEvent(const SDL_Event &event) { SetPointAndClick(false); if (Handler.Handle(event)) { return; } if (!IsFingerDown(event) && !IsFingerUp(event) && !IsFingerMotion(event)) { return; } SimulateMouseMovement(event); if (HandleGameMenuInteraction(event)) return; if (HandleStoreInteraction(event)) return; if (HandleSpeedBookInteraction(event)) return; HandleSpellBookInteraction(event); HandleBottomPanelInteraction(event); HandleCharacterPanelInteraction(event); HandleStashPanelInteraction(event); } void DeactivateTouchEventHandlers() { SDL_Event event; event.type = GetDeactivateEventType(); HandleTouchEvent(event); } bool VirtualGamepadEventHandler::Handle(const SDL_Event &event) { if (!IsDeactivateEvent(event)) { if (!VirtualGamepadState.isActive || (!IsFingerDown(event) && !IsFingerUp(event) && !IsFingerMotion(event))) { VirtualGamepadState.primaryActionButton.didStateChange = false; VirtualGamepadState.secondaryActionButton.didStateChange = false; VirtualGamepadState.spellActionButton.didStateChange = false; VirtualGamepadState.cancelButton.didStateChange = false; return false; } } if (charMenuButtonEventHandler.Handle(event)) return true; if (questsMenuButtonEventHandler.Handle(event)) return true; if (inventoryMenuButtonEventHandler.Handle(event)) return true; if (mapMenuButtonEventHandler.Handle(event)) return true; if (directionPadEventHandler.Handle(event)) return true; if (leveltype != DTYPE_TOWN && standButtonEventHandler.Handle(event)) return true; if (primaryActionButtonEventHandler.Handle(event)) return true; if (secondaryActionButtonEventHandler.Handle(event)) return true; if (spellActionButtonEventHandler.Handle(event)) return true; if (cancelButtonEventHandler.Handle(event)) return true; if (healthButtonEventHandler.Handle(event)) return true; if (manaButtonEventHandler.Handle(event)) return true; return false; } bool VirtualDirectionPadEventHandler::Handle(const SDL_Event &event) { if (IsDeactivateEvent(event)) { isActive = false; return false; } if (IsFingerDown(event)) return HandleFingerDown(event.tfinger); if (IsFingerUp(event)) return HandleFingerUp(event.tfinger); if (IsFingerMotion(event)) return HandleFingerMotion(event.tfinger); return false; } bool VirtualDirectionPadEventHandler::HandleFingerDown(const SDL_TouchFingerEvent &event) { if (isActive) return false; const float x = event.x; const float y = event.y; const Point touchCoordinates = ScaleToScreenCoordinates(x, y); if (!virtualDirectionPad->area.contains(touchCoordinates)) return false; virtualDirectionPad->UpdatePosition(touchCoordinates); activeFinger = FingerId(event); isActive = true; return true; } bool VirtualDirectionPadEventHandler::HandleFingerUp(const SDL_TouchFingerEvent &event) { if (!isActive || FingerId(event) != activeFinger) return false; const Point position = virtualDirectionPad->area.position; virtualDirectionPad->UpdatePosition(position); isActive = false; return true; } bool VirtualDirectionPadEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent &event) { if (!isActive || FingerId(event) != activeFinger) return false; const float x = event.x; const float y = event.y; const Point touchCoordinates = ScaleToScreenCoordinates(x, y); virtualDirectionPad->UpdatePosition(touchCoordinates); return true; } bool VirtualButtonEventHandler::Handle(const SDL_Event &event) { if (IsDeactivateEvent(event)) { isActive = false; return false; } if (!virtualButton->isUsable()) { virtualButton->didStateChange = virtualButton->isHeld; virtualButton->isHeld = false; return false; } virtualButton->didStateChange = false; if (IsFingerDown(event)) return HandleFingerDown(event.tfinger); if (IsFingerUp(event)) return HandleFingerUp(event.tfinger); if (IsFingerMotion(event)) return HandleFingerMotion(event.tfinger); return false; } bool VirtualButtonEventHandler::HandleFingerDown(const SDL_TouchFingerEvent &event) { if (isActive) return false; const float x = event.x; const float y = event.y; const Point touchCoordinates = ScaleToScreenCoordinates(x, y); if (!virtualButton->contains(touchCoordinates)) return false; if (toggles) virtualButton->isHeld = !virtualButton->isHeld; else virtualButton->isHeld = true; virtualButton->didStateChange = true; activeFinger = FingerId(event); isActive = true; return true; } bool VirtualButtonEventHandler::HandleFingerUp(const SDL_TouchFingerEvent &event) { if (!isActive || FingerId(event) != activeFinger) return false; if (!toggles) { if (virtualButton->isHeld) virtualButton->didStateChange = true; virtualButton->isHeld = false; } isActive = false; return true; } bool VirtualButtonEventHandler::HandleFingerMotion(const SDL_TouchFingerEvent &event) { if (!isActive || FingerId(event) != activeFinger) return false; if (toggles) return true; const float x = event.x; const float y = event.y; const Point touchCoordinates = ScaleToScreenCoordinates(x, y); const bool wasHeld = virtualButton->isHeld; virtualButton->isHeld = virtualButton->contains(touchCoordinates); virtualButton->didStateChange = virtualButton->isHeld != wasHeld; return true; } } // namespace devilution