From a6a507656f6f9131efaeffcd96ab874d51809763 Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Feb 24 2018 16:27:58 +0000 Subject: assistance: key history --- diff --git a/mono/Assistance/InputState.cs b/mono/Assistance/InputState.cs new file mode 100644 index 0000000..81560cd --- /dev/null +++ b/mono/Assistance/InputState.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; + +namespace Assistance { + public class InputState { + public long ticks; + public KeyHistory keyHistory = new KeyHistory(); + public readonly Dictionary> buttonHistories = new Dictionary>(); + + public void touch(long ticks) { + if (this.ticks < ticks) + this.ticks = ticks; + else + ++this.ticks; + } + + public KeyState keyState + { get { return keyHistory.current; } } + + public void keyEvent(bool press, Gdk.Key key, long ticks) { + touch(ticks); + keyHistory.change(press, key, this.ticks); + } + public void keyPress(Gdk.Key key, long ticks) + { keyEvent(true, key, ticks); } + public void keyRelease(Gdk.Key key, long ticks) + { keyEvent(false, key, ticks); } + + public KeyState keyFind(Gdk.Key key) + { return keyState.find(key); } + public bool isKeyPressed(Gdk.Key key) + { return keyFind(key) != null; } + public double howLongKeyPressed(Gdk.Key key, long ticks, double timeOffset = 0.0) + { return KeyState.Holder.howLongPressed(keyFind(key), ticks, timeOffset); } + public double howLongKeyPressed(Gdk.Key key) + { return howLongKeyPressed(key, ticks); } + + public KeyHistory buttonHistory(Gdk.Device device) { + KeyHistory history; + if (!buttonHistories.TryGetValue(device, out history)) + history = new KeyHistory(); + buttonHistories[device] = history; + return history; + } + public KeyState buttonState(Gdk.Device device) + { buttonHistory(device).current; } + + public void buttonEvent(bool press, Gdk.Device device, uint button, long ticks) { + touch(ticks); + buttonHistory(device).change(press, button, this.ticks); + } + public void buttonPress(Gdk.Device device, uint button, long ticks) + { buttonEvent(true, device, button, ticks); } + public void buttonRelease(Gdk.Device device, uint button, long ticks) + { buttonEvent(false, device, button, ticks); } + public void buttonEvent(bool press, uint button, long ticks) + { buttonEvent(press, null, button, ticks); } + public void buttonPress(uint button, long ticks) + { buttonEvent(true, button, ticks); } + public void buttonRelease(uint button, long ticks) + { buttonEvent(false, button, ticks); } + + public KeyState buttonFind(Gdk.Device device, uint button) + { return buttonState(device).find(button); } + public bool isButtonPressed(Gdk.Device device, uint button) + { return buttonFind(device, button) != null; } + public double howLongButtonPressed(Gdk.Device device, uint button, long ticks, double timeOffset = 0.0) + { return KeyState.Holder.howLongPressed(buttonFind(device, button), ticks, timeOffset); } + public double howLongButtonPressed(Gdk.Device device, uint button) + { return howLongButtonPressed(device, ticks); } + + public KeyState buttonFindDefault(uint button) + { return buttonFind(null, button); } + public bool isButtonPressedDefault(uint button) + { return isButtonPressed(null, button); } + public double howLongButtonPressedDefault(uint button, long ticks, double timeOffset = 0.0) + { return howLongButtonPressed(null, button, ticks, timeOffset); } + public double howLongButtonPressedDefault(uint button) + { return howLongButtonPressedDefault(button, ticks); } + + public KeyState buttonFindAny(uint button, out Gdk.Device device) { + device = null; + KeyState state = null; + foreach(KeyValuePair> pair in buttonHistories) { + KeyState s = pair.Value == null ? null : pair.Value.current.find(button); + if (s != null && (state == null || s.ticks < state.ticks)) + { state = s; device = pair.Key; } + } + return state; + } + public KeyState buttonFindAny(uint button) + { Gdk.Device device; return buttonFindAny(button, out device); } + public bool isButtonPressedAny(uint button) + { return buttonFindAny(button) != null; } + public double howLongButtonPressedAny(uint button, long ticks, double timeOffset = 0.0) + { return KeyState.Holder.howLongPressed(buttonFindAny(button), ticks, timeOffset); } + public double howLongButtonPressedAny(uint button) + { return howLongButtonPressedAny(button, ticks); } + + public KeyState.Holder keyStateHolder(long ticks, double timeOffset = 0.0) + { return new KeyState.Holder(keyState, ticks, timeOffset); } + public KeyState.Holder keyStateHolder() + { return keyStateHolder(ticks); } + public KeyHistory.Holder keyStateHolder(long ticks, double timeOffset = 0.0) + { return new KeyHistory.Holder(keyHistory, ticks, timeOffset); } + public KeyHistory.Holder keyStateHolder() + { return keyStateHolder(ticks); } + + public KeyState.Holder buttonStateHolder(Gdk.Device device, long ticks, double timeOffset = 0.0) + { return new KeyState.Holder(buttonState(device), ticks, timeOffset); } + public KeyState.Holder buttonStateHolder(Gdk.Device device) + { return buttonStateHolder(device, ticks); } + public KeyHistory.Holder buttonHistoryHolder(Gdk.Device device, long ticks, double timeOffset = 0.0) + { return new KeyHistory.Holder(buttonHistory(device), ticks, timeOffset); } + public KeyHistory.Holder buttonHistoryHolder(Gdk.Device device) + { return buttonHistoryHolder(buttonHistory(device), ticks); } + } +} + diff --git a/mono/Assistance/KeyHistory.cs b/mono/Assistance/KeyHistory.cs new file mode 100644 index 0000000..ea3fdaf --- /dev/null +++ b/mono/Assistance/KeyHistory.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; + +namespace Assistance { + public class KeyHistory where T: IComparable, new() { + public class Holder: IDisposable { + public readonly KeyHistory history; + public readonly long ticks; + public readonly double timeOffset; + + private readonly long heldTicks; + private bool disposed = false; + + public Holder(KeyHistory history, long ticks, double timeOffset = 0.0) { + this.history = history; + this.ticks = ticks; + this.timeOffset = timeOffset; + heldTicks = history.hold(ticks); + } + + public KeyState.Holder get(double time) { + long dticks = (long)Math.Ceiling(Timer.frequency*(time + timeOffset)); + KeyState state = history.get(ticks + dticks); + return new KeyState.Holder(state, ticks, timeOffset + time); + } + + public void Dispose() { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) { + if (disposed) return; + history.release(heldTicks); + disposed = true; + } + + ~Holder() + { Dispose(false); } + } + + private readonly List> states = new List>() { new KeyState() }; + private readonly List locks = new List(); + + public KeyState current + { get { return states[ states.Count - 1 ]; } } + + public void change(bool press, T value, long ticks) { + states.Add(current.change(press, value, ticks)); + autoRemove(); + } + public KeyState press(T value, long ticks) + { return change(true, value, ticks); } + public KeyState release(T value, long ticks) + { return change(false, value, ticks); } + + private int findLock(long ticks) { + // locks[a] <= ticks < locks[b] + int a = 0; + int b = states.Count - 1; + if (locks[a] < locks) return -1; + if (ticks >= locks[b]) return b; + while(True) { + int c = (a + b)/2; + if (a == c) break; + if (ticks < locks[c]) b = c; else a = c; + } + return a; + } + + private void autoRemove() { + long ticks = locks.Count > 0 ? locks[0] : long.MaxValue; + while(states.Count > 1 && (states[0].ticks < ticks || states[0].isEmpty)) + states.RemoveAt(0); + } + + private long hold(long ticks) { + long heldTicks = Math.Max(ticks, current.ticks); + locks.Insert(findLock(heldTicks) + 1, heldTicks); + return heldTicks; + } + + private void release(long heldTicks) { + int i = findLock(heldTicks); + if (i >= 0 && locks[i] == heldTicks) locks.RemoveAt(i); + autoRemove(); + } + + private KeyState get(long ticks) { + // state[a].ticks <= ticks < state[b].ticks + int a = 0; + int b = states.Count - 1; + if (states[a].ticks < ticks) return new KeyState(); + if (ticks >= states[b].ticks) return states[b].ticks; + while(True) { + int c = (a + b)/2; + if (a == c) break; + if (ticks < states[c].ticks) b = c; else a = c; + } + return states[a]; + } + } +} + diff --git a/mono/Assistance/KeyState.cs b/mono/Assistance/KeyState.cs index 7b46c9f..ac40ae7 100644 --- a/mono/Assistance/KeyState.cs +++ b/mono/Assistance/KeyState.cs @@ -27,7 +27,7 @@ namespace Assistance { : Math.Max(Timer.step, (ticks - state.ticks)*Timer.step + timeOffset); } } - + public static readonly T none = new T(); public static readonly KeyState empty = new KeyState();