LenovoLegionToolkit
108 строк · 3.4 Кб
1using System;
2using System.Runtime.InteropServices;
3using System.Threading.Tasks;
4using LenovoLegionToolkit.Lib.Extensions;
5using LenovoLegionToolkit.Lib.Utils;
6using Microsoft.Win32.SafeHandles;
7
8namespace LenovoLegionToolkit.Lib.Features;
9
10public abstract class AbstractDriverFeature<T> : IFeature<T> where T : struct, Enum, IComparable
11{
12protected readonly uint ControlCode;
13protected readonly Func<SafeFileHandle> DriverHandle;
14
15protected T LastState;
16
17protected AbstractDriverFeature(Func<SafeFileHandle> driverHandleHandle, uint controlCode)
18{
19DriverHandle = driverHandleHandle;
20ControlCode = controlCode;
21}
22
23public virtual async Task<bool> IsSupportedAsync()
24{
25try
26{
27_ = await GetStateAsync().ConfigureAwait(false);
28return true;
29}
30catch
31{
32return false;
33}
34}
35
36public Task<T[]> GetAllStatesAsync() => Task.FromResult(Enum.GetValues<T>());
37
38public virtual async Task<T> GetStateAsync()
39{
40if (Log.Instance.IsTraceEnabled)
41Log.Instance.Trace($"Getting state... [feature={GetType().Name}]");
42
43var outBuffer = await SendCodeAsync(DriverHandle(), ControlCode, GetInBufferValue()).ConfigureAwait(false);
44
45if (Log.Instance.IsTraceEnabled)
46Log.Instance.Trace($"Buffer value: {outBuffer} [feature={GetType().Name}]");
47
48var state = await FromInternalAsync(outBuffer).ConfigureAwait(false);
49LastState = state;
50
51if (Log.Instance.IsTraceEnabled)
52Log.Instance.Trace($"State is {state} [feature={GetType().Name}]");
53
54return state;
55}
56
57public virtual async Task SetStateAsync(T state)
58{
59if (Log.Instance.IsTraceEnabled)
60Log.Instance.Trace($"Setting state to {state}... [feature={GetType().Name}]");
61
62var codes = await ToInternalAsync(state).ConfigureAwait(false);
63foreach (var code in codes)
64await SendCodeAsync(DriverHandle(), ControlCode, code).ConfigureAwait(false);
65LastState = state;
66
67await VerifyStateSetAsync(state).ConfigureAwait(false);
68
69if (Log.Instance.IsTraceEnabled)
70Log.Instance.Trace($"State set to {state} [feature={GetType().Name}]");
71}
72
73protected abstract Task<T> FromInternalAsync(uint state);
74
75protected abstract uint GetInBufferValue();
76
77protected abstract Task<uint[]> ToInternalAsync(T state);
78
79protected Task<uint> SendCodeAsync(SafeFileHandle handle, uint controlCode, uint inBuffer) => Task.Run(() =>
80{
81if (PInvokeExtensions.DeviceIoControl(handle, controlCode, inBuffer, out uint outBuffer))
82return outBuffer;
83
84var error = Marshal.GetLastWin32Error();
85
86if (Log.Instance.IsTraceEnabled)
87Log.Instance.Trace($"DeviceIoControl returned 0, last error: {error} [feature={GetType().Name}]");
88
89throw new InvalidOperationException($"DeviceIoControl returned 0, last error: {error}");
90});
91
92private async Task VerifyStateSetAsync(T state)
93{
94var retries = 0;
95while (retries < 10)
96{
97if (state.Equals(await GetStateAsync().ConfigureAwait(false)))
98break;
99
100retries++;
101
102await Task.Delay(50).ConfigureAwait(false);
103}
104
105if (Log.Instance.IsTraceEnabled)
106Log.Instance.Trace($"Verify state {state} set failed. [feature={GetType().Name}]");
107}
108}
109