Merge remote-tracking branch 'upstream/HEAD' into dev

This commit is contained in:
LA
2026-02-09 21:27:17 +08:00
4 changed files with 97 additions and 20 deletions

View File

@@ -117,10 +117,18 @@ namespace osu.Framework.Graphics.Transforms
protected void ComputeSingleValue(float dt, ref float current, ref float velocity, float target, float targetVelocity)
{
float k2Stable = MathF.Max(MathF.Max(k2, dt * dt / 2 + dt * k1 / 2), dt * k1);
float k2Stable = MathF.Max(MathF.Max(k2, (dt * dt) / 2 + (dt * k1) / 2), dt * k1);
current += dt * velocity;
velocity += (dt * (target + k3 * targetVelocity - current - k1 * velocity)) / k2Stable;
velocity += (dt * (target + (k3 * targetVelocity) - current - (k1 * velocity))) / k2Stable;
}
protected void ComputeSingleValue(float dt, ref double current, ref double velocity, double target, double targetVelocity)
{
float k2Stable = MathF.Max(MathF.Max(k2, (dt * dt) / 2 + (dt * k1) / 2), dt * k1);
current += dt * velocity;
velocity += (dt * (target + (k3 * targetVelocity) - current - (k1 * velocity))) / k2Stable;
}
}
@@ -136,6 +144,18 @@ namespace osu.Framework.Graphics.Transforms
}
}
public class DoubleSpring : Spring<double>
{
protected override double GetTargetVelocity(double target, double previousTarget, float dt) => (target - previousTarget) / dt;
protected override double ComputeNextValue(float dt, double target, double targetVelocity)
{
ComputeSingleValue(dt, ref Current, ref Velocity, target, targetVelocity);
return Current;
}
}
public class Vector2Spring : Spring<Vector2>
{
protected override Vector2 GetTargetVelocity(Vector2 target, Vector2 previousTarget, float dt) => (target - previousTarget) / dt;

View File

@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -44,7 +45,7 @@ namespace osu.Framework.Input.Handlers.Tablet
public Bindable<float> Rotation { get; } = new Bindable<float>();
public BindableFloat PressureThreshold { get; } = new BindableFloat(0.0f)
public BindableFloat PressureThreshold { get; } = new BindableFloat
{
MinValue = 0f,
MaxValue = 1f,
@@ -119,7 +120,34 @@ namespace osu.Framework.Input.Handlers.Tablet
enqueueInput(new MousePositionRelativeInputFromPen { Delta = new Vector2(delta.X, delta.Y), DeviceType = lastTabletDeviceType });
}
void IPressureHandler.SetPressure(float percentage) => enqueueInput(new MouseButtonInputFromPen(percentage > PressureThreshold.Value) { DeviceType = lastTabletDeviceType });
private bool penPressed;
void IPressureHandler.SetPressure(float pressure)
{
// Most important for edge cases where users have pressure set to 0 or 1 and tablets can report fuzzy data.
const float hysteresis_half = 0.02f;
pressure = Math.Clamp(pressure, 0f, 1f);
float releaseThreshold = PressureThreshold.Value - hysteresis_half;
float pressThreshold = PressureThreshold.Value + hysteresis_half;
// keep press..release threshold range constant for edge cases.
if (releaseThreshold < 0f)
{
pressThreshold = hysteresis_half * 2;
releaseThreshold = 0f;
}
else if (pressThreshold > 1f)
{
releaseThreshold = 1 - (hysteresis_half * 2);
pressThreshold = 1f;
}
setPressed(penPressed
? pressure > releaseThreshold
: pressure > pressThreshold);
}
private void handleTabletsChanged(object? sender, IEnumerable<TabletReference> tablets)
{
@@ -135,7 +163,22 @@ namespace osu.Framework.Input.Handlers.Tablet
updateOutputArea(host.Window);
}
else
{
// Ensure we don't leave the simulated mouse button pressed if the tablet disappears.
setPressed(false);
tablet.Value = null;
}
}
private void setPressed(bool pressed)
{
// Importantly, only fire input when the state changes.
// If we fire more often, this may intefere with users that click with mouse but use tablet for positional input (hovering).
if (pressed == penPressed)
return;
enqueueInput(new MouseButtonInputFromPen(pressed) { DeviceType = lastTabletDeviceType });
penPressed = pressed;
}
private void handleDeviceReported(object? sender, IDeviceReport report)

View File

@@ -275,8 +275,7 @@ namespace osu.Framework.Platform.SDL2
break;
case SDL_EventType.SDL_CONTROLLERDEVICEREMOVED:
SDL_GameControllerClose(controllers[evtCdevice.which].ControllerHandle);
controllers.Remove(evtCdevice.which);
removeJoystick(evtCdevice.which);
break;
case SDL_EventType.SDL_CONTROLLERDEVICEREMAPPED:
@@ -323,6 +322,20 @@ namespace osu.Framework.Platform.SDL2
controllers[instanceID] = new SDL2ControllerBindings(joystick, controller);
}
private void removeJoystick(int which)
{
int instanceID = SDL_JoystickGetDeviceInstanceID(which);
if (controllers.Remove(instanceID, out var controller))
{
if (controller.ControllerHandle != IntPtr.Zero)
SDL_GameControllerClose(controller.ControllerHandle);
if (controller.JoystickHandle != IntPtr.Zero)
SDL_JoystickClose(controller.JoystickHandle);
}
}
/// <summary>
/// Populates <see cref="controllers"/> with joysticks that are already connected.
/// </summary>
@@ -343,12 +356,7 @@ namespace osu.Framework.Platform.SDL2
break;
case SDL_EventType.SDL_JOYDEVICEREMOVED:
// if the joystick is already closed, ignore it
if (!controllers.ContainsKey(evtJdevice.which))
break;
SDL_JoystickClose(controllers[evtJdevice.which].JoystickHandle);
controllers.Remove(evtJdevice.which);
removeJoystick(evtJdevice.which);
break;
}
}

View File

@@ -324,8 +324,7 @@ namespace osu.Framework.Platform.SDL3
break;
case SDL_EventType.SDL_EVENT_GAMEPAD_REMOVED:
SDL_CloseGamepad(controllers[evtCdevice.which].GamepadHandle);
controllers.Remove(evtCdevice.which);
removeJoystick(evtCdevice.which);
break;
case SDL_EventType.SDL_EVENT_GAMEPAD_REMAPPED:
@@ -370,6 +369,18 @@ namespace osu.Framework.Platform.SDL3
controllers[instanceID] = new SDL3ControllerBindings(joystick, controller);
}
private void removeJoystick(SDL_JoystickID instanceID)
{
if (controllers.Remove(instanceID, out var controller))
{
if (controller.GamepadHandle != null)
SDL_CloseGamepad(controller.GamepadHandle);
if (controller.JoystickHandle != null)
SDL_CloseJoystick(controller.JoystickHandle);
}
}
/// <summary>
/// Populates <see cref="controllers"/> with joysticks that are already connected.
/// </summary>
@@ -395,12 +406,7 @@ namespace osu.Framework.Platform.SDL3
break;
case SDL_EventType.SDL_EVENT_JOYSTICK_REMOVED:
// if the joystick is already closed, ignore it
if (!controllers.ContainsKey(evtJdevice.which))
break;
SDL_CloseJoystick(controllers[evtJdevice.which].JoystickHandle);
controllers.Remove(evtJdevice.which);
removeJoystick(evtJdevice.which);
break;
}
}