Merge pull request #18551 from hrydgard/mouse-settings

Mouse refactor, restore smoothing
This commit is contained in:
Henrik Rydgård 2023-12-14 20:37:26 +01:00 committed by GitHub
commit e99d6e8f09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 393 additions and 345 deletions

View File

@ -392,6 +392,26 @@ public:
}
return list;
}
std::vector<std::string> GetPresentModeList(const char *currentMarkerString) const override {
std::vector<std::string> list;
for (auto mode : vulkan_->GetAvailablePresentModes()) {
std::string str = VulkanPresentModeToString(mode);
if (mode == vulkan_->GetPresentMode()) {
str += std::string(" (") + currentMarkerString + ")";
}
list.push_back(str);
}
return list;
}
std::vector<std::string> GetSurfaceFormatList() const override {
std::vector<std::string> list;
for (auto &format : vulkan_->SurfaceFormats()) {
std::string str = StringFromFormat("%s : %s", VulkanFormatToString(format.format), VulkanColorSpaceToString(format.colorSpace));
list.push_back(str);
}
return list;
}
uint32_t GetSupportedShaderLanguages() const override {
return (uint32_t)ShaderLanguage::GLSL_VULKAN;
}

View File

@ -698,6 +698,8 @@ public:
virtual std::vector<std::string> GetFeatureList() const { return std::vector<std::string>(); }
virtual std::vector<std::string> GetExtensionList(bool device, bool enabledOnly) const { return std::vector<std::string>(); }
virtual std::vector<std::string> GetDeviceList() const { return std::vector<std::string>(); }
virtual std::vector<std::string> GetPresentModeList(const char *currentMarkerString) const { return std::vector<std::string>(); }
virtual std::vector<std::string> GetSurfaceFormatList() const { return std::vector<std::string>(); }
// Describes the primary shader language that this implementation prefers.
const ShaderLanguageDesc &GetShaderLanguageDesc() {

View File

@ -173,7 +173,6 @@ Draw::Texture *CreateTextureFromFileData(Draw::DrawContext *draw, const uint8_t
}
Draw::Texture *CreateTextureFromFile(Draw::DrawContext *draw, const char *filename, ImageFileType type, bool generateMips) {
INFO_LOG(SYSTEM, "CreateTextureFromFile(%s)", filename);
size_t fileSize;
uint8_t *buffer = g_VFS.ReadFile(filename, &fileSize);
if (!buffer) {
@ -203,7 +202,6 @@ Draw::Texture *ManagedTexture::GetTexture() {
ManagedTexture::ManagedTexture(Draw::DrawContext *draw, std::string_view filename, ImageFileType type, bool generateMips)
: draw_(draw), filename_(filename), type_(type), generateMips_(generateMips)
{
INFO_LOG(SYSTEM, "ManagedTexture::ManagedTexture (%s)", filename_.c_str());
StartLoadTask();
}

View File

@ -11,6 +11,7 @@
#include "Common/Render/Text/draw_text.h"
#include "Common/Render/ManagedTexture.h"
#include "Common/Log.h"
#include "Common/TimeUtil.h"
#include "Common/LogReporting.h"
UIContext::UIContext() {
@ -44,6 +45,7 @@ void UIContext::setUIAtlas(const std::string &name) {
}
void UIContext::BeginFrame() {
frameStartTime_ = time_now_d();
if (!uitexture_ || UIAtlas_ != lastUIAtlas_) {
uitexture_ = CreateTextureFromFile(draw_, UIAtlas_.c_str(), ImageFileType::ZIM, false);
lastUIAtlas_ = UIAtlas_;

View File

@ -61,6 +61,8 @@ public:
void RebindTexture() const;
void BindFontTexture() const;
double FrameStartTime() const { return frameStartTime_; }
// TODO: Support transformed bounds using stencil
void PushScissor(const Bounds &bounds);
void PopScissor();
@ -119,6 +121,8 @@ private:
Draw::DrawContext *draw_ = nullptr;
Bounds bounds_;
double frameStartTime_ = 0.0;
float fontScaleX_ = 1.0f;
float fontScaleY_ = 1.0f;
UI::FontStyle *fontStyle_ = nullptr;

View File

@ -2,11 +2,13 @@
#include <algorithm>
#include <cmath>
#include <mutex>
#include "Common/Math/math_util.h"
#include "Common/Math/lin/vec3.h"
#include "Common/Math/lin/matrix4x4.h"
#include "Common/Log.h"
#include "Common/System/Display.h"
#include "Core/Config.h"
#include "Core/ConfigValues.h"
@ -32,6 +34,8 @@ void GenerateDPadEvent(int digitalX, int digitalY);
void GenerateActionButtonEvent(int digitalX, int digitalY);
void GenerateTriggerButtonEvent(int digitalX, int digitalY);
}
// deadzone is normalized - 0 to 1
// sensitivity controls how fast the deadzone reaches max value
inline float ApplyDeadzoneAxis(float x, float deadzone) {
@ -49,32 +53,35 @@ inline float ApplyDeadzoneAxis(float x, float deadzone) {
}
}
inline void ApplyDeadzoneXY(float tiltX, float tiltY, float *adjustedTiltX, float *adjustedTiltY, float deadzone, bool circular) {
inline void ApplyDeadzoneXY(float x, float y, float *adjustedX, float *adjustedY, float deadzone, bool circular) {
if (circular) {
if (tiltX == 0.0f && tiltY == 0.0f) {
*adjustedTiltX = 0.0f;
*adjustedTiltY = 0.0f;
if (x == 0.0f && y == 0.0f) {
*adjustedX = 0.0f;
*adjustedY = 0.0f;
return;
}
float magnitude = sqrtf(tiltX * tiltX + tiltY * tiltY);
float magnitude = sqrtf(x * x + y * y);
if (magnitude <= deadzone + 0.00001f) {
*adjustedTiltX = 0.0f;
*adjustedTiltY = 0.0f;
*adjustedX = 0.0f;
*adjustedY = 0.0f;
return;
}
float factor = 1.0f / (1.0f - deadzone);
float newMagnitude = (magnitude - deadzone) * factor;
*adjustedTiltX = (tiltX / magnitude) * newMagnitude;
*adjustedTiltY = (tiltY / magnitude) * newMagnitude;
*adjustedX = (x / magnitude) * newMagnitude;
*adjustedY = (y / magnitude) * newMagnitude;
} else {
*adjustedTiltX = ApplyDeadzoneAxis(tiltX, deadzone);
*adjustedTiltY = ApplyDeadzoneAxis(tiltY, deadzone);
*adjustedX = ApplyDeadzoneAxis(x, deadzone);
*adjustedY = ApplyDeadzoneAxis(y, deadzone);
}
}
namespace TiltEventProcessor {
// Also clamps to -1.0..1.0.
// This applies a (circular if desired) inverse deadzone.
inline void ApplyInverseDeadzone(float x, float y, float *outX, float *outY, float inverseDeadzone, bool circular) {
@ -301,3 +308,71 @@ void ResetTiltEvents() {
}
} // namespace TiltEventProcessor
namespace MouseEventProcessor {
// Technically, we may be OK without a mutex here.
// But, the cost isn't high.
std::mutex g_mouseMutex;
float g_mouseDeltaXAccum = 0;
float g_mouseDeltaYAccum = 0;
float g_mouseDeltaX;
float g_mouseDeltaY;
void DecayMouse(double now) {
g_mouseDeltaX = g_mouseDeltaXAccum;
g_mouseDeltaY = g_mouseDeltaYAccum;
const float decay = g_Config.fMouseSmoothing;
static double lastTime = 0.0f;
if (lastTime == 0.0) {
lastTime = now;
return;
}
double dt = now - lastTime;
lastTime = now;
// Decay the mouse deltas. We do an approximation of the old polling.
// Should be able to use a smooth exponential here, when I get around to doing
// the math.
static double accumDt = 0.0;
accumDt += dt;
const double oldPollInterval = 1.0 / 250.0; // See Windows "PollControllers".
while (accumDt > oldPollInterval) {
accumDt -= oldPollInterval;
g_mouseDeltaXAccum *= decay;
g_mouseDeltaYAccum *= decay;
}
}
void ProcessDelta(double now, float dx, float dy) {
std::unique_lock<std::mutex> lock(g_mouseMutex);
// Accumulate mouse deltas, for some kind of smoothing.
g_mouseDeltaXAccum += dx;
g_mouseDeltaYAccum += dy;
DecayMouse(now);
}
void MouseDeltaToAxes(double now, float *mx, float *my) {
std::unique_lock<std::mutex> lock(g_mouseMutex);
float scaleFactor_x = g_display.dpi_scale_x * 0.1 * g_Config.fMouseSensitivity;
float scaleFactor_y = g_display.dpi_scale_y * 0.1 * g_Config.fMouseSensitivity;
DecayMouse(now);
// TODO: Make configurable.
float mouseDeadZone = 0.1f;
float outX = clamp_value(g_mouseDeltaX * scaleFactor_x, -1.0f, 1.0f);
float outY = clamp_value(g_mouseDeltaY * scaleFactor_y, -1.0f, 1.0f);
ApplyDeadzoneXY(outX, outY, mx, my, mouseDeadZone, true);
}
} // namespace

View File

@ -16,3 +16,10 @@ extern float rawTiltAnalogX;
extern float rawTiltAnalogY;
} // namespace
namespace MouseEventProcessor {
void ProcessDelta(double now, float dx, float dy);
void MouseDeltaToAxes(double now, float *mx, float *my);
} // namespace

View File

@ -413,25 +413,9 @@ bool KeyMappingNewMouseKeyDialog::key(const KeyInput &key) {
return true;
}
static bool IgnoreAxisForMapping(int axis) {
switch (axis) {
case JOYSTICK_AXIS_ACCELEROMETER_X:
case JOYSTICK_AXIS_ACCELEROMETER_Y:
case JOYSTICK_AXIS_ACCELEROMETER_Z:
// Ignore the accelerometer for mapping for now.
// We use tilt control for these.
return true;
default:
return false;
}
}
void KeyMappingNewKeyDialog::axis(const AxisInput &axis) {
if (time_now_d() < delayUntil_)
return;
if (IgnoreAxisForMapping(axis.axisId))
return;
if (ignoreInput_)
return;
@ -468,8 +452,6 @@ void KeyMappingNewKeyDialog::axis(const AxisInput &axis) {
void KeyMappingNewMouseKeyDialog::axis(const AxisInput &axis) {
if (mapped_)
return;
if (IgnoreAxisForMapping(axis.axisId))
return;
if (axis.value > AXIS_BIND_THRESHOLD) {
mapped_ = true;
@ -583,222 +565,6 @@ UI::EventReturn AnalogSetupScreen::OnResetToDefaults(UI::EventParams &e) {
return UI::EVENT_DONE;
}
void TouchTestScreen::touch(const TouchInput &touch) {
UIDialogScreenWithGameBackground::touch(touch);
if (touch.flags & TOUCH_DOWN) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
WARN_LOG(SYSTEM, "Double touch");
touches_[i].x = touch.x;
touches_[i].y = touch.y;
found = true;
}
}
if (!found) {
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == -1) {
touches_[i].id = touch.id;
touches_[i].x = touch.x;
touches_[i].y = touch.y;
break;
}
}
}
}
if (touch.flags & TOUCH_MOVE) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
touches_[i].x = touch.x;
touches_[i].y = touch.y;
found = true;
}
}
if (!found) {
WARN_LOG(SYSTEM, "Move without touch down: %d", touch.id);
}
}
if (touch.flags & TOUCH_UP) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
found = true;
touches_[i].id = -1;
break;
}
}
if (!found) {
WARN_LOG(SYSTEM, "Touch release without touch down");
}
}
}
// TODO: Move this screen out into its own file.
void TouchTestScreen::CreateViews() {
using namespace UI;
auto di = GetI18NCategory(I18NCat::DIALOG);
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
root_ = new LinearLayout(ORIENT_VERTICAL);
LinearLayout *theTwo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
lastKeyEvents_ = theTwo->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
root_->Add(theTwo);
#if !PPSSPP_PLATFORM(UWP)
static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };
PopupMultiChoice *renderingBackendChoice = root_->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), I18NCat::GRAPHICS, screenManager()));
renderingBackendChoice->OnChoice.Handle(this, &TouchTestScreen::OnRenderingBackend);
if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))
renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL);
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9))
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9);
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11);
if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))
renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN);
#endif
#if PPSSPP_PLATFORM(ANDROID)
root_->Add(new Choice(gr->T("Recreate Activity")))->OnClick.Handle(this, &TouchTestScreen::OnRecreateActivity);
#endif
root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange);
root_->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
}
void TouchTestScreen::UpdateLogView() {
while (keyEventLog_.size() > 8) {
keyEventLog_.erase(keyEventLog_.begin());
}
std::string text;
for (auto &iter : keyEventLog_) {
text += iter + "\n";
}
if (lastKeyEvents_) {
lastKeyEvents_->SetText(text);
}
}
bool TouchTestScreen::key(const KeyInput &key) {
UIScreen::key(key);
char buf[512];
snprintf(buf, sizeof(buf), "%s (%d) Device ID: %d [%s%s%s%s]", KeyMap::GetKeyName(key.keyCode).c_str(), key.keyCode, key.deviceId,
(key.flags & KEY_IS_REPEAT) ? "REP" : "",
(key.flags & KEY_UP) ? "UP" : "",
(key.flags & KEY_DOWN) ? "DOWN" : "",
(key.flags & KEY_CHAR) ? "CHAR" : "");
keyEventLog_.push_back(buf);
UpdateLogView();
return true;
}
void TouchTestScreen::axis(const AxisInput &axis) {
// This just filters out accelerometer events. We show everything else.
if (IgnoreAxisForMapping(axis.axisId))
return;
char buf[512];
snprintf(buf, sizeof(buf), "Axis: %s (%d) (value %1.3f) Device ID: %d",
KeyMap::GetAxisName(axis.axisId).c_str(), axis.axisId, axis.value, axis.deviceId);
keyEventLog_.push_back(buf);
if (keyEventLog_.size() > 8) {
keyEventLog_.erase(keyEventLog_.begin());
}
UpdateLogView();
}
void TouchTestScreen::DrawForeground(UIContext &dc) {
Bounds bounds = dc.GetLayoutBounds();
double now = time_now_d();
double delta = now - lastFrameTime_;
lastFrameTime_ = now;
dc.BeginNoTex();
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
}
}
dc.Flush();
dc.Begin();
char buffer[4096];
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y);
dc.DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII);
}
}
char extra_debug[2048]{};
#if PPSSPP_PLATFORM(ANDROID)
truncate_cpy(extra_debug, Android_GetInputDeviceDebugString().c_str());
#endif
snprintf(buffer, sizeof(buffer),
#if PPSSPP_PLATFORM(ANDROID)
"display_res: %dx%d\n"
#endif
"dp_res: %dx%d pixel_res: %dx%d\n"
"g_dpi: %0.3f g_dpi_scale: %0.3fx%0.3f\n"
"g_dpi_scale_real: %0.3fx%0.3f\n"
"delta: %0.2f ms fps: %0.3f\n%s",
#if PPSSPP_PLATFORM(ANDROID)
System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES),
#endif
g_display.dp_xres, g_display.dp_yres, g_display.pixel_xres, g_display.pixel_yres,
g_display.dpi, g_display.dpi_scale_x, g_display.dpi_scale_y,
g_display.dpi_scale_real_x, g_display.dpi_scale_real_y,
delta * 1000.0, 1.0 / delta,
extra_debug);
// On Android, also add joystick debug data.
dc.DrawTextShadow(buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
dc.Flush();
}
void RecreateActivity() {
const int SYSTEM_JELLYBEAN = 16;
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= SYSTEM_JELLYBEAN) {
INFO_LOG(SYSTEM, "Sending recreate");
System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);
INFO_LOG(SYSTEM, "Got back from recreate");
} else {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
System_Toast(gr->T("Must Restart", "You must restart PPSSPP for this change to take effect"));
}
}
UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) {
System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE);
if (g_Config.iAndroidHwScale != 0) {
RecreateActivity();
}
return UI::EVENT_DONE;
}
UI::EventReturn TouchTestScreen::OnRenderingBackend(UI::EventParams &e) {
g_Config.Save("GameSettingsScreen::RenderingBackend");
System_RestartApp("--touchscreentest");
return UI::EVENT_DONE;
}
UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) {
RecreateActivity();
return UI::EVENT_DONE;
}
class Backplate : public UI::InertView {
public:
Backplate(float scale, UI::LayoutParams *layoutParams = nullptr) : InertView(layoutParams), scale_(scale) {}

View File

@ -138,47 +138,6 @@ private:
JoystickHistoryView *stickView_[2]{};
};
class TouchTestScreen : public UIDialogScreenWithGameBackground {
public:
TouchTestScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) {
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
touches_[i].id = -1;
}
}
void touch(const TouchInput &touch) override;
void DrawForeground(UIContext &dc) override;
bool key(const KeyInput &key) override;
void axis(const AxisInput &axis) override;
const char *tag() const override { return "TouchTest"; }
protected:
struct TrackedTouch {
int id;
float x;
float y;
};
enum {
MAX_TOUCH_POINTS = 10,
};
TrackedTouch touches_[MAX_TOUCH_POINTS]{};
std::vector<std::string> keyEventLog_;
UI::TextView *lastKeyEvents_ = nullptr;
double lastFrameTime_ = 0.0;
void CreateViews() override;
void UpdateLogView();
UI::EventReturn OnImmersiveModeChange(UI::EventParams &e);
UI::EventReturn OnRenderingBackend(UI::EventParams &e);
UI::EventReturn OnRecreateActivity(UI::EventParams &e);
};
class MockPSP;
class VisualMappingScreen : public UIDialogScreenWithGameBackground {

View File

@ -34,9 +34,6 @@
#include "Common/System/OSD.h"
#include "Common/GPU/OpenGL/GLFeatures.h"
#if !PPSSPP_PLATFORM(UWP)
#include "Common/GPU/Vulkan/VulkanContext.h"
#endif
#include "Common/File/AndroidStorage.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Encoding/Utf8.h"
@ -72,9 +69,7 @@
#include "UI/ControlMappingScreen.h"
#include "UI/GameSettingsScreen.h"
#ifdef _WIN32
#include "Common/CommonWindows.h"
// Want to avoid including the full header here as it includes d3dx.h
int GetD3DCompilerVersion();
#endif
@ -799,11 +794,6 @@ void SystemInfoScreen::CreateTabs() {
}
} else if (GetGPUBackend() == GPUBackend::VULKAN) {
LinearLayout *gpuExtensions = AddTab("DevSystemInfoOGLExt", si->T("Vulkan Features"));
#if !PPSSPP_PLATFORM(UWP)
// Vulkan specific code here, can't be bothered to abstract.
// OK because of above check.
VulkanContext *vk = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT);
CollapsibleSection *vulkanFeatures = gpuExtensions->Add(new CollapsibleSection(si->T("Vulkan Features")));
std::vector<std::string> features = draw->GetFeatureList();
@ -812,23 +802,14 @@ void SystemInfoScreen::CreateTabs() {
}
CollapsibleSection *presentModes = gpuExtensions->Add(new CollapsibleSection(si->T("Present Modes")));
for (auto mode : vk->GetAvailablePresentModes()) {
std::string str = VulkanPresentModeToString(mode);
if (mode == vk->GetPresentMode()) {
str += std::string(" (") + di->T("Current") + ")";
}
presentModes->Add(new TextView(str, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
for (auto mode : draw->GetPresentModeList(di->T("Current"))) {
presentModes->Add(new TextView(mode, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
CollapsibleSection *colorFormats = gpuExtensions->Add(new CollapsibleSection(si->T("Display Color Formats")));
if (vk) {
for (auto &format : vk->SurfaceFormats()) {
std::string line = StringFromFormat("%s : %s", VulkanFormatToString(format.format), VulkanColorSpaceToString(format.colorSpace));
colorFormats->Add(new TextView(line,
new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
for (auto &format : draw->GetSurfaceFormatList()) {
colorFormats->Add(new TextView(format, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
}
#endif
CollapsibleSection *enabledExtensions = gpuExtensions->Add(new CollapsibleSection(std::string(si->T("Vulkan Extensions")) + " (" + di->T("Enabled") + ")"));
std::vector<std::string> extensions = draw->GetExtensionList(true, true);
@ -1471,3 +1452,215 @@ void FrameDumpTestScreen::update() {
RecreateViews();
}
}
void TouchTestScreen::touch(const TouchInput &touch) {
UIDialogScreenWithGameBackground::touch(touch);
if (touch.flags & TOUCH_DOWN) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
WARN_LOG(SYSTEM, "Double touch");
touches_[i].x = touch.x;
touches_[i].y = touch.y;
found = true;
}
}
if (!found) {
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == -1) {
touches_[i].id = touch.id;
touches_[i].x = touch.x;
touches_[i].y = touch.y;
break;
}
}
}
}
if (touch.flags & TOUCH_MOVE) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
touches_[i].x = touch.x;
touches_[i].y = touch.y;
found = true;
}
}
if (!found) {
WARN_LOG(SYSTEM, "Move without touch down: %d", touch.id);
}
}
if (touch.flags & TOUCH_UP) {
bool found = false;
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id == touch.id) {
found = true;
touches_[i].id = -1;
break;
}
}
if (!found) {
WARN_LOG(SYSTEM, "Touch release without touch down");
}
}
}
// TODO: Move this screen out into its own file.
void TouchTestScreen::CreateViews() {
using namespace UI;
auto di = GetI18NCategory(I18NCat::DIALOG);
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
root_ = new LinearLayout(ORIENT_VERTICAL);
LinearLayout *theTwo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
lastKeyEvents_ = theTwo->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
root_->Add(theTwo);
#if !PPSSPP_PLATFORM(UWP)
static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };
PopupMultiChoice *renderingBackendChoice = root_->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), I18NCat::GRAPHICS, screenManager()));
renderingBackendChoice->OnChoice.Handle(this, &TouchTestScreen::OnRenderingBackend);
if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))
renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL);
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9))
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9);
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11);
if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))
renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN);
#endif
#if PPSSPP_PLATFORM(ANDROID)
root_->Add(new Choice(gr->T("Recreate Activity")))->OnClick.Handle(this, &TouchTestScreen::OnRecreateActivity);
#endif
root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange);
root_->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
}
void TouchTestScreen::UpdateLogView() {
while (keyEventLog_.size() > 8) {
keyEventLog_.erase(keyEventLog_.begin());
}
std::string text;
for (auto &iter : keyEventLog_) {
text += iter + "\n";
}
if (lastKeyEvents_) {
lastKeyEvents_->SetText(text);
}
}
bool TouchTestScreen::key(const KeyInput &key) {
UIScreen::key(key);
char buf[512];
snprintf(buf, sizeof(buf), "%s (%d) Device ID: %d [%s%s%s%s]", KeyMap::GetKeyName(key.keyCode).c_str(), key.keyCode, key.deviceId,
(key.flags & KEY_IS_REPEAT) ? "REP" : "",
(key.flags & KEY_UP) ? "UP" : "",
(key.flags & KEY_DOWN) ? "DOWN" : "",
(key.flags & KEY_CHAR) ? "CHAR" : "");
keyEventLog_.push_back(buf);
UpdateLogView();
return true;
}
void TouchTestScreen::axis(const AxisInput &axis) {
char buf[512];
snprintf(buf, sizeof(buf), "Axis: %s (%d) (value %1.3f) Device ID: %d",
KeyMap::GetAxisName(axis.axisId).c_str(), axis.axisId, axis.value, axis.deviceId);
keyEventLog_.push_back(buf);
if (keyEventLog_.size() > 8) {
keyEventLog_.erase(keyEventLog_.begin());
}
UpdateLogView();
}
void TouchTestScreen::DrawForeground(UIContext &dc) {
Bounds bounds = dc.GetLayoutBounds();
double now = dc.FrameStartTime();
double delta = now - lastFrameTime_;
lastFrameTime_ = now;
dc.BeginNoTex();
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
}
}
dc.Flush();
dc.Begin();
char buffer[4096];
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
if (touches_[i].id != -1) {
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y);
dc.DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII);
}
}
char extra_debug[2048]{};
#if PPSSPP_PLATFORM(ANDROID)
truncate_cpy(extra_debug, Android_GetInputDeviceDebugString().c_str());
#endif
snprintf(buffer, sizeof(buffer),
#if PPSSPP_PLATFORM(ANDROID)
"display_res: %dx%d\n"
#endif
"dp_res: %dx%d pixel_res: %dx%d\n"
"g_dpi: %0.3f g_dpi_scale: %0.3fx%0.3f\n"
"g_dpi_scale_real: %0.3fx%0.3f\n"
"delta: %0.2f ms fps: %0.3f\n%s",
#if PPSSPP_PLATFORM(ANDROID)
System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES),
#endif
g_display.dp_xres, g_display.dp_yres, g_display.pixel_xres, g_display.pixel_yres,
g_display.dpi, g_display.dpi_scale_x, g_display.dpi_scale_y,
g_display.dpi_scale_real_x, g_display.dpi_scale_real_y,
delta * 1000.0, 1.0 / delta,
extra_debug);
// On Android, also add joystick debug data.
dc.DrawTextShadow(buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
dc.Flush();
}
void RecreateActivity() {
const int SYSTEM_JELLYBEAN = 16;
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= SYSTEM_JELLYBEAN) {
INFO_LOG(SYSTEM, "Sending recreate");
System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);
INFO_LOG(SYSTEM, "Got back from recreate");
} else {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
System_Toast(gr->T("Must Restart", "You must restart PPSSPP for this change to take effect"));
}
}
UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) {
System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE);
if (g_Config.iAndroidHwScale != 0) {
RecreateActivity();
}
return UI::EVENT_DONE;
}
UI::EventReturn TouchTestScreen::OnRenderingBackend(UI::EventParams &e) {
g_Config.Save("GameSettingsScreen::RenderingBackend");
System_RestartApp("--touchscreentest");
return UI::EVENT_DONE;
}
UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) {
RecreateActivity();
return UI::EVENT_DONE;
}

View File

@ -222,6 +222,47 @@ private:
std::shared_ptr<http::Request> dumpDownload_;
};
class TouchTestScreen : public UIDialogScreenWithGameBackground {
public:
TouchTestScreen(const Path &gamePath) : UIDialogScreenWithGameBackground(gamePath) {
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
touches_[i].id = -1;
}
}
void touch(const TouchInput &touch) override;
void DrawForeground(UIContext &dc) override;
bool key(const KeyInput &key) override;
void axis(const AxisInput &axis) override;
const char *tag() const override { return "TouchTest"; }
protected:
struct TrackedTouch {
int id;
float x;
float y;
};
enum {
MAX_TOUCH_POINTS = 10,
};
TrackedTouch touches_[MAX_TOUCH_POINTS]{};
std::vector<std::string> keyEventLog_;
UI::TextView *lastKeyEvents_ = nullptr;
double lastFrameTime_ = 0.0;
void CreateViews() override;
void UpdateLogView();
UI::EventReturn OnImmersiveModeChange(UI::EventParams &e);
UI::EventReturn OnRenderingBackend(UI::EventParams &e);
UI::EventReturn OnRecreateActivity(UI::EventParams &e);
};
void DrawProfile(UIContext &ui);
const char *GetCompilerABI();

View File

@ -114,6 +114,7 @@
#include "UI/AudioCommon.h"
#include "UI/BackgroundAudio.h"
#include "UI/ControlMappingScreen.h"
#include "UI/DevScreens.h"
#include "UI/DiscordIntegration.h"
#include "UI/EmuScreen.h"
#include "UI/GameInfoCache.h"
@ -1323,40 +1324,10 @@ void NativeAxis(const AxisInput *axes, size_t count) {
}
}
float g_mouseDeltaX = 0;
float g_mouseDeltaY = 0;
void NativeMouseDelta(float dx, float dy) {
// Remap, shared code. Then send it as a regular axis event.
if (!g_Config.bMouseControl)
return;
// Accumulate mouse deltas, for some kind of smoothing.
g_mouseDeltaX += dx;
g_mouseDeltaY += dy;
}
// Called from NativeFrame.
// Called from NativeFrame and from NativeMouseDelta.
static void SendMouseDeltaAxis() {
static double lastTime = 0.0f;
double now = time_now_d();
if (lastTime == 0.0) {
lastTime = now;
return;
}
double dt = now - lastTime;
lastTime = now;
float scaleFactor_x = g_display.dpi_scale_x * 0.1 * g_Config.fMouseSensitivity;
float scaleFactor_y = g_display.dpi_scale_y * 0.1 * g_Config.fMouseSensitivity;
float mx = clamp_value(g_mouseDeltaX * scaleFactor_x, -1.0f, 1.0f);
float my = clamp_value(g_mouseDeltaY * scaleFactor_y, -1.0f, 1.0f);
// Decay the mouse deltas. This is where we should use dt.
float decay = expf(-dt * 50.0f * (1.0f - g_Config.fMouseSmoothing));
g_mouseDeltaX *= decay;
g_mouseDeltaY *= decay;
float mx, my;
MouseEventProcessor::MouseDeltaToAxes(time_now_d(), &mx, &my);
AxisInput axis[2];
axis[0].axisId = JOYSTICK_AXIS_MOUSE_REL_X;
@ -1377,6 +1348,16 @@ static void SendMouseDeltaAxis() {
}
}
void NativeMouseDelta(float dx, float dy) {
// Remap, shared code. Then send it as a regular axis event.
if (!g_Config.bMouseControl)
return;
MouseEventProcessor::ProcessDelta(time_now_d(), dx, dy);
SendMouseDeltaAxis();
}
void NativeAccelerometer(float tiltX, float tiltY, float tiltZ) {
if (g_Config.iTiltInputType == TILT_NULL) {
// if tilt events are disabled, don't do anything special.

View File

@ -77,7 +77,7 @@ void WindowsInputManager::Init() {
}
void WindowsInputManager::PollControllers() {
static const int CHECK_FREQUENCY = 71;
static const int CHECK_FREQUENCY = 71; // Just an arbitrary prime to try to not collide with other periodic checks.
if (checkCounter_++ > CHECK_FREQUENCY) {
#ifndef _M_ARM
size_t newCount = DinputDevice::getNumPads();