Files
better-genshin-impact/BetterGenshinImpact/View/MaskWindow.xaml.cs
2024-08-14 18:01:02 +08:00

343 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using BetterGenshinImpact.Core.Config;
using BetterGenshinImpact.Core.Recognition.OpenCv;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.Genshin.Settings;
using BetterGenshinImpact.Helpers;
using BetterGenshinImpact.Helpers.DpiAwareness;
using BetterGenshinImpact.View.Drawable;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.Extensions.Logging;
using Serilog.Sinks.RichTextBox.Abstraction;
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using Vanara.PInvoke;
using FontFamily = System.Windows.Media.FontFamily;
namespace BetterGenshinImpact.View;
/// <summary>
/// 一个用于覆盖在游戏窗口上的窗口,用于显示识别结果、显示日志、设置区域位置等
/// 请使用 Instance 方法获取单例
/// </summary>
public partial class MaskWindow : Window
{
private static MaskWindow? _maskWindow;
private static readonly Typeface _typeface;
private nint _hWnd;
private IRichTextBox? _richTextBox;
private readonly ILogger<MaskWindow> _logger = App.GetLogger<MaskWindow>();
static MaskWindow()
{
if (Application.Current.TryFindResource("TextThemeFontFamily") is FontFamily fontFamily)
{
_typeface = fontFamily.GetTypefaces().First();
}
else
{
_typeface = new FontFamily("Microsoft Yahei UI").GetTypefaces().First();
}
DefaultStyleKeyProperty.OverrideMetadata(typeof(MaskWindow), new FrameworkPropertyMetadata(typeof(MaskWindow)));
}
public static MaskWindow Instance()
{
if (_maskWindow == null)
{
throw new Exception("MaskWindow 未初始化");
}
return _maskWindow;
}
public bool IsExist()
{
return _maskWindow != null && PresentationSource.FromVisual(_maskWindow) != null;
}
public void RefreshPosition()
{
if (TaskContext.Instance().Config.MaskWindowConfig.UseSubform)
{
RefreshPositionForSubform();
}
else
{
RefreshPositionForNormal();
}
}
public void RefreshPositionForNormal()
{
var currentRect = SystemControl.GetCaptureRect(TaskContext.Instance().GameHandle);
Invoke(() =>
{
double dpiScale = DpiHelper.ScaleY;
Left = currentRect.Left / dpiScale;
Top = currentRect.Top / dpiScale;
Width = currentRect.Width / dpiScale;
Height = currentRect.Height / dpiScale;
Canvas.SetTop(LogTextBoxWrapper, Height - LogTextBoxWrapper.Height - 65);
Canvas.SetLeft(LogTextBoxWrapper, 20);
Canvas.SetTop(StatusWrapper, Height - LogTextBoxWrapper.Height - 90);
Canvas.SetLeft(StatusWrapper, 20);
});
// 重新计算控件位置
// shit code 预定了
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(this, "RefreshSettings", new object(), "重新计算控件位置"));
}
public void RefreshPositionForSubform()
{
nint targetHWnd = TaskContext.Instance().GameHandle;
_ = User32.GetClientRect(targetHWnd, out RECT targetRect);
float x = DpiHelper.GetScale(targetHWnd).X;
_ = User32.SetWindowPos(_hWnd, IntPtr.Zero, 0, 0, (int)(targetRect.Width * x), (int)(targetRect.Height * x), User32.SetWindowPosFlags.SWP_SHOWWINDOW);
Invoke(() =>
{
Canvas.SetTop(LogTextBoxWrapper, Height * 1d / DpiHelper.ScaleY - LogTextBoxWrapper.Height * 1d / DpiHelper.ScaleY - 65);
Canvas.SetLeft(LogTextBoxWrapper, 20);
Canvas.SetTop(StatusWrapper, Height * 1d / DpiHelper.ScaleY - LogTextBoxWrapper.Height * 1d / DpiHelper.ScaleY - 90);
Canvas.SetLeft(StatusWrapper, 20);
});
// 重新计算控件位置
// shit code 预定了
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(this, "RefreshSettings", new object(), "重新计算控件位置"));
}
public MaskWindow()
{
_maskWindow = this;
this.SetResourceReference(StyleProperty, typeof(MaskWindow));
InitializeComponent();
this.InitializeDpiAwareness();
LogTextBox.TextChanged += LogTextBoxTextChanged;
//AddAreaSettingsControl("测试识别窗口");
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
_richTextBox = App.GetService<IRichTextBox>();
if (_richTextBox != null)
{
_richTextBox.RichTextBox = LogTextBox;
}
if (TaskContext.Instance().Config.MaskWindowConfig.UseSubform)
{
_hWnd = new WindowInteropHelper(this).Handle;
nint targetHWnd = TaskContext.Instance().GameHandle;
if (User32.GetParent(_hWnd) != targetHWnd)
{
_ = User32.SetParent(_hWnd, targetHWnd);
}
}
RefreshPosition();
PrintSystemInfo();
}
private void PrintSystemInfo()
{
_logger.LogInformation("更好的原神 {Version}", Global.Version);
var systemInfo = TaskContext.Instance().SystemInfo;
var width = systemInfo.GameScreenSize.Width;
var height = systemInfo.GameScreenSize.Height;
var dpiScale = TaskContext.Instance().DpiScale;
_logger.LogInformation("遮罩窗口已启动,游戏大小{Width}x{Height},素材缩放{Scale}DPI缩放{Dpi}",
width, height, systemInfo.AssetScale.ToString("F"), dpiScale);
if (width * 9 != height * 16)
{
_logger.LogWarning("当前游戏分辨率不是16:9部分功能可能无法正常使用");
}
// 读取游戏注册表配置
ReadGameSettings();
}
private void ReadGameSettings()
{
try
{
SettingsContainer settings = new();
TaskContext.Instance().GameSettings = settings;
var lang = settings.Language?.TextLang;
if (lang != null && lang != TextLanguage.SimplifiedChinese)
{
_logger.LogWarning("当前游戏语言{Lang}不是简体中文部分功能可能无法正常使用。The game language is not Simplified Chinese, some functions may not work properly", lang);
}
}
catch (Exception e)
{
_logger.LogWarning("游戏注册表配置信息读取失败:" + e.Source + "\r\n--" + Environment.NewLine + e.StackTrace + "\r\n---" + Environment.NewLine + e.Message);
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
this.SetLayeredWindow();
this.SetChildWindow();
this.HideFromAltTab();
}
private void LogTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
var textRange = new TextRange(LogTextBox.Document.ContentStart, LogTextBox.Document.ContentEnd);
if (textRange.Text.Length > 10000)
{
LogTextBox.Document.Blocks.Clear();
}
LogTextBox.ScrollToEnd();
}
public void Refresh()
{
Dispatcher.Invoke(InvalidateVisual);
}
public void Invoke(Action action)
{
Dispatcher.Invoke(action);
}
protected override void OnRender(DrawingContext drawingContext)
{
try
{
var cnt = VisionContext.Instance().DrawContent.RectList.Count + VisionContext.Instance().DrawContent.LineList.Count + VisionContext.Instance().DrawContent.TextList.Count;
if (cnt == 0)
{
return;
}
// 先有上方判断的原因是有可能Render的时候配置还未初始化
if (!TaskContext.Instance().Config.MaskWindowConfig.DisplayRecognitionResultsOnMask)
{
return;
}
foreach (var kv in VisionContext.Instance().DrawContent.RectList)
{
foreach (var drawable in kv.Value)
{
if (!drawable.IsEmpty)
{
drawingContext.DrawRectangle(Brushes.Transparent,
new Pen(new SolidColorBrush(drawable.Pen.Color.ToWindowsColor()), drawable.Pen.Width),
drawable.Rect);
}
}
}
foreach (var kv in VisionContext.Instance().DrawContent.LineList)
{
foreach (var drawable in kv.Value)
{
drawingContext.DrawLine(new Pen(new SolidColorBrush(drawable.Pen.Color.ToWindowsColor()), drawable.Pen.Width), drawable.P1, drawable.P2);
}
}
foreach (var kv in VisionContext.Instance().DrawContent.TextList)
{
foreach (var drawable in kv.Value)
{
if (!drawable.IsEmpty)
{
drawingContext.DrawText(new FormattedText(drawable.Text,
CultureInfo.GetCultureInfo("zh-cn"),
FlowDirection.LeftToRight,
_typeface,
36, Brushes.Black, 1), drawable.Point);
}
}
}
}
catch (Exception e)
{
Debug.WriteLine(e);
}
base.OnRender(drawingContext);
}
public RichTextBox LogBox => LogTextBox;
}
file static class MaskWindowExtension
{
public static void HideFromAltTab(this Window window)
{
HideFromAltTab(new WindowInteropHelper(window).Handle);
}
public static void HideFromAltTab(nint hWnd)
{
int style = User32.GetWindowLong(hWnd, User32.WindowLongFlags.GWL_EXSTYLE);
style |= (int)User32.WindowStylesEx.WS_EX_TOOLWINDOW;
User32.SetWindowLong(hWnd, User32.WindowLongFlags.GWL_EXSTYLE, style);
}
public static void SetLayeredWindow(this Window window, bool isLayered = true)
{
SetLayeredWindow(new WindowInteropHelper(window).Handle, isLayered);
}
private static void SetLayeredWindow(nint hWnd, bool isLayered = true)
{
int style = User32.GetWindowLong(hWnd, User32.WindowLongFlags.GWL_EXSTYLE);
if (isLayered)
{
style |= (int)User32.WindowStylesEx.WS_EX_TRANSPARENT;
style |= (int)User32.WindowStylesEx.WS_EX_LAYERED;
}
else
{
style &= ~(int)User32.WindowStylesEx.WS_EX_TRANSPARENT;
style &= ~(int)User32.WindowStylesEx.WS_EX_LAYERED;
}
_ = User32.SetWindowLong(hWnd, User32.WindowLongFlags.GWL_EXSTYLE, style);
}
public static void SetChildWindow(this Window window)
{
SetChildWindow(new WindowInteropHelper(window).Handle);
}
private static void SetChildWindow(nint hWnd)
{
int style = User32.GetWindowLong(hWnd, User32.WindowLongFlags.GWL_STYLE);
style |= (int)User32.WindowStyles.WS_CHILD;
_ = User32.SetWindowLong(hWnd, User32.WindowLongFlags.GWL_STYLE, style);
}
}