2
0
mirror of https://github.com/KDE/kdeconnect-android synced 2025-08-22 01:51:47 +00:00
kdeconnect-android/src/org/kde/kdeconnect/Plugins/MousePadPlugin/PointerAccelerationProfileFactory.java
Albert Vaca Cintora fd51ec7c14
Fix linter warnings
2025-04-05 00:44:08 +02:00

176 lines
6.4 KiB
Java

/*
* SPDX-FileCopyrightText: 2018 Chansol Yang <CosmicSubspace@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
package org.kde.kdeconnect.Plugins.MousePadPlugin;
public class PointerAccelerationProfileFactory {
/* The simplest profile. Merely adds the mouse deltas without any processing. */
private static class DefaultProfile extends PointerAccelerationProfile {
float accumulatedX = 0.0f;
float accumulatedY = 0.0f;
@Override
public void touchMoved(float deltaX, float deltaY, long eventTime) {
accumulatedX += deltaX;
accumulatedY += deltaY;
}
@Override
public MouseDelta commitAcceleratedMouseDelta(MouseDelta reusedObject) {
MouseDelta result;
if (reusedObject == null) result = new MouseDelta();
else result = reusedObject;
result.x = accumulatedX;
result.y = accumulatedY;
accumulatedY = 0;
accumulatedX = 0;
return result;
}
}
/* Base class for acceleration profiles that takes touch movement speed into account.
* To calculate the speed, a history of 32 touch events are stored
* and then later used to calculate the speed. */
private static abstract class SpeedBasedAccelerationProfile extends PointerAccelerationProfile {
float accumulatedX = 0.0f;
float accumulatedY = 0.0f;
// Higher values will reduce the amount of noise in the speed calculation
// but will also increase latency until the acceleration kicks in.
// 150ms seemed like a nice middle ground.
final long freshThreshold = 150;
private static class TouchDeltaEvent {
final float x;
final float y;
final long time;
TouchDeltaEvent(float x, float y, long t) {
this.x = x;
this.y = y;
this.time = t;
}
}
private final TouchDeltaEvent[] touchEventHistory = new TouchDeltaEvent[32];
/* add an event to the touchEventHistory array, shifting everything else in the array. */
private void addHistory(float deltaX, float deltaY, long eventTime) {
System.arraycopy(touchEventHistory, 0, touchEventHistory, 1, touchEventHistory.length - 1);
touchEventHistory[0] = new TouchDeltaEvent(deltaX, deltaY, eventTime);
}
@Override
public void touchMoved(float deltaX, float deltaY, long eventTime) {
// To calculate the touch movement speed,
// we iterate through the touchEventHistory array,
// adding up the distance moved for each event.
// Breaks if a touchEventHistory entry is too "stale".
float distanceMoved = 0.0f;
long deltaT = 0;
for (TouchDeltaEvent aTouchEventHistory : touchEventHistory) {
if (aTouchEventHistory == null) break;
if (eventTime - aTouchEventHistory.time > freshThreshold) break;
distanceMoved += (float) Math.sqrt(
aTouchEventHistory.x * aTouchEventHistory.x
+ aTouchEventHistory.y * aTouchEventHistory.y);
deltaT = eventTime - aTouchEventHistory.time;
}
float multiplier;
if (deltaT == 0) {
// Edge case when there are no historical touch data to calculate speed from.
multiplier = 0;
} else {
float speed = distanceMoved / (deltaT / 1000.0f); // units: px/sec
multiplier = calculateMultiplier(speed);
}
if (multiplier < 0.01f) multiplier = 0.01f;
accumulatedX += deltaX * multiplier;
accumulatedY += deltaY * multiplier;
addHistory(deltaX, deltaY, eventTime);
}
/* Should be overridden by the child class.
* Given the current touch movement speed, this method should return a multiplier
* for the touch delta. ( mouse_delta = touch_delta * multiplier ) */
abstract float calculateMultiplier(float speed);
@Override
public MouseDelta commitAcceleratedMouseDelta(MouseDelta reusedObject) {
MouseDelta result;
if (reusedObject == null) result = new MouseDelta();
else result = reusedObject;
/* This makes sure that only the integer components of the deltas are sent,
* since the coordinates are converted to integers in the desktop client anyway.
* The leftover fractional part is stored and added later; this makes
* the cursor move much smoother in slow speeds. */
result.x = (int) accumulatedX;
result.y = (int) accumulatedY;
accumulatedY = accumulatedY % 1.0f;
accumulatedX = accumulatedX % 1.0f;
return result;
}
}
/* Pointer acceleration with mouse_delta = touch_delta * ( touch_speed ^ exponent )
* It is similar to x.org server's Polynomial pointer acceleration profile. */
private static class PolynomialProfile extends SpeedBasedAccelerationProfile {
final float exponent;
PolynomialProfile(float exponent) {
this.exponent = exponent;
}
@Override
float calculateMultiplier(float speed) {
// The value 600 was chosen arbitrarily.
return (float) (Math.pow(speed / 600, exponent));
}
}
/* Mimics the behavior of xorg's Power profile.
* Currently not visible to the user since it is rather hard to control. */
private static class PowerProfile extends SpeedBasedAccelerationProfile {
@Override
float calculateMultiplier(float speed) {
return (float) (Math.pow(2, speed / 1000)) - 1;
}
}
public static PointerAccelerationProfile getProfileWithName(String name) {
switch (name) {
case "weaker":
return new PolynomialProfile(0.25f);
case "weak":
return new PolynomialProfile(0.5f);
case "medium":
return new PolynomialProfile(1.0f);
case "strong":
return new PolynomialProfile(1.5f);
case "stronger":
return new PolynomialProfile(2.0f);
case "noacceleration":
default:
return new DefaultProfile();
}
}
}