diff --git a/ThreeFingersDragOnWindows.csproj b/ThreeFingersDragOnWindows.csproj index e298541..caeda6c 100755 --- a/ThreeFingersDragOnWindows.csproj +++ b/ThreeFingersDragOnWindows.csproj @@ -1,7 +1,7 @@  - Exe + WinExe net6.0-windows true diff --git a/src/touchpad/ContactsManager.cs b/src/touchpad/ContactsManager.cs index f8b9710..8dc0828 100644 --- a/src/touchpad/ContactsManager.cs +++ b/src/touchpad/ContactsManager.cs @@ -9,9 +9,10 @@ namespace ThreeFingersDragOnWindows.src.touchpad; public class ContactsManager where T : Window, IContactsManager { private readonly List _lastContacts = new(); private readonly T _source; - private HwndSource _targetSource; + private long _lastInput; + public ContactsManager(T source){ _source = source; } @@ -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 { diff --git a/src/touchpad/ThreeFingersDrag.cs b/src/touchpad/ThreeFingersDrag.cs index 97849fe..2200c03 100644 --- a/src/touchpad/ThreeFingersDrag.cs +++ b/src/touchpad/ThreeFingersDrag.cs @@ -1,5 +1,6 @@ using System; using System.Timers; +using Microsoft.VisualBasic.Devices; using ThreeFingersDragOnWindows.src.utils; namespace ThreeFingersDragOnWindows.src.touchpad; @@ -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(); } }; @@ -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(); } } @@ -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; } diff --git a/src/utils/MouseOperations.cs b/src/utils/MouseOperations.cs index cfdb7f2..eae231b 100644 --- a/src/utils/MouseOperations.cs +++ b/src/utils/MouseOperations.cs @@ -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); diff --git a/src/utils/Utils.cs b/src/utils/Utils.cs index 93c3525..c9b42c4 100644 --- a/src/utils/Utils.cs +++ b/src/utils/Utils.cs @@ -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; @@ -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); @@ -66,6 +73,7 @@ public IntMousePoint(int x, int y){ } public struct ThreeFingersPoints { + public int Length; public float x1; public float y1; public float x2; @@ -73,6 +81,7 @@ public struct ThreeFingersPoints { public float x3; public float y3; public ThreeFingersPoints(){ + this.Length = 0; this.x1 = 0; this.y1 = 0; this.x2 = 0; @@ -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){ @@ -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; }