diff options
author | Caian Benedicto <caianbene@gmail.com> | 2021-10-12 16:54:21 -0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-10-12 21:54:21 +0200 |
commit | 380b95bc59e7dc419f89df951cdc086e792cb0ff (patch) | |
tree | 59a636b48db991d8e13132d7d3f41464d9b04b24 /Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs | |
parent | 69093cf2d69490862aff974f170cee63a0016fd0 (diff) |
Inline software keyboard without input pop up dialog (#2180)
* Initial implementation
* Refactor dynamic text input keys out to facilitate configuration via UI
* Fix code styling
* Add per applet indirect layer handles
* Remove static functions from SoftwareKeyboardRenderer
* Remove inline keyboard reset delay
* Remove inline keyboard V2 responses
* Add inline keyboard soft-lock recovering
* Add comments
* Forward accept and cancel key names to the keyboard and add soft-lock prevention line
* Add dummy window to handle paste events
* Rework inline keyboard state machine and graphics
* Implement IHostUiHandler interfaces on headless WindowBase class
* Add inline keyboard assets
* Fix coding style
* Fix coding style
* Change mode cycling shortcut to F6
* Fix invalid calc size error in games using extended calc
* Remove unnecessary namespaces
Diffstat (limited to 'Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs')
-rw-r--r-- | Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs new file mode 100644 index 00000000..8884bdcf --- /dev/null +++ b/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/TimedAction.cs @@ -0,0 +1,172 @@ +using System; +using System.Threading; + +namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard +{ + /// <summary> + /// A threaded executor of periodic actions that can be cancelled. The total execution time is optional + /// and, in this case, a progress is reported back to the action. + /// </summary> + class TimedAction + { + public const int MaxThreadSleep = 100; + + private class SleepSubstepData + { + public readonly int SleepMilliseconds; + public readonly int SleepCount; + public readonly int SleepRemainderMilliseconds; + + public SleepSubstepData(int sleepMilliseconds) + { + SleepMilliseconds = Math.Min(sleepMilliseconds, MaxThreadSleep); + SleepCount = sleepMilliseconds / SleepMilliseconds; + SleepRemainderMilliseconds = sleepMilliseconds - SleepCount * SleepMilliseconds; + } + } + + private TRef<bool> _cancelled = null; + private Thread _thread = null; + private object _lock = new object(); + + public bool IsRunning + { + get + { + lock (_lock) + { + if (_thread == null) + { + return false; + } + + return _thread.IsAlive; + } + } + } + + public void RequestCancel() + { + lock (_lock) + { + if (_cancelled != null) + { + Volatile.Write(ref _cancelled.Value, true); + } + } + } + + public TimedAction() { } + + private void Reset(Thread thread, TRef<bool> cancelled) + { + lock (_lock) + { + // Cancel the current task. + if (_cancelled != null) + { + Volatile.Write(ref _cancelled.Value, true); + } + + _cancelled = cancelled; + + _thread = thread; + _thread.IsBackground = true; + _thread.Start(); + } + } + + public void Reset(Action<float> action, int totalMilliseconds, int sleepMilliseconds) + { + // Create a dedicated cancel token for each task. + var cancelled = new TRef<bool>(false); + + Reset(new Thread(() => + { + var substepData = new SleepSubstepData(sleepMilliseconds); + + int totalCount = totalMilliseconds / sleepMilliseconds; + int totalRemainder = totalMilliseconds - totalCount * sleepMilliseconds; + + if (Volatile.Read(ref cancelled.Value)) + { + action(-1); + + return; + } + + action(0); + + for (int i = 1; i <= totalCount; i++) + { + if (SleepWithSubstep(substepData, cancelled)) + { + action(-1); + + return; + } + + action((float)(i * sleepMilliseconds) / totalMilliseconds); + } + + if (totalRemainder > 0) + { + if (SleepWithSubstep(substepData, cancelled)) + { + action(-1); + + return; + } + + action(1); + } + }), cancelled); + } + + public void Reset(Action action, int sleepMilliseconds) + { + // Create a dedicated cancel token for each task. + var cancelled = new TRef<bool>(false); + + Reset(new Thread(() => + { + var substepData = new SleepSubstepData(sleepMilliseconds); + + while (!Volatile.Read(ref cancelled.Value)) + { + action(); + + if (SleepWithSubstep(substepData, cancelled)) + { + return; + } + } + }), cancelled); + } + + private static bool SleepWithSubstep(SleepSubstepData substepData, TRef<bool> cancelled) + { + for (int i = 0; i < substepData.SleepCount; i++) + { + if (Volatile.Read(ref cancelled.Value)) + { + return true; + } + + Thread.Sleep(substepData.SleepMilliseconds); + } + + if (substepData.SleepRemainderMilliseconds > 0) + { + if (Volatile.Read(ref cancelled.Value)) + { + return true; + } + + Thread.Sleep(substepData.SleepRemainderMilliseconds); + } + + return Volatile.Read(ref cancelled.Value); + } + } +} |