Skip to content

Commit

Permalink
Reviewing all the threeFingersDrag process fixes #1
Browse files Browse the repository at this point in the history
  • Loading branch information
ClementGre committed Nov 27, 2022
1 parent 86ebf7e commit b6c158d
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 46 deletions.
2 changes: 1 addition & 1 deletion ThreeFingersDragOnWindows.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<OutputType>WinExe</OutputType>
<!-- TODO: switch to WinExe-->
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
Expand Down
12 changes: 10 additions & 2 deletions src/touchpad/ContactsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ namespace ThreeFingersDragOnWindows.src.touchpad;
public class ContactsManager<T> where T : Window, IContactsManager {
private readonly List<TouchpadContact> _lastContacts = new();
private readonly T _source;

private HwndSource _targetSource;

private long _lastInput;

public ContactsManager(T source){
_source = source;
}
Expand Down Expand Up @@ -45,14 +46,21 @@ private void RegisterTouchpadContact(TouchpadContact contact){
foreach(var lastContact in _lastContacts)
if(lastContact.ContactId == contact.ContactId){
// A contact is registered twice: send the event with the list of all contacts
_source.OnTouchpadContact(_lastContacts.ToArray());
if (Ctms() - _lastInput < 20){
_source.OnTouchpadContact(_lastContacts.ToArray());
}
_lastContacts.Clear();
break;
}

_lastInput = Ctms();
// Add the contact to the list
_lastContacts.Add(contact);
}

private long Ctms(){
return new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds();
}
}

public interface IContactsManager {
Expand Down
102 changes: 62 additions & 40 deletions src/touchpad/ThreeFingersDrag.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Timers;
using Microsoft.VisualBasic.Devices;
using ThreeFingersDragOnWindows.src.utils;

namespace ThreeFingersDragOnWindows.src.touchpad;
Expand All @@ -9,18 +10,24 @@ public class ThreeFingersDrag {
private readonly Timer _oneFingerTimer = new(20);

private bool _isDragging;
private int _numberOfThreeFingersInputs = 0;

private ThreeFingersPoints _firstOneFingerPoints = ThreeFingersPoints.Empty;
private bool _isFirstOneFingerInput;
private long _firstOneFingerContact;
private long _lastOneFingerContact;


private ThreeFingersPoints _lastPoints = ThreeFingersPoints.Empty;
private MousePoint _lastOneFingerPoint = MousePoint.Empty;
private ThreeFingersPoints _lastThreeFingersPoints = ThreeFingersPoints.Empty;
private bool _isFirstThreeFingersInput;
private long _firstThreeFingersContact;
private long _lastThreeFingersContact;
private IntMousePoint _lastDragMousePoint = new IntMousePoint(0, 0);

public ThreeFingersDrag(){
// Setup timer
_dragEndTimer.Elapsed += (_, _) => CheckDragEnd();
_oneFingerTimer.Elapsed += (_, _) => {
if(Ctms() - _lastThreeFingersContact > 250){
if (Ctms() - _lastThreeFingersContact > 250){
stopDrag();
}
};
Expand All @@ -30,70 +37,85 @@ public ThreeFingersDrag(){

public void OnTouchpadContact(TouchpadContact[] contacts){
ThreeFingersPoints points = new(contacts);

if(contacts.Length == 3) {
if(_numberOfThreeFingersInputs == 0) _firstThreeFingersContact = Ctms();
_numberOfThreeFingersInputs++;

if(contacts.Length == 3){
_isFirstOneFingerInput = true;
if(_isFirstThreeFingersInput){
_firstThreeFingersContact = Ctms();
_isFirstThreeFingersInput = false;
}

if(!_isDragging){
if (Ctms() - _firstThreeFingersContact > 100) {
if (Ctms() - _firstThreeFingersContact > 100){ // When placing four fingers, a three fingers input can be detected for less than 100ms
_isDragging = true;
MouseOperations.Click(true);
MouseOperations.Click(true);
}

}else{
if(App.Prefs.ThreeFingersMove && _lastPoints != ThreeFingersPoints.Empty){
var dist2d = points.GetLongestDist2D(_lastThreeFingersPoints);

var dist2d = points.GetLongestDist2D(_lastPoints);
if (App.Prefs.ThreeFingersMove // Three Fingers drag enabled in preferences
&& _lastThreeFingersPoints != ThreeFingersPoints.Empty // Last contact is a three fingers drag contact
&& Math.Sqrt(Math.Pow(dist2d.x, 2) + Math.Pow(dist2d.y, 2)) <= 30){ // Fingers can be released and replaced without catching any one/two finger contact. This makes sure the fingers haven't been replaced on the touchpad

float elapsed = _lastThreeFingersContact == 0 ? 0 : Ctms() - _lastThreeFingersContact;

// Apply the Mouse Speed preference
dist2d.Multiply(App.Prefs.MouseSpeed / 60);

// Calculate the mouse velocity
var mouseVelocity = (float) Math.Max(0.2, Math.Min(dist2d.Length() / elapsed, 20));
if(float.IsNaN(mouseVelocity) || float.IsInfinity(mouseVelocity)) mouseVelocity = 1;
var mouseVelocity = (float)Math.Max(0.2, Math.Min(dist2d.Length() / elapsed, 20));
if (float.IsNaN(mouseVelocity) || float.IsInfinity(mouseVelocity)) mouseVelocity = 1;

// Calculate the pointer velocity in function of the mouse velocity and the preference
var pointerVelocity = (float) (App.Prefs.MouseAcceleration/10 * Math.Pow(mouseVelocity, 2) + 0.4 * mouseVelocity);
pointerVelocity = (float) Math.Max(0.4, Math.Min(pointerVelocity, 1.6)); // Clamp
if(App.Prefs.MouseAcceleration == 0) pointerVelocity = 1; // Disable acceleration
var pointerVelocity = (float)(App.Prefs.MouseAcceleration / 10 * Math.Pow(mouseVelocity, 2) +
0.4 * mouseVelocity);
pointerVelocity = (float)Math.Max(0.4, Math.Min(pointerVelocity, 1.6)); // Clamp
if (App.Prefs.MouseAcceleration == 0) pointerVelocity = 1; // Disable acceleration
// Apply acceleration
dist2d.Multiply(pointerVelocity);

MouseOperations.ShiftCursorPosition(dist2d.x, dist2d.y);

}


_lastDragMousePoint = MouseOperations.GetCursorPosition();
_dragEndTimer.Stop();
_dragEndTimer.Interval = GetReleaseDelay();
_dragEndTimer.Start();
}
_lastThreeFingersContact = Ctms();
_lastPoints = points;
_lastOneFingerPoint = MousePoint.Empty;

}else{
_numberOfThreeFingersInputs = 0;

if(!_isDragging) return;
MousePoint point = new MousePoint(contacts[0].X, contacts[0].Y);
_lastThreeFingersPoints = points;
}
else{
_isFirstThreeFingersInput = true;
if (!_isDragging){
_isFirstOneFingerInput = true;
return;
}

if(_isFirstOneFingerInput || points.Length != _firstOneFingerPoints.Length || Ctms() - _lastOneFingerContact > 50){
_isFirstOneFingerInput = false;
_firstOneFingerContact = Ctms();
_firstOneFingerPoints = points;
}

if(!App.Prefs.AllowReleaseAndRestart) stopDrag();
// When releasing the fingers, one finger or two can be detected during some milliseconds.
else if(Ctms() - _lastThreeFingersContact > 50){
// Using a timer in case only one finger is detected when re-grabing the element
if(_oneFingerTimer.Enabled && _lastOneFingerPoint != MousePoint.Empty
&& point.DistTo(_lastOneFingerPoint)/MouseSpeedFactor() is < 50 and > 5){
stopDrag();
}else _oneFingerTimer.Enabled = true;
if (!App.Prefs.AllowReleaseAndRestart) stopDrag();
else if (_firstOneFingerPoints.GetLongestDist(points) > 30){
// When RELEASING and then REPLACING the fingers, one finger or two can be detected and send some events.
stopDrag();
}
else{
MouseOperations.SetCursorPosition(_lastDragMousePoint.x, _lastDragMousePoint.y);
}
_lastPoints = ThreeFingersPoints.Empty;
_lastOneFingerPoint = point;

_lastOneFingerContact = Ctms();
_lastThreeFingersPoints = ThreeFingersPoints.Empty;
}
}

private void CheckDragEnd(){
// minus 15 to avoid bugs when the timer ends before the time elapsed
if(_isDragging && Ctms() - _lastThreeFingersContact >= GetReleaseDelay()-15){
if (_isDragging && Ctms() - _lastThreeFingersContact >= GetReleaseDelay() - 15){
stopDrag();
}
}
Expand All @@ -102,7 +124,7 @@ private void stopDrag(){
_isDragging = false;
MouseOperations.Click(false);
}

private int GetReleaseDelay(){
return App.Prefs.AllowReleaseAndRestart ? Math.Max(App.Prefs.ReleaseDelay, 50) : 50;
}
Expand Down
10 changes: 9 additions & 1 deletion src/utils/MouseOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,18 @@ public static void ShiftCursorPosition(float x, float y){
var intY = (int) (y + _decimalY);
_decimalX = (x + _decimalX) - intX;
_decimalY = (y + _decimalY) - intY;
var point = GetCursorPosition();

Move(intX, intY);
}
public static void SetCursorPosition(float x, float y){
var intX = (int) (x + _decimalX);
var intY = (int) (y + _decimalY);
_decimalX = (x + _decimalX) - intX;
_decimalY = (y + _decimalY) - intY;
var point = GetCursorPosition();

Move(intX - point.x, intY - point.y);
}

public static void Move(int dx, int dy) {
MouseEvent(MouseOperations.MouseEventFlags.Move, dx, dy);
Expand Down
21 changes: 19 additions & 2 deletions src/utils/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public MousePoint(float x, float y){
this.x = x;
this.y = y;
}
public MousePoint(IntMousePoint point){
this.x = point.x;
this.y = point.y;
}

public void Multiply(float multiplicator){
x = x * multiplicator;
Expand All @@ -44,6 +48,9 @@ public float Length(){
public float DistTo(MousePoint point){
return (float) Math.Sqrt(Math.Pow(x - point.x, 2) + Math.Pow(y - point.y, 2));
}
public float DistTo(IntMousePoint point){
return (float) Math.Sqrt(Math.Pow(x - point.x, 2) + Math.Pow(y - point.y, 2));
}

public static MousePoint Empty = new(0, 0);

Expand All @@ -66,13 +73,15 @@ public IntMousePoint(int x, int y){
}

public struct ThreeFingersPoints {
public int Length;
public float x1;
public float y1;
public float x2;
public float y2;
public float x3;
public float y3;
public ThreeFingersPoints(){
this.Length = 0;
this.x1 = 0;
this.y1 = 0;
this.x2 = 0;
Expand All @@ -87,6 +96,13 @@ public ThreeFingersPoints(float x1, float y1, float x2, float y2, float x3, floa
this.y2 = y2;
this.x3 = x3;
this.y3 = y3;
if (x1 == 0 && y1 == 0 && x2 == 0 && y2 == 0 && x3 == 0 && y3 == 0){
this.Length = 0;
}else if (x2 == 0 && y2 == 0 && x3 == 0 && y3 == 0){
this.Length = 1;
}else if (x3 == 0 && y3 == 0){
this.Length = 2;
}else this.Length = 3;
}
public ThreeFingersPoints(TouchpadContact[] contacts){
if(contacts.Length >= 1){
Expand All @@ -110,9 +126,10 @@ public ThreeFingersPoints(TouchpadContact[] contacts){
this.x3 = contacts[0].X;
this.y3 = contacts[0].Y;
}
this.Length = contacts.Length;
}
public static ThreeFingersPoints Empty = new (0, 0, 0, 0, 0, 0);
public static ThreeFingersPoints Empty = new();

public static bool operator ==(ThreeFingersPoints a, ThreeFingersPoints b){
return a.x1 == b.x1 && a.y1 == b.y1 && a.x2 == b.x2 && a.y2 == b.y2 && a.x3 == b.x3 && a.y3 == b.y3;
}
Expand Down

0 comments on commit b6c158d

Please sign in to comment.