mirror of
https://github.com/libretro/devilutionX.git
synced 2026-11-11 23:41:00 -10:00
This cleans up a bit of code and solves a few edge cases where an item could be lost, the game be unresponsive, or miss fire an event during lag cause of the cursor not reflecting the currently held item.
855 lines
21 KiB
C++
855 lines
21 KiB
C++
#include <cstdint>
|
|
#include <deque>
|
|
#include <string>
|
|
#ifdef USE_SDL1
|
|
#include <cassert>
|
|
#include <codecvt>
|
|
#include <locale>
|
|
#endif
|
|
|
|
#include <SDL.h>
|
|
|
|
#include "control.h"
|
|
#include "controls/controller.h"
|
|
#include "controls/controller_motion.h"
|
|
#include "controls/game_controls.h"
|
|
#include "controls/input.h"
|
|
#include "controls/plrctrls.h"
|
|
#include "controls/remap_keyboard.h"
|
|
#ifndef USE_SDL1
|
|
#include "controls/touch/event_handlers.h"
|
|
#endif
|
|
#include "cursor.h"
|
|
#include "engine/demomode.h"
|
|
#include "engine/rectangle.hpp"
|
|
#include "hwcursor.hpp"
|
|
#include "inv.h"
|
|
#include "menu.h"
|
|
#include "miniwin/miniwin.h"
|
|
#include "movie.h"
|
|
#include "panels/spell_list.hpp"
|
|
#include "qol/stash.h"
|
|
#include "utils/display.h"
|
|
#include "utils/log.hpp"
|
|
#include "utils/sdl_compat.h"
|
|
#include "utils/stubs.h"
|
|
#include "utils/utf8.hpp"
|
|
|
|
#ifdef __vita__
|
|
#include "platform/vita/touch.h"
|
|
#endif
|
|
|
|
#ifdef __SWITCH__
|
|
#include "platform/switch/docking.h"
|
|
#include <switch.h>
|
|
#endif
|
|
|
|
/** @file
|
|
* *
|
|
* Windows message handling and keyboard event conversion for SDL.
|
|
*/
|
|
|
|
namespace devilution {
|
|
|
|
static std::deque<tagMSG> message_queue;
|
|
|
|
void SetMouseButtonEvent(SDL_Event &event, uint32_t type, uint8_t button, Point position)
|
|
{
|
|
event.type = type;
|
|
event.button.button = button;
|
|
if (type == SDL_MOUSEBUTTONDOWN) {
|
|
event.button.state = SDL_PRESSED;
|
|
} else {
|
|
event.button.state = SDL_RELEASED;
|
|
}
|
|
event.button.x = position.x;
|
|
event.button.y = position.y;
|
|
}
|
|
|
|
void SetCursorPos(Point position)
|
|
{
|
|
if (ControlDevice != ControlTypes::KeyboardAndMouse) {
|
|
MousePosition = position;
|
|
return;
|
|
}
|
|
|
|
LogicalToOutput(&position.x, &position.y);
|
|
if (!demo::IsRunning())
|
|
SDL_WarpMouseInWindow(ghMainWnd, position.x, position.y);
|
|
}
|
|
|
|
// Moves the mouse to the first attribute "+" button.
|
|
void FocusOnCharInfo()
|
|
{
|
|
auto &myPlayer = Players[MyPlayerId];
|
|
|
|
if (invflag || myPlayer._pStatPts <= 0)
|
|
return;
|
|
|
|
// Find the first incrementable stat.
|
|
int stat = -1;
|
|
for (auto attribute : enum_values<CharacterAttribute>()) {
|
|
if (myPlayer.GetBaseAttributeValue(attribute) >= myPlayer.GetMaximumAttributeValue(attribute))
|
|
continue;
|
|
stat = static_cast<int>(attribute);
|
|
}
|
|
if (stat == -1)
|
|
return;
|
|
|
|
SetCursorPos(ChrBtnsRect[stat].Center());
|
|
}
|
|
|
|
int TranslateSdlKey(SDL_Keysym key)
|
|
{
|
|
// ref: https://wiki.libsdl.org/SDL_Keycode
|
|
// ref: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
|
|
SDL_Keycode sym = key.sym;
|
|
remap_keyboard_key(&sym);
|
|
switch (sym) {
|
|
case SDLK_BACKSPACE:
|
|
return DVL_VK_BACK;
|
|
case SDLK_TAB:
|
|
return DVL_VK_TAB;
|
|
case SDLK_RETURN:
|
|
return DVL_VK_RETURN;
|
|
case SDLK_ESCAPE:
|
|
return DVL_VK_ESCAPE;
|
|
case SDLK_SPACE:
|
|
return DVL_VK_SPACE;
|
|
case SDLK_QUOTE:
|
|
return DVL_VK_OEM_7;
|
|
case SDLK_COMMA:
|
|
return DVL_VK_OEM_COMMA;
|
|
case SDLK_MINUS:
|
|
return DVL_VK_OEM_MINUS;
|
|
case SDLK_PERIOD:
|
|
return DVL_VK_OEM_PERIOD;
|
|
case SDLK_SLASH:
|
|
return DVL_VK_OEM_2;
|
|
case SDLK_SEMICOLON:
|
|
return DVL_VK_OEM_1;
|
|
case SDLK_EQUALS:
|
|
return DVL_VK_OEM_PLUS;
|
|
case SDLK_LEFTBRACKET:
|
|
return DVL_VK_OEM_4;
|
|
case SDLK_BACKSLASH:
|
|
return DVL_VK_OEM_5;
|
|
case SDLK_RIGHTBRACKET:
|
|
return DVL_VK_OEM_6;
|
|
case SDLK_BACKQUOTE:
|
|
return DVL_VK_OEM_3;
|
|
case SDLK_DELETE:
|
|
return DVL_VK_DELETE;
|
|
case SDLK_CAPSLOCK:
|
|
return DVL_VK_CAPITAL;
|
|
case SDLK_F1:
|
|
return DVL_VK_F1;
|
|
case SDLK_F2:
|
|
return DVL_VK_F2;
|
|
case SDLK_F3:
|
|
return DVL_VK_F3;
|
|
case SDLK_F4:
|
|
return DVL_VK_F4;
|
|
case SDLK_F5:
|
|
return DVL_VK_F5;
|
|
case SDLK_F6:
|
|
return DVL_VK_F6;
|
|
case SDLK_F7:
|
|
return DVL_VK_F7;
|
|
case SDLK_F8:
|
|
return DVL_VK_F8;
|
|
case SDLK_F9:
|
|
return DVL_VK_F9;
|
|
case SDLK_F10:
|
|
return DVL_VK_F10;
|
|
case SDLK_F11:
|
|
return DVL_VK_F11;
|
|
case SDLK_F12:
|
|
return DVL_VK_F12;
|
|
case SDLK_PRINTSCREEN:
|
|
return DVL_VK_SNAPSHOT;
|
|
case SDLK_SCROLLLOCK:
|
|
return DVL_VK_SCROLL;
|
|
case SDLK_PAUSE:
|
|
return DVL_VK_PAUSE;
|
|
case SDLK_INSERT:
|
|
return DVL_VK_INSERT;
|
|
case SDLK_HOME:
|
|
return DVL_VK_HOME;
|
|
case SDLK_PAGEUP:
|
|
return DVL_VK_PRIOR;
|
|
case SDLK_END:
|
|
return DVL_VK_END;
|
|
case SDLK_PAGEDOWN:
|
|
return DVL_VK_NEXT;
|
|
case SDLK_RIGHT:
|
|
return DVL_VK_RIGHT;
|
|
case SDLK_LEFT:
|
|
return DVL_VK_LEFT;
|
|
case SDLK_DOWN:
|
|
return DVL_VK_DOWN;
|
|
case SDLK_UP:
|
|
return DVL_VK_UP;
|
|
case SDLK_NUMLOCKCLEAR:
|
|
return DVL_VK_NUMLOCK;
|
|
case SDLK_KP_DIVIDE:
|
|
return DVL_VK_DIVIDE;
|
|
case SDLK_KP_MULTIPLY:
|
|
return DVL_VK_MULTIPLY;
|
|
case SDLK_KP_MINUS:
|
|
// Returning DVL_VK_OEM_MINUS to play nice with Devilution automap zoom.
|
|
//
|
|
// For a distinct keypad key-code, DVL_VK_SUBTRACT should be returned.
|
|
return DVL_VK_OEM_MINUS;
|
|
case SDLK_KP_PLUS:
|
|
// Returning DVL_VK_OEM_PLUS to play nice with Devilution automap zoom.
|
|
//
|
|
// For a distinct keypad key-code, DVL_VK_ADD should be returned.
|
|
return DVL_VK_OEM_PLUS;
|
|
case SDLK_KP_ENTER:
|
|
return DVL_VK_RETURN;
|
|
case SDLK_KP_1:
|
|
return DVL_VK_NUMPAD1;
|
|
case SDLK_KP_2:
|
|
return DVL_VK_NUMPAD2;
|
|
case SDLK_KP_3:
|
|
return DVL_VK_NUMPAD3;
|
|
case SDLK_KP_4:
|
|
return DVL_VK_NUMPAD4;
|
|
case SDLK_KP_5:
|
|
return DVL_VK_NUMPAD5;
|
|
case SDLK_KP_6:
|
|
return DVL_VK_NUMPAD6;
|
|
case SDLK_KP_7:
|
|
return DVL_VK_NUMPAD7;
|
|
case SDLK_KP_8:
|
|
return DVL_VK_NUMPAD8;
|
|
case SDLK_KP_9:
|
|
return DVL_VK_NUMPAD9;
|
|
#ifndef USE_SDL1
|
|
case SDLK_KP_000:
|
|
case SDLK_KP_00:
|
|
#endif
|
|
case SDLK_KP_0:
|
|
return DVL_VK_NUMPAD0;
|
|
case SDLK_KP_PERIOD:
|
|
return DVL_VK_DECIMAL;
|
|
case SDLK_MENU:
|
|
return DVL_VK_MENU;
|
|
#ifndef USE_SDL1
|
|
case SDLK_KP_COMMA:
|
|
return DVL_VK_OEM_COMMA;
|
|
#endif
|
|
case SDLK_LCTRL:
|
|
return DVL_VK_LCONTROL;
|
|
case SDLK_LSHIFT:
|
|
return DVL_VK_LSHIFT;
|
|
case SDLK_LALT:
|
|
return DVL_VK_LMENU;
|
|
case SDLK_LGUI:
|
|
return DVL_VK_LWIN;
|
|
case SDLK_RCTRL:
|
|
return DVL_VK_RCONTROL;
|
|
case SDLK_RSHIFT:
|
|
return DVL_VK_RSHIFT;
|
|
case SDLK_RALT:
|
|
return DVL_VK_RMENU;
|
|
case SDLK_RGUI:
|
|
return DVL_VK_RWIN;
|
|
default:
|
|
if (sym >= SDLK_a && sym <= SDLK_z) {
|
|
return 'A' + (sym - SDLK_a);
|
|
} else if (sym >= SDLK_0 && sym <= SDLK_9) {
|
|
return '0' + (sym - SDLK_0);
|
|
} else if (sym >= SDLK_F1 && sym <= SDLK_F12) {
|
|
return DVL_VK_F1 + (sym - SDLK_F1);
|
|
}
|
|
Log("unknown key: name={} sym=0x{:X} scan={} mod=0x{:X}", SDL_GetKeyName(sym), sym, key.scancode, key.mod);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
int32_t PositionForMouse(int16_t x, int16_t y)
|
|
{
|
|
return (((uint16_t)(y & 0xFFFF)) << 16) | (uint16_t)(x & 0xFFFF);
|
|
}
|
|
|
|
int32_t KeystateForMouse(int32_t ret)
|
|
{
|
|
ret |= (SDL_GetModState() & KMOD_SHIFT) != 0 ? DVL_MK_SHIFT : 0;
|
|
ret |= (SDL_GetModState() & KMOD_CTRL) != 0 ? DVL_MK_CTRL : 0;
|
|
// XXX: other DVL_MK_* codes not implemented
|
|
return ret;
|
|
}
|
|
|
|
bool FalseAvail(const char *name, int value)
|
|
{
|
|
LogVerbose("Unhandled SDL event: {} {}", name, value);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Try to clean the inventory related cursor states.
|
|
* @return True if it is safe to close the inventory
|
|
*/
|
|
bool BlurInventory()
|
|
{
|
|
if (!MyPlayer->HoldItem.isEmpty()) {
|
|
if (!TryDropItem()) {
|
|
Players[MyPlayerId].Say(HeroSpeech::WhereWouldIPutThis);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
CloseInventory();
|
|
if (pcurs > CURSOR_HAND)
|
|
NewCursor(CURSOR_HAND);
|
|
if (chrflag)
|
|
FocusOnCharInfo();
|
|
|
|
return true;
|
|
}
|
|
|
|
void ProcessGamepadEvents(GameAction &action)
|
|
{
|
|
switch (action.type) {
|
|
case GameActionType_NONE:
|
|
case GameActionType_SEND_KEY:
|
|
case GameActionType_SEND_MOUSE_CLICK:
|
|
break;
|
|
case GameActionType_USE_HEALTH_POTION:
|
|
if (IsStashOpen)
|
|
Stash.PreviousPage();
|
|
else
|
|
UseBeltItem(BLT_HEALING);
|
|
break;
|
|
case GameActionType_USE_MANA_POTION:
|
|
if (IsStashOpen)
|
|
Stash.NextPage();
|
|
else
|
|
UseBeltItem(BLT_MANA);
|
|
break;
|
|
case GameActionType_PRIMARY_ACTION:
|
|
PerformPrimaryAction();
|
|
break;
|
|
case GameActionType_SECONDARY_ACTION:
|
|
PerformSecondaryAction();
|
|
break;
|
|
case GameActionType_CAST_SPELL:
|
|
PerformSpellAction();
|
|
break;
|
|
case GameActionType_TOGGLE_QUICK_SPELL_MENU:
|
|
if (!invflag || BlurInventory()) {
|
|
if (!spselflag)
|
|
DoSpeedBook();
|
|
else
|
|
spselflag = false;
|
|
chrflag = false;
|
|
QuestLogIsOpen = false;
|
|
sbookflag = false;
|
|
CloseGoldWithdraw();
|
|
IsStashOpen = false;
|
|
}
|
|
break;
|
|
case GameActionType_TOGGLE_CHARACTER_INFO:
|
|
chrflag = !chrflag;
|
|
if (chrflag) {
|
|
QuestLogIsOpen = false;
|
|
CloseGoldWithdraw();
|
|
IsStashOpen = false;
|
|
spselflag = false;
|
|
if (pcurs == CURSOR_DISARM)
|
|
NewCursor(CURSOR_HAND);
|
|
FocusOnCharInfo();
|
|
}
|
|
break;
|
|
case GameActionType_TOGGLE_QUEST_LOG:
|
|
if (!QuestLogIsOpen) {
|
|
StartQuestlog();
|
|
chrflag = false;
|
|
CloseGoldWithdraw();
|
|
IsStashOpen = false;
|
|
spselflag = false;
|
|
} else {
|
|
QuestLogIsOpen = false;
|
|
}
|
|
break;
|
|
case GameActionType_TOGGLE_INVENTORY:
|
|
if (invflag) {
|
|
BlurInventory();
|
|
} else {
|
|
sbookflag = false;
|
|
spselflag = false;
|
|
invflag = true;
|
|
if (pcurs == CURSOR_DISARM)
|
|
NewCursor(CURSOR_HAND);
|
|
FocusOnInventory();
|
|
}
|
|
break;
|
|
case GameActionType_TOGGLE_SPELL_BOOK:
|
|
if (BlurInventory()) {
|
|
CloseInventory();
|
|
spselflag = false;
|
|
sbookflag = !sbookflag;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
bool FetchMessage_Real(tagMSG *lpMsg)
|
|
{
|
|
#ifdef __SWITCH__
|
|
HandleDocking();
|
|
#endif
|
|
|
|
if (!message_queue.empty()) {
|
|
*lpMsg = message_queue.front();
|
|
message_queue.pop_front();
|
|
return true;
|
|
}
|
|
|
|
SDL_Event e;
|
|
if (PollEvent(&e) == 0) {
|
|
return false;
|
|
}
|
|
|
|
lpMsg->message = 0;
|
|
lpMsg->lParam = 0;
|
|
lpMsg->wParam = 0;
|
|
|
|
#ifdef __vita__
|
|
HandleTouchEvent(&e, MousePosition);
|
|
#elif !defined(USE_SDL1)
|
|
HandleTouchEvent(e);
|
|
#endif
|
|
|
|
if (e.type == SDL_QUIT) {
|
|
lpMsg->message = DVL_WM_QUIT;
|
|
return true;
|
|
}
|
|
|
|
if (IsAnyOf(e.type, SDL_KEYUP, SDL_KEYDOWN) && e.key.keysym.sym == SDLK_UNKNOWN) {
|
|
// Erroneous events generated by RG350 kernel.
|
|
return true;
|
|
}
|
|
|
|
#if !defined(USE_SDL1) && !defined(__vita__)
|
|
if (!movie_playing) {
|
|
// SDL generates mouse events from touch-based inputs to provide basic
|
|
// touchscreeen support for apps that don't explicitly handle touch events
|
|
if (IsAnyOf(e.type, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP) && e.button.which == SDL_TOUCH_MOUSEID)
|
|
return true;
|
|
if (e.type == SDL_MOUSEMOTION && e.motion.which == SDL_TOUCH_MOUSEID)
|
|
return true;
|
|
if (e.type == SDL_MOUSEWHEEL && e.wheel.which == SDL_TOUCH_MOUSEID)
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef USE_SDL1
|
|
if (e.type == SDL_MOUSEMOTION) {
|
|
OutputToLogical(&e.motion.x, &e.motion.y);
|
|
} else if (IsAnyOf(e.type, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP)) {
|
|
OutputToLogical(&e.button.x, &e.button.y);
|
|
}
|
|
#endif
|
|
|
|
const ControllerButtonEvent ctrlEvent = ToControllerButtonEvent(e);
|
|
bool isGamepadMotion = ProcessControllerMotion(e, ctrlEvent);
|
|
|
|
DetectInputMethod(e, ctrlEvent);
|
|
if (isGamepadMotion) {
|
|
return true;
|
|
}
|
|
|
|
if (IsAnyOf(ctrlEvent.button, ControllerButtonPrimary, ControllerButtonSecondary, ControllerButtonTertiary) && IsAnyOf(ControllerButtonHeld, ctrlEvent.button, ControllerButton_NONE)) {
|
|
ControllerButtonHeld = (ctrlEvent.up || IsControllerButtonPressed(ControllerButton_BUTTON_BACK)) ? ControllerButton_NONE : ctrlEvent.button;
|
|
LastMouseButtonAction = MouseActionType::None;
|
|
}
|
|
|
|
GameAction action;
|
|
if (GetGameAction(e, ctrlEvent, &action)) {
|
|
if (movie_playing) {
|
|
if (action.type != GameActionType_NONE) {
|
|
lpMsg->message = DVL_WM_KEYDOWN;
|
|
if (action.type == GameActionType_SEND_KEY)
|
|
lpMsg->wParam = action.send_key.vk_code;
|
|
}
|
|
} else if (action.type == GameActionType_SEND_KEY) {
|
|
lpMsg->message = action.send_key.up ? DVL_WM_KEYUP : DVL_WM_KEYDOWN;
|
|
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 {
|
|
ProcessGamepadEvents(action);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
if (HandleControllerAddedOrRemovedEvent(e))
|
|
return true;
|
|
|
|
switch (e.type) {
|
|
case SDL_QUIT:
|
|
lpMsg->message = DVL_WM_QUIT;
|
|
break;
|
|
case SDL_KEYDOWN:
|
|
case SDL_KEYUP: {
|
|
#ifdef USE_SDL1
|
|
if (gbRunGame && (IsTalkActive() || dropGoldFlag)) {
|
|
Uint16 unicode = e.key.keysym.unicode;
|
|
if (unicode >= ' ') {
|
|
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> convert;
|
|
std::string utf8 = convert.to_bytes(unicode);
|
|
if (IsTalkActive())
|
|
control_new_text(utf8);
|
|
if (dropGoldFlag)
|
|
GoldDropNewText(utf8);
|
|
}
|
|
}
|
|
#endif
|
|
int key = TranslateSdlKey(e.key.keysym);
|
|
if (key == -1)
|
|
return FalseAvail(e.type == SDL_KEYDOWN ? "SDL_KEYDOWN" : "SDL_KEYUP", e.key.keysym.sym);
|
|
lpMsg->message = e.type == SDL_KEYDOWN ? DVL_WM_KEYDOWN : DVL_WM_KEYUP;
|
|
lpMsg->wParam = (uint32_t)key;
|
|
// HACK: Encode modifier in lParam for TranslateMessage later
|
|
lpMsg->lParam = e.key.keysym.mod << 16;
|
|
} break;
|
|
case SDL_MOUSEMOTION:
|
|
lpMsg->message = DVL_WM_MOUSEMOVE;
|
|
lpMsg->lParam = PositionForMouse(e.motion.x, e.motion.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
if (ControlMode == ControlTypes::KeyboardAndMouse && invflag)
|
|
InvalidateInventorySlot();
|
|
break;
|
|
case SDL_MOUSEBUTTONDOWN: {
|
|
int button = e.button.button;
|
|
switch (button) {
|
|
case SDL_BUTTON_LEFT:
|
|
lpMsg->message = DVL_WM_LBUTTONDOWN;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(DVL_MK_LBUTTON);
|
|
break;
|
|
case SDL_BUTTON_RIGHT:
|
|
lpMsg->message = DVL_WM_RBUTTONDOWN;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(DVL_MK_RBUTTON);
|
|
break;
|
|
case SDL_BUTTON_MIDDLE:
|
|
lpMsg->message = DVL_WM_MBUTTONDOWN;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
case SDL_BUTTON_X1:
|
|
lpMsg->message = DVL_WM_X1BUTTONDOWN;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
case SDL_BUTTON_X2:
|
|
lpMsg->message = DVL_WM_X2BUTTONDOWN;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
}
|
|
} break;
|
|
case SDL_MOUSEBUTTONUP: {
|
|
int button = e.button.button;
|
|
switch (button) {
|
|
case SDL_BUTTON_LEFT:
|
|
lpMsg->message = DVL_WM_LBUTTONUP;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
case SDL_BUTTON_RIGHT:
|
|
lpMsg->message = DVL_WM_RBUTTONUP;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
case SDL_BUTTON_MIDDLE:
|
|
lpMsg->message = DVL_WM_MBUTTONUP;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
case SDL_BUTTON_X1:
|
|
lpMsg->message = DVL_WM_X1BUTTONUP;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
case SDL_BUTTON_X2:
|
|
lpMsg->message = DVL_WM_X2BUTTONUP;
|
|
lpMsg->lParam = PositionForMouse(e.button.x, e.button.y);
|
|
lpMsg->wParam = KeystateForMouse(0);
|
|
break;
|
|
}
|
|
} break;
|
|
#ifndef USE_SDL1
|
|
case SDL_MOUSEWHEEL:
|
|
lpMsg->message = DVL_WM_KEYDOWN;
|
|
if (e.wheel.y > 0) {
|
|
lpMsg->wParam = GetAsyncKeyState(DVL_VK_CONTROL) ? DVL_VK_OEM_PLUS : DVL_VK_UP;
|
|
} else if (e.wheel.y < 0) {
|
|
lpMsg->wParam = GetAsyncKeyState(DVL_VK_CONTROL) ? DVL_VK_OEM_MINUS : DVL_VK_DOWN;
|
|
} else if (e.wheel.x > 0) {
|
|
lpMsg->wParam = DVL_VK_LEFT;
|
|
} else if (e.wheel.x < 0) {
|
|
lpMsg->wParam = DVL_VK_RIGHT;
|
|
}
|
|
break;
|
|
#if SDL_VERSION_ATLEAST(2, 0, 4)
|
|
case SDL_AUDIODEVICEADDED:
|
|
return FalseAvail("SDL_AUDIODEVICEADDED", e.adevice.which);
|
|
case SDL_AUDIODEVICEREMOVED:
|
|
return FalseAvail("SDL_AUDIODEVICEREMOVED", e.adevice.which);
|
|
case SDL_KEYMAPCHANGED:
|
|
return FalseAvail("SDL_KEYMAPCHANGED", 0);
|
|
#endif
|
|
case SDL_TEXTEDITING:
|
|
if (gbRunGame)
|
|
break;
|
|
return FalseAvail("SDL_TEXTEDITING", e.edit.length);
|
|
case SDL_TEXTINPUT:
|
|
if (gbRunGame && IsTalkActive()) {
|
|
control_new_text(e.text.text);
|
|
break;
|
|
}
|
|
if (gbRunGame && dropGoldFlag) {
|
|
GoldDropNewText(e.text.text);
|
|
break;
|
|
}
|
|
if (gbRunGame && IsWithdrawGoldOpen) {
|
|
GoldWithdrawNewText(e.text.text);
|
|
break;
|
|
}
|
|
return FalseAvail("SDL_TEXTINPUT", e.text.windowID);
|
|
case SDL_WINDOWEVENT:
|
|
switch (e.window.event) {
|
|
case SDL_WINDOWEVENT_SHOWN:
|
|
gbActive = true;
|
|
lpMsg->message = DVL_WM_PAINT;
|
|
break;
|
|
case SDL_WINDOWEVENT_HIDDEN:
|
|
gbActive = false;
|
|
break;
|
|
case SDL_WINDOWEVENT_EXPOSED:
|
|
lpMsg->message = DVL_WM_PAINT;
|
|
break;
|
|
case SDL_WINDOWEVENT_LEAVE:
|
|
lpMsg->message = DVL_WM_CAPTURECHANGED;
|
|
break;
|
|
case SDL_WINDOWEVENT_SIZE_CHANGED:
|
|
ReinitializeHardwareCursor();
|
|
break;
|
|
case SDL_WINDOWEVENT_MOVED:
|
|
case SDL_WINDOWEVENT_RESIZED:
|
|
case SDL_WINDOWEVENT_MINIMIZED:
|
|
case SDL_WINDOWEVENT_MAXIMIZED:
|
|
case SDL_WINDOWEVENT_RESTORED:
|
|
#if SDL_VERSION_ATLEAST(2, 0, 5)
|
|
case SDL_WINDOWEVENT_TAKE_FOCUS:
|
|
#endif
|
|
break;
|
|
case SDL_WINDOWEVENT_CLOSE:
|
|
lpMsg->message = DVL_WM_QUERYENDSESSION;
|
|
break;
|
|
|
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
|
diablo_focus_pause();
|
|
break;
|
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
|
diablo_focus_unpause();
|
|
break;
|
|
|
|
default:
|
|
return FalseAvail("SDL_WINDOWEVENT", e.window.event);
|
|
}
|
|
|
|
break;
|
|
#else
|
|
case SDL_ACTIVEEVENT:
|
|
if ((e.active.state & SDL_APPINPUTFOCUS) != 0) {
|
|
if (e.active.gain == 0)
|
|
diablo_focus_pause();
|
|
else
|
|
diablo_focus_unpause();
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
return FalseAvail("unknown", e.type);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool FetchMessage(tagMSG *lpMsg)
|
|
{
|
|
bool available = demo::IsRunning() ? demo::FetchMessage(lpMsg) : FetchMessage_Real(lpMsg);
|
|
|
|
if (available && demo::IsRecording())
|
|
demo::RecordMessage(lpMsg);
|
|
|
|
return available;
|
|
}
|
|
|
|
bool TranslateMessage(const tagMSG *lpMsg)
|
|
{
|
|
if (lpMsg->message == DVL_WM_KEYDOWN) {
|
|
int key = lpMsg->wParam;
|
|
unsigned mod = (uint32_t)lpMsg->lParam >> 16;
|
|
|
|
bool shift = (mod & KMOD_SHIFT) != 0;
|
|
bool caps = (mod & KMOD_CAPS) != 0;
|
|
bool upper = shift != caps;
|
|
|
|
bool isAlpha = (key >= 'A' && key <= 'Z');
|
|
bool isNumeric = (key >= '0' && key <= '9');
|
|
bool isControl = key == DVL_VK_SPACE || key == DVL_VK_BACK || key == DVL_VK_ESCAPE || key == DVL_VK_TAB || key == DVL_VK_RETURN;
|
|
bool isOem = (key >= DVL_VK_OEM_1 && key <= DVL_VK_OEM_7);
|
|
|
|
if (isControl || isAlpha || isNumeric || isOem) {
|
|
if (!upper && isAlpha) {
|
|
key = tolower(key);
|
|
} else if (shift && isNumeric) {
|
|
switch (key) {
|
|
case '1':
|
|
key = '!';
|
|
break;
|
|
case '2':
|
|
key = '@';
|
|
break;
|
|
case '3':
|
|
key = '#';
|
|
break;
|
|
case '4':
|
|
key = '$';
|
|
break;
|
|
case '5':
|
|
key = '%';
|
|
break;
|
|
case '6':
|
|
key = '^';
|
|
break;
|
|
case '7':
|
|
key = '&';
|
|
break;
|
|
case '8':
|
|
key = '*';
|
|
break;
|
|
case '9':
|
|
key = '(';
|
|
break;
|
|
case '0':
|
|
key = ')';
|
|
break;
|
|
}
|
|
} else if (isOem) {
|
|
// XXX: This probably only supports US keyboard layout
|
|
switch (key) {
|
|
case DVL_VK_OEM_1:
|
|
key = shift ? ':' : ';';
|
|
break;
|
|
case DVL_VK_OEM_2:
|
|
key = shift ? '?' : '/';
|
|
break;
|
|
case DVL_VK_OEM_3:
|
|
key = shift ? '~' : '`';
|
|
break;
|
|
case DVL_VK_OEM_4:
|
|
key = shift ? '{' : '[';
|
|
break;
|
|
case DVL_VK_OEM_5:
|
|
key = shift ? '|' : '\\';
|
|
break;
|
|
case DVL_VK_OEM_6:
|
|
key = shift ? '}' : ']';
|
|
break;
|
|
case DVL_VK_OEM_7:
|
|
key = shift ? '"' : '\'';
|
|
break;
|
|
case DVL_VK_OEM_MINUS:
|
|
key = shift ? '_' : '-';
|
|
break;
|
|
case DVL_VK_OEM_PLUS:
|
|
key = shift ? '+' : '=';
|
|
break;
|
|
case DVL_VK_OEM_PERIOD:
|
|
key = shift ? '>' : '.';
|
|
break;
|
|
case DVL_VK_OEM_COMMA:
|
|
key = shift ? '<' : ',';
|
|
break;
|
|
|
|
default:
|
|
UNIMPLEMENTED();
|
|
}
|
|
}
|
|
|
|
if (key >= 32) {
|
|
LogVerbose("char: {:c}", key);
|
|
}
|
|
|
|
// XXX: This does not add extended info to lParam
|
|
PostMessage(DVL_WM_CHAR, key, 0);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool GetAsyncKeyState(int vKey)
|
|
{
|
|
if (vKey == DVL_MK_LBUTTON)
|
|
return (SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
|
|
if (vKey == DVL_MK_RBUTTON)
|
|
return (SDL_GetMouseState(nullptr, nullptr) & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
|
|
|
|
const Uint8 *state = SDLC_GetKeyState();
|
|
switch (vKey) {
|
|
case DVL_VK_CONTROL:
|
|
return state[SDLC_KEYSTATE_LEFTCTRL] != 0 || state[SDLC_KEYSTATE_RIGHTCTRL] != 0;
|
|
case DVL_VK_SHIFT:
|
|
return state[SDLC_KEYSTATE_LEFTSHIFT] != 0 || state[SDLC_KEYSTATE_RIGHTSHIFT] != 0;
|
|
case DVL_VK_MENU:
|
|
return state[SDLC_KEYSTATE_LALT] != 0 || state[SDLC_KEYSTATE_RALT] != 0;
|
|
case DVL_VK_LEFT:
|
|
return state[SDLC_KEYSTATE_LEFT] != 0;
|
|
case DVL_VK_UP:
|
|
return state[SDLC_KEYSTATE_UP] != 0;
|
|
case DVL_VK_RIGHT:
|
|
return state[SDLC_KEYSTATE_RIGHT] != 0;
|
|
case DVL_VK_DOWN:
|
|
return state[SDLC_KEYSTATE_DOWN] != 0;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void PushMessage(const tagMSG *lpMsg)
|
|
{
|
|
assert(CurrentProc);
|
|
|
|
CurrentProc(lpMsg->message, lpMsg->wParam, lpMsg->lParam);
|
|
}
|
|
|
|
bool PostMessage(uint32_t type, int32_t wParam, int32_t lParam)
|
|
{
|
|
message_queue.push_back({ type, wParam, lParam });
|
|
|
|
return true;
|
|
}
|
|
|
|
void ClearMessageQueue()
|
|
{
|
|
message_queue.clear();
|
|
}
|
|
|
|
} // namespace devilution
|