Skip to content

Commit

Permalink
Mouse mapping support (#741)
Browse files Browse the repository at this point in the history
* init

* fix spelling

* mose key to button mapping

* more button mouse mapping

* separate file for mouse things

cause Ive got recursive include problem

* some fixes

* updating dxgi mouse capture

now compatible with sdl one

* fix

* fix mouse capture clip missing after alt+tab

* mouse binding & gui conflict check

* new mouse input block

* Mouse mapping menu update

* fix ImGui `WantCaptureKeyboard` locks when holding mouse button

* reading mouse from config

casually forgotten

* fixed strange buffered mapping bug

* mouse buttons callbacks

* mouse button to axis mapping

* fix

* mark

* use propper cursor hide method

* merge-fixes

* clang-format

* store main game window ID to reduce load

* update on_mouse_up/down in dxgi

* MouseMeta migration

* MouseKey -> MouseButton

* fix name collision

* another name collision fix

* remove imgui direct calls

* clang-format

* MouseCapture keybind

* replace cursor visibility with mouse capture

* clang-format

* mUseKeydownEventToCreateNewMapping -> mUseInputToCreateNewMapping

* change MAPPING_TYPE_UNKNOWN to -1

* dxgi/sdl GetMouseDelta parity

* set x/y to zero when no capture

* mapping type uint -> int

for MAPPING_TYPE_UNKNOWN = -1

* offset mapping popup

* simple popup check

completely dropped complexity

* clang format

* simplify mouse capture toggle

* boundaries check for mouse buttons

* remove duplicate cursor visibility change in fullscreen

* Windows ignore mouse input while mouse is captured

* simplify

* make separate method for handling capture

* fix cast

* clang-format

* ProcessMouseEvent -> ProcessMouseButtonEvent

* wheel mapping support WIP

* fix for axis

* wheel logging

* macos fix?

* clang format

* fix wheel axis on dx backend

* Revert "wheel logging"

This reverts commit 43082d8.

* buffered wheel axis mapping (test)

* clang format

* fix

* extend buffer

* dxgi horizontal scrolling support

* clang format

---------

Co-authored-by: lilacLunatic <[email protected]>
  • Loading branch information
lightmanLP and lilacLunatic authored Jan 3, 2025
1 parent 2942915 commit 78d951e
Show file tree
Hide file tree
Showing 59 changed files with 1,055 additions and 73 deletions.
32 changes: 31 additions & 1 deletion src/controller/controldeck/ControlDeck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#endif
#include <imgui.h>
#include "controller/deviceindex/ShipDeviceIndexMappingManager.h"
#include "controller/controldevice/controller/mapping/mouse/WheelHandler.h"

namespace Ship {

Expand All @@ -33,6 +34,7 @@ void ControlDeck::Init(uint8_t* controllerBits) {
// if we don't have a config for controller 1, set default keyboard bindings
if (!mPorts[0]->GetConnectedController()->HasConfig()) {
mPorts[0]->GetConnectedController()->AddDefaultMappings(ShipDeviceIndex::Keyboard);
mPorts[0]->GetConnectedController()->AddDefaultMappings(ShipDeviceIndex::Mouse);
}

Context::GetInstance()->GetWindow()->GetGui()->GetGuiWindow("Controller Reordering")->Show();
Expand All @@ -51,6 +53,19 @@ bool ControlDeck::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancod
return result;
}

bool ControlDeck::ProcessMouseButtonEvent(bool isPressed, MouseBtn button) {
bool result = false;
for (auto port : mPorts) {
auto controller = port->GetConnectedController();

if (controller != nullptr) {
result = controller->ProcessMouseButtonEvent(isPressed, button) || result;
}
}

return result;
}

bool ControlDeck::AllGameInputBlocked() {
return !mGameInputBlockers.empty();
}
Expand All @@ -63,7 +78,21 @@ bool ControlDeck::GamepadGameInputBlocked() {

bool ControlDeck::KeyboardGameInputBlocked() {
// block keyboard input when typing in imgui
return AllGameInputBlocked() || ImGui::GetIO().WantCaptureKeyboard;
ImGuiWindow* activeIDWindow = ImGui::GetCurrentContext()->ActiveIdWindow;
return AllGameInputBlocked() ||
(activeIDWindow != NULL &&
activeIDWindow->ID != Context::GetInstance()->GetWindow()->GetGui()->GetMainGameWindowID()) ||
ImGui::GetTopMostPopupModal() != NULL; // ImGui::GetIO().WantCaptureKeyboard, but ActiveId check altered
}

bool ControlDeck::MouseGameInputBlocked() {
// block mouse input when user interacting with gui
ImGuiWindow* window = ImGui::GetCurrentContext()->HoveredWindow;
if (window == NULL) {
return true;
}
return AllGameInputBlocked() ||
(window->ID != Context::GetInstance()->GetWindow()->GetGui()->GetMainGameWindowID());
}

std::shared_ptr<Controller> ControlDeck::GetControllerByPort(uint8_t port) {
Expand Down Expand Up @@ -112,6 +141,7 @@ void ControlDeck::WriteToPad(void* pad) {

void ControlDeck::WriteToOSContPad(OSContPad* pad) {
SDL_PumpEvents();
Ship::WheelHandler::GetInstance()->Update();

if (AllGameInputBlocked()) {
return;
Expand Down
3 changes: 3 additions & 0 deletions src/controller/controldeck/ControlDeck.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ class ControlDeck {
void UnblockGameInput(int32_t blockId);
bool GamepadGameInputBlocked();
bool KeyboardGameInputBlocked();
bool MouseGameInputBlocked();
void SetSinglePlayerMappingMode(bool singlePlayer);
bool IsSinglePlayerMappingMode();
bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, MouseBtn button);

std::shared_ptr<ShipDeviceIndexMappingManager> GetDeviceIndexMappingManager();

protected:
Expand Down
10 changes: 10 additions & 0 deletions src/controller/controldevice/controller/Controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ bool Controller::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode
return result;
}

bool Controller::ProcessMouseButtonEvent(bool isPressed, MouseBtn mouseButton) {
bool result = false;
for (auto [bitmask, button] : GetAllButtons()) {
result = button->ProcessMouseButtonEvent(isPressed, mouseButton) || result;
}
result = GetLeftStick()->ProcessMouseButtonEvent(isPressed, mouseButton) || result;
result = GetRightStick()->ProcessMouseButtonEvent(isPressed, mouseButton) || result;
return result;
}

bool Controller::HasMappingsForShipDeviceIndex(ShipDeviceIndex lusIndex) {
for (auto [bitmask, button] : GetAllButtons()) {
if (button->HasMappingsForShipDeviceIndex(lusIndex)) {
Expand Down
1 change: 1 addition & 0 deletions src/controller/controldevice/controller/Controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class Controller : public ControlDevice {
std::vector<std::shared_ptr<ControllerMapping>> GetAllMappings();

bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, MouseBtn button);

bool HasMappingsForShipDeviceIndex(ShipDeviceIndex lusIndex);
void MoveMappingsToDifferentController(std::shared_ptr<Controller> newController, ShipDeviceIndex lusIndex);
Expand Down
46 changes: 41 additions & 5 deletions src/controller/controldevice/controller/ControllerButton.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@
#include "controller/controldevice/controller/mapping/factories/ButtonMappingFactory.h"

#include "controller/controldevice/controller/mapping/keyboard/KeyboardKeyToButtonMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseButtonToButtonMapping.h"

#include "public/bridge/consolevariablebridge.h"
#include "utils/StringHelper.h"
#include <sstream>
#include <algorithm>

#include "Context.h"

namespace Ship {
ControllerButton::ControllerButton(uint8_t portIndex, CONTROLLERBUTTONS_T bitmask)
: mPortIndex(portIndex), mBitmask(bitmask), mUseKeydownEventToCreateNewMapping(false),
mKeyboardScancodeForNewMapping(LUS_KB_UNKNOWN) {
: mPortIndex(portIndex), mBitmask(bitmask), mUseEventInputToCreateNewMapping(false),
mKeyboardScancodeForNewMapping(LUS_KB_UNKNOWN), mMouseButtonForNewMapping(LUS_MOUSE_BTN_UNKNOWN) {
}

ControllerButton::~ControllerButton() {
Expand Down Expand Up @@ -175,11 +178,20 @@ bool ControllerButton::HasMappingsForShipDeviceIndex(ShipDeviceIndex lusIndex) {
bool ControllerButton::AddOrEditButtonMappingFromRawPress(CONTROLLERBUTTONS_T bitmask, std::string id) {
std::shared_ptr<ControllerButtonMapping> mapping = nullptr;

mUseKeydownEventToCreateNewMapping = true;
mUseEventInputToCreateNewMapping = true;
if (mKeyboardScancodeForNewMapping != LUS_KB_UNKNOWN) {
mapping = std::make_shared<KeyboardKeyToButtonMapping>(mPortIndex, bitmask, mKeyboardScancodeForNewMapping);
}

else if (!Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverAnyGuiItem() &&
Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverActivePopup()) {
if (mMouseButtonForNewMapping != LUS_MOUSE_BTN_UNKNOWN) {
mapping = std::make_shared<MouseButtonToButtonMapping>(mPortIndex, bitmask, mMouseButtonForNewMapping);
} else {
mapping = ButtonMappingFactory::CreateButtonMappingFromMouseWheelInput(mPortIndex, bitmask);
}
}

if (mapping == nullptr) {
mapping = ButtonMappingFactory::CreateButtonMappingFromSDLInput(mPortIndex, bitmask);
}
Expand All @@ -189,7 +201,8 @@ bool ControllerButton::AddOrEditButtonMappingFromRawPress(CONTROLLERBUTTONS_T bi
}

mKeyboardScancodeForNewMapping = LUS_KB_UNKNOWN;
mUseKeydownEventToCreateNewMapping = false;
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
mUseEventInputToCreateNewMapping = false;

if (id != "") {
ClearButtonMapping(id);
Expand All @@ -206,7 +219,7 @@ bool ControllerButton::AddOrEditButtonMappingFromRawPress(CONTROLLERBUTTONS_T bi
}

bool ControllerButton::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode) {
if (mUseKeydownEventToCreateNewMapping && eventType == LUS_KB_EVENT_KEY_DOWN) {
if (mUseEventInputToCreateNewMapping) {
if (eventType == LUS_KB_EVENT_KEY_DOWN) {
mKeyboardScancodeForNewMapping = scancode;
return true;
Expand All @@ -228,6 +241,29 @@ bool ControllerButton::ProcessKeyboardEvent(KbEventType eventType, KbScancode sc
return result;
}

bool ControllerButton::ProcessMouseButtonEvent(bool isPressed, MouseBtn button) {
if (mUseEventInputToCreateNewMapping) {
if (isPressed) {
mMouseButtonForNewMapping = button;
return true;
} else {
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
}
}

bool result = false;
for (auto [id, mapping] : GetAllButtonMappings()) {
if (mapping->GetMappingType() == MAPPING_TYPE_MOUSE) {
std::shared_ptr<MouseButtonToButtonMapping> mtobMapping =
std::dynamic_pointer_cast<MouseButtonToButtonMapping>(mapping);
if (mtobMapping != nullptr) {
result = result || mtobMapping->ProcessMouseButtonEvent(isPressed, button);
}
}
}
return result;
}

void ControllerButton::AddDefaultMappings(ShipDeviceIndex shipDeviceIndex) {
for (auto mapping : ButtonMappingFactory::CreateDefaultSDLButtonMappings(shipDeviceIndex, mPortIndex, mBitmask)) {
AddButtonMapping(mapping);
Expand Down
4 changes: 3 additions & 1 deletion src/controller/controldevice/controller/ControllerButton.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class ControllerButton {
void UpdatePad(CONTROLLERBUTTONS_T& padButtons);

bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, Ship::MouseBtn button);

bool HasMappingsForShipDeviceIndex(ShipDeviceIndex lusIndex);

Expand All @@ -46,7 +47,8 @@ class ControllerButton {
std::unordered_map<std::string, std::shared_ptr<ControllerButtonMapping>> mButtonMappings;
std::string GetConfigNameFromBitmask(CONTROLLERBUTTONS_T bitmask);

bool mUseKeydownEventToCreateNewMapping;
bool mUseEventInputToCreateNewMapping;
KbScancode mKeyboardScancodeForNewMapping;
MouseBtn mMouseButtonForNewMapping;
};
} // namespace Ship
49 changes: 43 additions & 6 deletions src/controller/controldevice/controller/ControllerStick.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <spdlog/spdlog.h>

#include "controller/controldevice/controller/mapping/keyboard/KeyboardKeyToAxisDirectionMapping.h"
#include "controller/controldevice/controller/mapping/mouse/MouseButtonToAxisDirectionMapping.h"

#include "controller/controldevice/controller/mapping/factories/AxisDirectionMappingFactory.h"

Expand All @@ -11,6 +12,8 @@
#include <sstream>
#include <algorithm>

#include "Context.h"

// for some reason windows isn't seeing M_PI
// this is copied from my system's math.h
#ifndef M_PI
Expand All @@ -22,8 +25,8 @@

namespace Ship {
ControllerStick::ControllerStick(uint8_t portIndex, StickIndex stickIndex)
: mPortIndex(portIndex), mStickIndex(stickIndex), mUseKeydownEventToCreateNewMapping(false),
mKeyboardScancodeForNewMapping(KbScancode::LUS_KB_UNKNOWN) {
: mPortIndex(portIndex), mStickIndex(stickIndex), mUseEventInputToCreateNewMapping(false),
mKeyboardScancodeForNewMapping(KbScancode::LUS_KB_UNKNOWN), mMouseButtonForNewMapping(LUS_MOUSE_BTN_UNKNOWN) {
mSensitivityPercentage = DEFAULT_STICK_SENSITIVITY_PERCENTAGE;
mSensitivity = 1.0f;
mDeadzonePercentage = DEFAULT_STICK_DEADZONE_PERCENTAGE;
Expand Down Expand Up @@ -268,12 +271,20 @@ void ControllerStick::Process(int8_t& x, int8_t& y) {
bool ControllerStick::AddOrEditAxisDirectionMappingFromRawPress(Direction direction, std::string id) {
std::shared_ptr<ControllerAxisDirectionMapping> mapping = nullptr;

mUseKeydownEventToCreateNewMapping = true;
mUseEventInputToCreateNewMapping = true;
if (mKeyboardScancodeForNewMapping != LUS_KB_UNKNOWN) {
mapping = std::make_shared<KeyboardKeyToAxisDirectionMapping>(mPortIndex, mStickIndex, direction,
mKeyboardScancodeForNewMapping);
} else if (!Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverAnyGuiItem() &&
Context::GetInstance()->GetWindow()->GetGui()->IsMouseOverActivePopup()) {
if (mMouseButtonForNewMapping != LUS_MOUSE_BTN_UNKNOWN) {
mapping = std::make_shared<MouseButtonToAxisDirectionMapping>(mPortIndex, mStickIndex, direction,
mMouseButtonForNewMapping);
} else {
mapping = AxisDirectionMappingFactory::CreateAxisDirectionMappingFromMouseWheelInput(
mPortIndex, mStickIndex, direction);
}
}

if (mapping == nullptr) {
mapping =
AxisDirectionMappingFactory::CreateAxisDirectionMappingFromSDLInput(mPortIndex, mStickIndex, direction);
Expand All @@ -284,7 +295,8 @@ bool ControllerStick::AddOrEditAxisDirectionMappingFromRawPress(Direction direct
}

mKeyboardScancodeForNewMapping = LUS_KB_UNKNOWN;
mUseKeydownEventToCreateNewMapping = false;
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
mUseEventInputToCreateNewMapping = false;

if (id != "") {
ClearAxisDirectionMapping(direction, id);
Expand Down Expand Up @@ -319,7 +331,7 @@ void ControllerStick::UpdatePad(int8_t& x, int8_t& y) {
}

bool ControllerStick::ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode) {
if (mUseKeydownEventToCreateNewMapping) {
if (mUseEventInputToCreateNewMapping) {
if (eventType == LUS_KB_EVENT_KEY_DOWN) {
mKeyboardScancodeForNewMapping = scancode;
return true;
Expand All @@ -343,6 +355,31 @@ bool ControllerStick::ProcessKeyboardEvent(KbEventType eventType, KbScancode sca
return result;
}

bool ControllerStick::ProcessMouseButtonEvent(bool isPressed, MouseBtn button) {
if (mUseEventInputToCreateNewMapping) {
if (isPressed) {
mMouseButtonForNewMapping = button;
return true;
} else {
mMouseButtonForNewMapping = LUS_MOUSE_BTN_UNKNOWN;
}
}

bool result = false;
for (auto [direction, mappings] : mAxisDirectionMappings) {
for (auto [id, mapping] : mappings) {
if (mapping->GetMappingType() == MAPPING_TYPE_MOUSE) {
std::shared_ptr<MouseButtonToAxisDirectionMapping> mtoadMapping =
std::dynamic_pointer_cast<MouseButtonToAxisDirectionMapping>(mapping);
if (mtoadMapping != nullptr) {
result = result || mtoadMapping->ProcessMouseButtonEvent(isPressed, button);
}
}
}
}
return result;
}

void ControllerStick::SetSensitivity(uint8_t sensitivityPercentage) {
mSensitivityPercentage = sensitivityPercentage;
mSensitivity = sensitivityPercentage / 100.0f;
Expand Down
4 changes: 3 additions & 1 deletion src/controller/controldevice/controller/ControllerStick.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class ControllerStick {
bool NotchSnapAngleIsDefault();

bool ProcessKeyboardEvent(KbEventType eventType, KbScancode scancode);
bool ProcessMouseButtonEvent(bool isPressed, Ship::MouseBtn button);

bool HasMappingsForShipDeviceIndex(ShipDeviceIndex lusIndex);
StickIndex GetStickIndex();
Expand All @@ -75,7 +76,8 @@ class ControllerStick {
std::unordered_map<Direction, std::unordered_map<std::string, std::shared_ptr<ControllerAxisDirectionMapping>>>
mAxisDirectionMappings;

bool mUseKeydownEventToCreateNewMapping;
bool mUseEventInputToCreateNewMapping;
KbScancode mKeyboardScancodeForNewMapping;
MouseBtn mMouseButtonForNewMapping;
};
} // namespace Ship
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ ControllerAxisDirectionMapping::ControllerAxisDirectionMapping(ShipDeviceIndex s
ControllerAxisDirectionMapping::~ControllerAxisDirectionMapping() {
}

uint8_t ControllerAxisDirectionMapping::GetMappingType() {
int8_t ControllerAxisDirectionMapping::GetMappingType() {
return MAPPING_TYPE_UNKNOWN;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class ControllerAxisDirectionMapping : virtual public ControllerInputMapping {
Direction direction);
~ControllerAxisDirectionMapping();
virtual float GetNormalizedAxisDirectionValue() = 0;
virtual uint8_t GetMappingType();
virtual int8_t GetMappingType();

virtual std::string GetAxisDirectionMappingId() = 0;
virtual void SaveToConfig() = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ CONTROLLERBUTTONS_T ControllerButtonMapping::GetBitmask() {
return mBitmask;
}

uint8_t ControllerButtonMapping::GetMappingType() {
int8_t ControllerButtonMapping::GetMappingType() {
return MAPPING_TYPE_UNKNOWN;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ControllerButtonMapping : virtual public ControllerInputMapping {

CONTROLLERBUTTONS_T GetBitmask();
virtual void UpdatePad(CONTROLLERBUTTONS_T& padButtons) = 0;
virtual uint8_t GetMappingType();
virtual int8_t GetMappingType();
void SetPortIndex(uint8_t portIndex);

virtual void SaveToConfig() = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

namespace Ship {

#define MAPPING_TYPE_UNKNOWN -1
#define MAPPING_TYPE_GAMEPAD 0
#define MAPPING_TYPE_KEYBOARD 1
#define MAPPING_TYPE_UNKNOWN 2
#define MAPPING_TYPE_MOUSE 2

class ControllerMapping {
public:
Expand Down
Loading

0 comments on commit 78d951e

Please sign in to comment.