This commit is contained in:
辉鸭蛋
2024-12-23 02:51:22 +08:00
parent ef0ced332c
commit da8fc4a89e
7 changed files with 482 additions and 20 deletions

View File

@@ -68,6 +68,7 @@
<PackageReference Include="Serilog.Sinks.RichTextBoxEx.Wpf" Version="1.1.0.1" />
<PackageReference Include="SharpAvi" Version="3.0.1" />
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
<PackageReference Include="System.Management" Version="9.0.0" />
<PackageReference Include="Vanara.PInvoke.NtDll" Version="4.0.2" />
<PackageReference Include="Vanara.PInvoke.SHCore" Version="4.0.2" />
<PackageReference Include="Vanara.PInvoke.User32" Version="4.0.2" />

View File

@@ -11,6 +11,7 @@ using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Windows.Forms;
using BetterGenshinImpact.Helpers;
using Vanara.PInvoke;
namespace BetterGenshinImpact.Core.Recorder;
@@ -22,8 +23,6 @@ public class KeyMouseRecorder
public List<MacroEvent> MouseMoveToMacroEvents { get; } = [];
public List<MacroEvent> MouseMoveByMacroEvents { get; } = [];
public DateTime StartTime { get; set; } = DateTime.UtcNow;
public uint StartTick { get; set; } = Kernel32.GetTickCount();
public DateTime LastOrientationDetection { get; set; } = DateTime.UtcNow;
@@ -44,6 +43,10 @@ public class KeyMouseRecorder
{
// MacroEvents 需要以实际时间进行排序
MacroEvents.Sort((a, b) => a.Time.CompareTo(b.Time));
// 删除为负数的时间
MacroEvents.RemoveAll(m => m.Time < 0);
var startTime = EnvironmentUtil.LastBootUpTime()! + TimeSpan.FromMilliseconds(StartTick);
var rect = TaskContext.Instance().SystemInfo.CaptureAreaRect;
KeyMouseScript keyMouseScript = new()
@@ -58,8 +61,8 @@ public class KeyMouseRecorder
Width = rect.Width,
Height = rect.Height,
RecordDpi = TaskContext.Instance().DpiScale,
StartTime = $"{StartTime:yyyy-MM-dd HH:mm:ss:ffff}",
StartTimeUnixTimestamp = (StartTime - new DateTime(1970, 1, 1)).TotalNanoseconds.ToString("F0")
StartTime = $"{startTime:yyyy-MM-dd HH:mm:ss:ffff}",
StartTimeUnixTimestamp = (startTime - new DateTime(1970, 1, 1)).Value.TotalNanoseconds.ToString("F0")
}
};
return JsonSerializer.Serialize(keyMouseScript, JsonOptions);
@@ -143,23 +146,12 @@ public class KeyMouseRecorder
public void MouseMoveBy(MouseState state, uint tick, bool save = false)
{
uint prevTickCount = 0;
if (MouseMoveByMacroEvents.Count > 0)
{
prevTickCount = MouseMoveByMacroEvents[^1].TickCount - StartTick;
}
else
{
prevTickCount = tick - 5; // 减去间隔时间5ms
}
var mEvent = new MacroEvent
{
Type = MacroEventType.MouseMoveBy,
MouseX = state.X,
MouseY = state.Y,
Time = prevTickCount,
TickCount = tick
Time = tick - 5,
};
MouseMoveByMacroEvents.Add(mEvent);
if (save)

View File

@@ -0,0 +1,191 @@
using BetterGenshinImpact.Core.Recorder.Model;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.Common;
using BetterGenshinImpact.GameTask.Common.Map;
using Gma.System.MouseKeyHook;
using SharpDX.DirectInput;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Channels;
using System.Threading.Tasks;
using System.Windows.Forms;
using BetterGenshinImpact.Helpers;
using Vanara.PInvoke;
namespace BetterGenshinImpact.Core.Recorder;
public class KeyMouseRecorderJsonLine
{
public KeyMouseScriptInfo Info { get; set; }
private readonly Channel<MacroEvent> _macroEventsChannel = Channel.CreateUnbounded<MacroEvent>();
private readonly Channel<MacroEvent> _mouseMoveToMacroEventsChannel = Channel.CreateUnbounded<MacroEvent>();
private readonly Channel<MacroEvent> _mouseMoveByMacroEventsChannel = Channel.CreateUnbounded<MacroEvent>();
private readonly Task _consumerTask;
public uint StartTick { get; set; } = Kernel32.GetTickCount();
public static readonly JsonSerializerOptions JsonOptions = new()
{
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
AllowTrailingCommas = true,
ReadCommentHandling = JsonCommentHandling.Skip,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
public KeyMouseRecorderJsonLine(string path)
{
var bootTime = EnvironmentUtil.LastBootUpTime();
if (bootTime != null)
{
throw new Exception("无法获取系统启动时间");
}
var startTime = bootTime! + TimeSpan.FromMilliseconds(StartTick);
var rect = TaskContext.Instance().SystemInfo.CaptureAreaRect;
Info = new KeyMouseScriptInfo
{
X = rect.X,
Y = rect.Y,
Width = rect.Width,
Height = rect.Height,
RecordDpi = TaskContext.Instance().DpiScale,
StartTime = $"{startTime:yyyy-MM-dd HH:mm:ss:ffff}",
StartTimeUnixTimestamp = (startTime - new DateTime(1970, 1, 1)).Value.TotalNanoseconds.ToString("F0")
};
var infoJson = JsonSerializer.Serialize(Info, JsonOptions);
File.WriteAllText(Path.Combine(path, "info.json"), infoJson);
_consumerTask = Task.Run(async () => await ConsumeEventsAsync(path));
}
private async Task ConsumeEventsAsync(string path)
{
var tasks = new List<Task>
{
ConsumeChannelAsync(_macroEventsChannel, Path.Combine(path, "macroEvents.jsonl")),
ConsumeChannelAsync(_mouseMoveToMacroEventsChannel, Path.Combine(path, "mouseMoveToMacroEvents.jsonl")),
ConsumeChannelAsync(_mouseMoveByMacroEventsChannel, Path.Combine(path, "mouseMoveByMacroEvents.jsonl"))
};
await Task.WhenAll(tasks);
}
private async Task ConsumeChannelAsync(Channel<MacroEvent> channel, string filePath)
{
await using var stream = new FileStream(filePath, FileMode.Append, FileAccess.Write, FileShare.None);
await using var writer = new StreamWriter(stream);
await foreach (var macroEvent in channel.Reader.ReadAllAsync())
{
var json = JsonSerializer.Serialize(macroEvent, JsonOptions);
await writer.WriteLineAsync(json);
await writer.FlushAsync();
}
}
private void AddEvent(Channel<MacroEvent> channel, MacroEvent macroEvent)
{
channel.Writer.TryWrite(macroEvent);
}
public void KeyDown(KeyEventArgsExt e)
{
var time = e.Timestamp - StartTick;
AddEvent(_macroEventsChannel, new MacroEvent
{
Type = MacroEventType.KeyDown,
KeyCode = e.KeyValue,
Time = time
});
}
public void KeyUp(KeyEventArgsExt e)
{
var time = e.Timestamp - StartTick;
AddEvent(_macroEventsChannel, new MacroEvent
{
Type = MacroEventType.KeyUp,
KeyCode = e.KeyValue,
Time = time
});
}
public void MouseDown(MouseEventExtArgs e)
{
var time = e.Timestamp - StartTick;
AddEvent(_macroEventsChannel, new MacroEvent
{
Type = MacroEventType.MouseDown,
MouseX = e.X,
MouseY = e.Y,
MouseButton = e.Button.ToString(),
Time = time
});
}
public void MouseUp(MouseEventExtArgs e)
{
var time = e.Timestamp - StartTick;
AddEvent(_macroEventsChannel, new MacroEvent
{
Type = MacroEventType.MouseUp,
MouseX = e.X,
MouseY = e.Y,
MouseButton = e.Button.ToString(),
Time = time
});
}
public void MouseMoveTo(MouseEventExtArgs e, bool save = false)
{
var time = e.Timestamp - StartTick;
var mEvent = new MacroEvent
{
Type = MacroEventType.MouseMoveTo,
MouseX = e.X,
MouseY = e.Y,
Time = time
};
AddEvent(_mouseMoveToMacroEventsChannel, mEvent);
if (save)
{
AddEvent(_macroEventsChannel, mEvent);
}
}
public void MouseWheel(MouseEventExtArgs e)
{
var time = e.Timestamp - StartTick;
AddEvent(_macroEventsChannel, new MacroEvent
{
Type = MacroEventType.MouseWheel,
MouseY = e.Delta, // 120 的倍率
Time = time
});
}
public void MouseMoveBy(MouseState state, uint tick, bool save = false)
{
var mEvent = new MacroEvent
{
Type = MacroEventType.MouseMoveBy,
MouseX = state.X,
MouseY = state.Y,
Time = tick - 5
};
AddEvent(_mouseMoveByMacroEventsChannel, mEvent);
if (save)
{
AddEvent(_macroEventsChannel, mEvent);
}
}
}

View File

@@ -12,10 +12,6 @@ public class MacroEvent
public int MouseY { get; set; }
public string? MouseButton { get; set; }
public double Time { get; set; }
[JsonIgnore]
public uint TickCount { get; set; }
public int? CameraOrientation { get; set; }
}

View File

@@ -0,0 +1,83 @@
namespace BetterGenshinImpact.Helpers.Device;
using System;
using System.Runtime.InteropServices;
public static class TouchpadManager
{
// 定义 DEVMODE 结构
[StructLayout(LayoutKind.Sequential)]
private struct DEVMODE
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmDeviceName;
public ushort dmSpecVersion;
public ushort dmDriverVersion;
public ushort dmSize;
public ushort dmDriverExtra;
public uint dmFields;
public int dmPositionX;
public int dmPositionY;
public uint dmDisplayOrientation;
public uint dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string dmFormName;
public ushort dmLogPixels;
public uint dmBitsPerPel;
public uint dmPelsWidth;
public uint dmPelsHeight;
public uint dmDisplayFlags;
public uint dmDisplayFrequency;
public uint dmICMMethod;
public uint dmICMIntent;
public uint dmMediaType;
public uint dmDitherType;
public uint dmReserved1;
public uint dmReserved2;
public uint dmPanningWidth;
public uint dmPanningHeight;
}
// 导入 ChangeDisplaySettingsEx 函数
[DllImport("user32.dll", CharSet = CharSet.Ansi)]
private static extern int ChangeDisplaySettingsEx(string lpszDeviceName, ref DEVMODE lpDevMode, IntPtr hwnd, uint dwflags, IntPtr lParam);
private const int DMDO_DEFAULT = 0;
private const int DMDO_90 = 1;
private const int DMDO_180 = 2;
private const int DMDO_270 = 3;
private const int CDS_UPDATEREGISTRY = 0x01;
private const int CDS_TEST = 0x02;
private const int CDS_FULLSCREEN = 0x04;
private const int CDS_GLOBAL = 0x08;
private const int CDS_SET_PRIMARY = 0x10;
private const int CDS_VIDEOPARAMETERS = 0x20;
private const int CDS_ENABLE_UNSAFE_MODES = 0x100;
private const int CDS_DISABLE_UNSAFE_MODES = 0x200;
private const int CDS_RESET = 0x40000000;
private const int CDS_NORESET = 0x10000000;
public static void DisableTouchpad()
{
DEVMODE devMode = new DEVMODE();
devMode.dmSize = (ushort)Marshal.SizeOf(typeof(DEVMODE));
devMode.dmFields = 0x00080000; // DM_DISPLAYORIENTATION
devMode.dmDisplayOrientation = DMDO_180; // 旋转显示方向
int result = ChangeDisplaySettingsEx(null, ref devMode, IntPtr.Zero, CDS_UPDATEREGISTRY, IntPtr.Zero);
if (result == 0)
{
Console.WriteLine("触控板已禁用。");
}
else
{
Console.WriteLine("无法禁用触控板,错误代码: " + result);
}
}
}

View File

@@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using NAudio.CoreAudioApi;
using Vanara.PInvoke;
namespace BetterGenshinImpact.Helpers;
public class EnvironmentUtil
{
public static bool IsProcessRunning(string processName)
{
// // 检测 QQ
// bool isQQRunning = IsProcessRunning("QQ"); // QQ的进程名称可能需要确认
// Console.WriteLine("QQ is " + (isQQRunning ? "running" : "not running"));
//
// // 检测微信
// bool isWeChatRunning = IsProcessRunning("WeChat"); // WeChat的进程名称可能需要确认
// Console.WriteLine("WeChat is " + (isWeChatRunning ? "running" : "not running"));
//
// // 检测飞书
// bool isFeiShuRunning = IsProcessRunning("FeiShu"); // FeiShu的进程名称可能需要确认
// Console.WriteLine("FeiShu is " + (isFeiShuRunning ? "running" : "not running"));
// 获取所有运行中的进程
Process[] processes = Process.GetProcessesByName(processName);
return processes.Length > 0;
}
public static DateTime? LastBootUpTime()
{
// 创建管理对象查询
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT LastBootUpTime FROM Win32_OperatingSystem");
foreach (ManagementObject queryObj in searcher.Get())
{
// 获取系统开机时间
DateTime bootTime = ManagementDateTimeConverter.ToDateTime(queryObj["LastBootUpTime"].ToString());
Debug.WriteLine("系统开机时间: " + bootTime);
return bootTime;
}
return null;
}
public static uint GetMouseSpeed()
{
User32.SystemParametersInfo(User32.SPI.SPI_GETMOUSESPEED, out uint mouseSpeed);
return mouseSpeed;
}
// 设置鼠标速度
public static void SetMouseSpeed(uint speed)
{
User32.SystemParametersInfo(User32.SPI.SPI_SETMOUSESPEED, speed);
}
//SPI_GETWHEELSCROLLLINES
public static uint GetWheelScrollLines()
{
User32.SystemParametersInfo(User32.SPI.SPI_GETWHEELSCROLLLINES, out uint wheelScrollLines);
return wheelScrollLines;
}
// 设置鼠标滚轮滚动行数
public static void SetWheelScrollLines(uint lines)
{
User32.SystemParametersInfo(User32.SPI.SPI_SETWHEELSCROLLLINES, lines);
}
// SPI_GETMOUSETRAILS
public static uint GetMouseTrails()
{
User32.SystemParametersInfo(User32.SPI.SPI_GETMOUSETRAILS, out uint mouseTrails);
return mouseTrails;
}
// 设置鼠标拖尾
public static void SetMouseTrails(uint trails)
{
User32.SystemParametersInfo(User32.SPI.SPI_SETMOUSETRAILS, trails);
}
// SPI_GETMOUSE
/// <summary>
/// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-mouse_event#remarks
/// </summary>
/// <returns></returns>
public static (int, int, int) GetMouse()
{
int[] mouseParams = new int[3];
IntPtr ptr = Marshal.AllocHGlobal(sizeof(int) * mouseParams.Length);
try
{
User32.SystemParametersInfo(User32.SPI.SPI_GETMOUSE, (uint)mouseParams.Length, ptr, User32.SPIF.None);
Marshal.Copy(ptr, mouseParams, 0, mouseParams.Length);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
return (mouseParams[0], mouseParams[1], mouseParams[2]);
}
public static void PrintMouseSettings()
{
Debug.WriteLine("鼠标速度: " + GetMouseSpeed());
Debug.WriteLine("鼠标滚轮滚动行数: " + GetWheelScrollLines());
Debug.WriteLine("鼠标拖尾: " + GetMouseTrails());
var (mouseThreshold1, mouseThreshold2, mouseThreshold3) = GetMouse();
Debug.WriteLine("鼠标阈值1 Mouse Double Click Time (ms): " + mouseThreshold1);
Debug.WriteLine("鼠标阈值2 Mouse Buttons Swapped:: " + mouseThreshold2);
Debug.WriteLine("鼠标阈值3 Mouse Speed: " + mouseThreshold3);
// Debug.WriteLine("Touchpad Enabled: " + IsTouchpadEnabled());
Debug.WriteLine("当前音量: " + GetMasterVolume());
}
public static bool IsTouchpadEnabled()
{
try
{
// Create a ManagementObjectSearcher to query for touchpad devices
ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT * FROM Win32_PointingDevice");
foreach (ManagementObject obj in searcher.Get())
{
// Check if the device is a touchpad
if (obj["Description"].ToString().ToLower().Contains("touchpad"))
{
// Get the status of the touchpad
string status = obj["Status"].ToString();
Debug.WriteLine($"Touchpad Status: {status}");
// Check if the touchpad is enabled
if (status.Equals("OK", StringComparison.OrdinalIgnoreCase))
{
Debug.WriteLine("The touchpad is enabled.");
return true;
}
else
{
Debug.WriteLine("The touchpad is disabled.");
return false;
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"An error occurred: {ex.Message}");
}
return true;
}
public static List<(string, int)> GetCurrentSpeakerVolume()
{
int volume = 0;
var enumerator = new MMDeviceEnumerator();
//获取音频输出设备
IEnumerable<MMDevice> speakDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).ToArray();
List<(string, int)> devices = new();
foreach (var speakDevice in speakDevices)
{
Debug.WriteLine($"Device Friendly Name: {speakDevice.FriendlyName}");
Debug.WriteLine($"Device Volume: {speakDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100}");
volume = (int)(speakDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
devices.Add((speakDevice.FriendlyName, volume));
}
return devices;
}
public static float GetMasterVolume()
{
var enumerator = new MMDeviceEnumerator();
var device = enumerator.GetDefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
return device.AudioEndpointVolume.MasterVolumeLevelScalar;
}
public static void GetCurrentSpeakerVolume(int volume)
{
var enumerator = new MMDeviceEnumerator();
IEnumerable<MMDevice> speakDevices = enumerator.EnumerateAudioEndPoints(DataFlow.Render, DeviceState.Active).ToArray();
if (speakDevices.Count() > 0)
{
MMDevice mMDevice = speakDevices.ToList()[0];
mMDevice.AudioEndpointVolume.MasterVolumeLevelScalar = volume / 100.0f;
}
}
}

View File

@@ -118,6 +118,8 @@ public partial class MainWindowViewModel : ObservableObject, IViewModel
// 更新仓库
// ScriptRepoUpdater.Instance.AutoUpdate();
EnvironmentUtil.PrintMouseSettings();
}
private async Task GetNewestInfoAsync()