Files
ppsspp/Core/ControlMapper.h
T
Henrik RydgårdandClaude Opus 5 375fa0fc11 Misc correctness fixes
GetStringErrorMsg had the strerror_r result test backwards. The XSI variant returns
0 on success, so every successful lookup returned "Unknown error"; and under glibc
with _GNU_SOURCE the GNU variant is selected instead, which returns the message by
pointer and typically leaves the buffer untouched, so it returned an empty string.
Either way GetLastErrorMsg() was useless on Linux, Android and macOS. Pick the right
handling by overload resolution rather than guessing which signature we got.

KeyMap's "no gamepad button mapped to cancel" fallback pushed into confirmKeys
instead of cancelKeys - and pushed the confirm button. So unmapping cancel left no
gamepad way out of menus, and duplicated an entry in the confirm list.

ControlMapper::AddListener mutated listeners_ without taking mutex_, while
RemoveListener takes it and the input thread iterates the vector under it. Opening a
screen while an axis is moving could reallocate it mid-iteration. The comment about
piggybacking on a screenmanager mutex was stale - there isn't one.

Config's two std::stof calls on PostShaderSetting values ran on user-editable ini
text with no try/catch, so a malformed entry called std::terminate during startup
config load. Use the same checked sscanf that LoadGameConfig already uses.
(CmdLine.cpp and Compatibility.cpp have the same pattern; not touched here.)

The screenshot downscale path leaked its final buffer on every downscaled shot,
which savestate thumbnails hit on every save at 3x and above.

HandleUploadPost is registered unconditionally, so closing the Upload screen left an
unauthenticated file-write endpoint live for as long as anything else kept the server
up. Check the flag in the handler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DCPmm7FoQUoqrbMdhfqhQ2
2026-08-31 01:19:06 +02:00

135 lines
4.4 KiB
C++

#pragma once
#include <cstring>
#include <atomic>
#include <functional>
#include <mutex>
#include <vector>
#include "Common/Input/InputState.h"
#include "Core/KeyMap.h"
struct DisplayLayoutConfig;
// Pure interface.
class ControlListener {
public:
virtual ~ControlListener() = default;
virtual void OnVKey(VirtKey vkey, bool down) {}
virtual void OnVKeyAnalog(VirtKey vkey, float value) {}
virtual void UpdatePSPButtons(uint32_t buttonMask, uint32_t changedMask) {}
virtual void SetPSPAnalog(int rotation, int stick, float x, float y) {}
virtual void SetRawAnalog(int stick, float x, float y) {}
};
class StringWriter;
// Utilities for mapping input events to PSP inputs and virtual keys.
// Main use is of course from EmuScreen.cpp, but also useful from control settings etc.
class ControlMapper {
public:
void UpdateConfig(const DisplayLayoutConfig &config);
void UpdateAutoMovements(double now);
// Inputs to the table-based mapping
// These functions are free-threaded.
bool Key(const KeyInput &key);
void Axis(const AxisInput *axes, size_t count);
// Required callbacks.
// TODO: These are so many now that a virtual interface might be more appropriate..
// Both of these take mutex_ - listeners_ is iterated on the input thread, and screens add and
// remove themselves from another one.
void AddListener(ControlListener *listener);
void RemoveListener(ControlListener *listener);
// Inject raw PSP key input directly, such as from touch screen controls.
// Combined with the mapped input. Unlike __Ctrl APIs, this supports
// virtual key codes, including analog mappings.
void PSPKey(int deviceId, int pspKeyCode, KeyInputFlags flags);
// Toggle swapping DPAD and Analog. Useful on some input devices with few buttons.
void ToggleSwapAxes();
// Call this when a Vkey press triggers leaving the screen you're using the controlmapper on. This can cause
// the loss of key-up events, which will confuse things later when you're back.
// Might replace this later by allowing through "key-up" and similar events to lower screens.
void ForceReleaseVKey(int vkey);
// Call when the emu screen gets pushed behind some other screen, like the pause screen, to release all "down" inputs.
void ReleaseAll();
void GetDebugString(StringWriter &w) const;
bool PollPauseTrigger() {
return pauseTrigger_.exchange(false);
}
struct InputSample {
float value;
double timestamp;
};
private:
void UpdateSwapAxes();
bool UpdatePSPState(const InputMapping &changedMapping, double now);
float MapAxisValue(float value, int vkId, const InputMapping &mapping, const InputMapping &changedMapping, bool *oppositeTouched);
void SwapMappingIfEnabled(uint32_t *vkey);
void SetPSPAxis(int deviceId, int stick, char axis, float value);
void UpdateAnalogOutput(int stick);
void onVKey(VirtKey vkey, bool down);
void onVKeyAnalog(int deviceId, VirtKey vkey, float value);
void UpdateCurInputAxis(const InputMapping &mapping, float value, double timestamp);
float GetDeviceAxisThreshold(int device, const InputMapping &mapping);
bool IsVirtKeyOn(VirtKey key) const {
int index = key - VIRTKEY_FIRST;
if (index < 0 || index >= VIRTKEY_COUNT) {
return false;
}
return virtKeyOn_[index];
}
// To track mappable virtual keys. We can have as many as we want.
float virtKeys_[VIRTKEY_COUNT]{};
bool virtKeyOn_[VIRTKEY_COUNT]{}; // Track boolean output separaately since thresholds may differ.
// This is only used for co-axis (analog stick to buttons), so not bothering to track separately
// per device.
float rawAxisValue_[JOYSTICK_AXIS_MAX]{};
double deviceTimestamps_[(size_t)DEVICE_ID_COUNT]{};
int lastNonDeadzoneDeviceID_[2]{};
float history_[2][2]{};
float converted_[2][2]{}; // for debug display
// Mappable auto-rotation. Useful for keyboard/dpad->analog in a few games.
bool autoRotatingAnalogCW_ = false;
bool autoRotatingAnalogCCW_ = false;
std::atomic<bool> pauseTrigger_{};
bool swapAxes_ = false;
int iInternalScreenRotationCached_ = 0;
// Protects basically all the state. (There is no screenmanager mutex to piggyback on, despite
// what a previous comment here claimed - input arrives on its own thread.)
std::mutex mutex_;
std::map<InputMapping, InputSample> curInput_;
// Callbacks
std::vector<ControlListener *> listeners_;
};
void ConvertAnalogStick(float x, float y, float *outX, float *outY);
float GetDeviceAxisThreshold(int device, const InputMapping &mapping);
extern ControlMapper g_controlMapper;