From 93cca99f38d0b3083dc76fc0574aba5a67701039 Mon Sep 17 00:00:00 2001 From: pdone <617941447@qq.com> Date: Fri, 19 Jan 2024 16:13:40 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=95=BF=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E5=90=8E=E5=85=B3=E9=97=AD=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E4=B8=8D=E5=BC=B9=E5=87=BA=E4=B8=BB=E7=95=8C=E9=9D=A2=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20#58?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeControl/Controller.Designer.cs | 2 +- FreeControl/Controller.cs | 18 ++-- FreeControl/Main.cs | 82 ++++++++++------- FreeControl/Properties/AssemblyInfo.cs | 4 +- FreeControl/Setting.cs | 10 +++ FreeControl/Update.en.md | 4 + FreeControl/Update.md | 4 + FreeControl/Utils/ADB.cs | 2 +- FreeControl/Utils/JsonHelper.cs | 37 ++++---- FreeControl/Utils/MoveListener.cs | 119 ++++++++++++++++++++++++- 10 files changed, 214 insertions(+), 68 deletions(-) diff --git a/FreeControl/Controller.Designer.cs b/FreeControl/Controller.Designer.cs index 43c7396..894303e 100644 --- a/FreeControl/Controller.Designer.cs +++ b/FreeControl/Controller.Designer.cs @@ -194,7 +194,7 @@ private void InitializeComponent() this.ShowRadius = false; this.ShowTitle = false; this.Text = "Controller"; - this.TopMost = true; + this.TopMost = false; this.flowPanel.ResumeLayout(false); this.ResumeLayout(false); diff --git a/FreeControl/Controller.cs b/FreeControl/Controller.cs index ad85920..2145fdc 100644 --- a/FreeControl/Controller.cs +++ b/FreeControl/Controller.cs @@ -19,15 +19,15 @@ public Controller() /// /// 更新控制器位置 /// - public void UpdateLocation() - { - Action action = () => - { - // 减去控制器自身默认宽度 - Location = new Point(Main._Setting.ScrcpyPointX - 57, Main._Setting.ScrcpyPointY); - }; - Invoke(action); - } + //public void UpdateLocation() + //{ + // Action action = () => + // { + // // 减去控制器自身默认宽度 + // Location = new Point(Main._Setting.ScrcpyPointX - 57, Main._Setting.ScrcpyPointY); + // }; + // Invoke(action); + //} /// /// 初始化窗口大小和位置 diff --git a/FreeControl/Main.cs b/FreeControl/Main.cs index d77f704..cbb6bfc 100644 --- a/FreeControl/Main.cs +++ b/FreeControl/Main.cs @@ -83,12 +83,17 @@ public class Info public static string NameVersion { get; set; } } - public MultiLanguage i18n; + /// + /// 多语言支持 + /// + public MultiLanguage I18n; + + public static IntPtr ControllerPtr = IntPtr.Zero; /// - /// 是否启用输入法切换 + /// 保持后台活跃 避免界面隐藏后被自动回收 /// - public readonly bool EnableSwitchIME = false; + private readonly System.Timers.Timer _Timer; #endregion #region 构造函数 @@ -99,12 +104,20 @@ public Main() { // 获取用户配置数据 _Setting = GetUserData(); - LoadResEn(); - + // 加载语言资源 + LoadLangRes(); + // 设置语言 string lang = _Setting.Language.GetDesc(); - i18n = new MultiLanguage(_Setting.Language); + I18n = new MultiLanguage(_Setting.Language); System.Globalization.CultureInfo UICulture = new System.Globalization.CultureInfo(lang); Thread.CurrentThread.CurrentUICulture = UICulture; + // 界面保活 + _Timer = new System.Timers.Timer(_Setting.Heartbeat); + _Timer.Elapsed += (sender, e) => + { + Logger.Info("", "alive"); + }; + InitializeComponent(); InitPdone(); IsInit = false; @@ -135,10 +148,10 @@ public static Setting GetUserData() Directory.CreateDirectory(UserDataPath); if (!File.Exists(fullPath)) { - File.WriteAllText(fullPath, JsonHelper.json(tempData)); + File.WriteAllText(fullPath, JsonHelper.Obj2Str(tempData)); } StreamReader reader = File.OpenText(fullPath); - tempData = JsonHelper.jsonDes(reader.ReadToEnd()); + tempData = JsonHelper.Str2Obj(reader.ReadToEnd()); reader.Close(); return tempData; } @@ -158,7 +171,7 @@ public static void SetUserData(Setting userData) { var fullPath = Path.Combine(UserDataPath, "config.json"); Directory.CreateDirectory(UserDataPath); - File.WriteAllText(fullPath, JsonHelper.json(userData)); + File.WriteAllText(fullPath, JsonHelper.Obj2Str(userData)); } catch (Exception ex) { @@ -197,8 +210,8 @@ public void InitPdone() } #region 控件状态 - uiLabel7.Visible = EnableSwitchIME; - linkIME.Visible = EnableSwitchIME; + uiLabel7.Visible = _Setting.EnableSwitchIME; + linkIME.Visible = _Setting.EnableSwitchIME; #endregion #region 事件绑定 @@ -280,9 +293,9 @@ public void InitPdone() #endregion #region 配置项默认值 - comboPx.Items[0] = i18n.def; - comboMbps.Items[0] = i18n.def; - comboMaxFPS.Items[0] = i18n.def; + comboPx.Items[0] = I18n.def; + comboMbps.Items[0] = I18n.def; + comboMaxFPS.Items[0] = I18n.def; comboPx.SelectedIndex = _Setting.PXIndex; comboMbps.SelectedIndex = _Setting.BitRateIndex; comboMaxFPS.SelectedIndex = _Setting.MaxFPSIndex; @@ -305,9 +318,9 @@ public void InitPdone() cbxShowTouches.Checked = _Setting.ShowTouches; cbxReadOnly.Checked = _Setting.ReadOnly; cbxAudioEnabled.Checked = _Setting.AudioEnabled; - linkIME.Text = i18n.imes[(InputMethod)_Setting.IME]; - tbxIp.Watermark = i18n.tbxIpPlaceholder; - tbxPort.Watermark = i18n.tbxPortPlaceholder; + linkIME.Text = I18n.imes[(InputMethod)_Setting.IME]; + tbxIp.Watermark = I18n.tbxIpPlaceholder; + tbxPort.Watermark = I18n.tbxPortPlaceholder; #endregion } @@ -334,9 +347,9 @@ private void ExtractResource(bool reload = false) } /// - /// 加载英文资源 + /// 加载语言资源 此资源控制界面布局 /// - public void LoadResEn() + public void LoadLangRes() { if (_Setting.Language == Lang.en) { @@ -365,7 +378,7 @@ private void StartButtonClick(object sender, EventArgs e) if (_Setting.UseWireless && (string.IsNullOrWhiteSpace(_Setting.IPAddress) || string.IsNullOrWhiteSpace(_Setting.Port))) { - ShowMessage(i18n.msgIpNull); + ShowMessage(I18n.msgIpNull); return; } @@ -511,7 +524,7 @@ private void RunScrcpy() scrcpy.Exited += (ss, ee) => { SetUserData(_Setting);// 关闭scrcpy后保存一下配置文件 - if (EnableSwitchIME && _Setting.IME != 0 && _Setting.IMEOrigin.IsNotNull()) + if (_Setting.EnableSwitchIME && _Setting.IME != 0 && _Setting.IMEOrigin.IsNotNull()) { ADB.Execute($"shell ime set {_Setting.IMEOrigin}"); } @@ -519,12 +532,12 @@ private void RunScrcpy() FromHandle(false); ButtonHandle(false); LoadHistoryIPs(true); - ShowMessage(i18n.msgExit); + ShowMessage(I18n.msgExit); }; scrcpy.BeginErrorReadLine(); scrcpy.BeginOutputReadLine(); - if (EnableSwitchIME && _Setting.IME != 0) + if (_Setting.EnableSwitchIME && _Setting.IME != 0) { // 获取当前输入法 string strCurIME = ADB.Execute($"adb shell settings get secure default_input_method"); @@ -562,12 +575,12 @@ private void ButtonHandle(bool isStart) if (isStart) { btnStart.Enabled = false; - btnStart.Text = i18n.btnStarting; + btnStart.Text = I18n.btnStarting; } else { btnStart.Enabled = true; - btnStart.Text = i18n.btnStartDef; + btnStart.Text = I18n.btnStartDef; } }; Invoke(action); @@ -588,8 +601,10 @@ private void FromHandle(bool isStart) if (_Setting.ControllerEnabled) { _Controller = new Controller(); + ControllerPtr = _Controller.Handle; _Controller.Show(); } + _Timer?.Start(); } else { @@ -597,6 +612,7 @@ private void FromHandle(bool isStart) Show(); Activate(); Focus(); + _Timer?.Stop(); } }; Invoke(action); @@ -621,7 +637,7 @@ void LoadHistoryIPs(bool isReload = false) else action(); } -#endregion + #endregion #region 配置项改变事件 /// @@ -903,7 +919,7 @@ private void linkEnabledADB_Click(object sender, EventArgs e) private void linkSetPort_Click(object sender, EventArgs e) { - if (UIMessageBox.Show(i18n.linkSetPort, i18n.linkSetPortTitle, + if (UIMessageBox.Show(I18n.linkSetPort, I18n.linkSetPortTitle, _Setting.DarkMode ? UIStyle.Black : UIStyle.Gray, UIMessageBoxButtons.OKCancel, false)) { var batPath = ScrcpyPath + "SetProt.bat"; @@ -972,27 +988,27 @@ private void lbAllShortcut_Click(object sender, EventArgs e) private void linkIME_Click(object sender, EventArgs e) { - var list = i18n.imes.Values.ToList(); + var list = I18n.imes.Values.ToList(); int select = _Setting.IME; - if (UISelectDialog.ShowSelectDialog(this, ref select, list, i18n.linkImeTitle, i18n.linkImeContent)) + if (UISelectDialog.ShowSelectDialog(this, ref select, list, I18n.linkImeTitle, I18n.linkImeContent)) { _Setting.IME = select; - linkIME.Text = i18n.imes[(InputMethod)_Setting.IME]; + linkIME.Text = I18n.imes[(InputMethod)_Setting.IME]; } } private void linkLang_Click(object sender, EventArgs e) { - var list = i18n.langs; + var list = I18n.langs; int select = (int)_Setting.Language; - if (UISelectDialog.ShowSelectDialog(this, ref select, list, i18n.linkLangTitle, i18n.linkLangContent)) + if (UISelectDialog.ShowSelectDialog(this, ref select, list, I18n.linkLangTitle, I18n.linkLangContent)) { if (select == (int)_Setting.Language) { return; } _Setting.Language = (Lang)select; - LoadResEn(); + LoadLangRes(); System.Windows.Forms.Application.Restart(); } diff --git a/FreeControl/Properties/AssemblyInfo.cs b/FreeControl/Properties/AssemblyInfo.cs index d6a1e84..51f2c18 100644 --- a/FreeControl/Properties/AssemblyInfo.cs +++ b/FreeControl/Properties/AssemblyInfo.cs @@ -33,5 +33,5 @@ //通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] //[assembly: AssemblyVersion("1.0.0")] -[assembly: AssemblyFileVersion("1.6.8")] -[assembly: AssemblyVersion("1.6.8")] +[assembly: AssemblyFileVersion("1.6.9")] +[assembly: AssemblyVersion("1.6.9")] diff --git a/FreeControl/Setting.cs b/FreeControl/Setting.cs index 1b6b145..dfee016 100644 --- a/FreeControl/Setting.cs +++ b/FreeControl/Setting.cs @@ -173,6 +173,11 @@ public List ControllerButton /// public string Version { get; set; } = "1.0.0"; + /// + /// 是否启用输入法切换功能 + /// + public bool EnableSwitchIME { get; set; } = false; + /// /// 启动Scrcpy时使用的输入法枚举值 /// @@ -196,5 +201,10 @@ public List ControllerButton /// 主窗口 y坐标 /// public int MainWindowY { get; set; } = 0; + + /// + /// 心跳间隔 单位:毫秒 + /// + public int Heartbeat { get; set; } = 60000; } } diff --git a/FreeControl/Update.en.md b/FreeControl/Update.en.md index 00c329c..db2a9f4 100644 --- a/FreeControl/Update.en.md +++ b/FreeControl/Update.en.md @@ -1,5 +1,9 @@ # Free Control Update Record +## v1.6.9 +- Fix bug +- Some optimized + ## v1.6.8 - Fix bug - Remove automatic switching IME diff --git a/FreeControl/Update.md b/FreeControl/Update.md index 2d8cf07..df4286b 100644 --- a/FreeControl/Update.md +++ b/FreeControl/Update.md @@ -1,5 +1,9 @@ # Free Control 更新记录 +## v1.6.9 +- 修复了一些bug +- 优化了代码 + ## v1.6.8 - 修复了一些bug - 移除自动切换输入法功能 diff --git a/FreeControl/Utils/ADB.cs b/FreeControl/Utils/ADB.cs index a4c9cd1..a383545 100644 --- a/FreeControl/Utils/ADB.cs +++ b/FreeControl/Utils/ADB.cs @@ -88,7 +88,7 @@ public static bool Screenshot() /// public static void ExecuteShell(string command) { - Logger.Info($"{command}", $"ADB Shell"); + Logger.Info($"{command}", $"adb shell"); var AdbProcessInfo = new ProcessStartInfo($"{ADBPath}adb.exe") { diff --git a/FreeControl/Utils/JsonHelper.cs b/FreeControl/Utils/JsonHelper.cs index d21f8bc..7dd6c03 100644 --- a/FreeControl/Utils/JsonHelper.cs +++ b/FreeControl/Utils/JsonHelper.cs @@ -1,31 +1,28 @@ -//using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Web.Script.Serialization; +using System.Web.Script.Serialization; namespace FreeControl.Utils { public class JsonHelper { - /// - /// json反序列化 - /// - /// - /// - public static T jsonDes(string input) + /// + /// Json字符串转对象 + /// + /// + /// + /// + public static T Str2Obj(string input) { - //return JsonConvert.DeserializeObject(input); - JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); - return javaScriptSerializer.Deserialize(input); + return new JavaScriptSerializer().Deserialize(input); } - public static string json(object obj) + + /// + /// 对象转Json字符串 + /// + /// + /// + public static string Obj2Str(object obj) { - //return JsonConvert.SerializeObject(obj, Formatting.Indented); - JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); - return javaScriptSerializer.Serialize(obj); + return new JavaScriptSerializer().Serialize(obj); } } } diff --git a/FreeControl/Utils/MoveListener.cs b/FreeControl/Utils/MoveListener.cs index 440090f..3add81d 100644 --- a/FreeControl/Utils/MoveListener.cs +++ b/FreeControl/Utils/MoveListener.cs @@ -34,6 +34,10 @@ public class MoveListener [DllImport("user32.dll")] private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool ShowWindow(IntPtr hWnd, ShowWindowCommands nCmdShow); + public static string GetWindowTitle(IntPtr hWnd) { const int nChars = 256; @@ -50,6 +54,65 @@ private struct RECT public int Right; public int Bottom; } + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + // 定义窗口状态的结构体 + [Serializable] + [StructLayout(LayoutKind.Sequential)] + struct WINDOWPLACEMENT + { + public int length; + public int flags; + public ShowWindowCommands showCmd; + public POINT ptMinPosition; + public POINT ptMaxPosition; + public RECT rcNormalPosition; + } + + // 定义显示窗口的命令 + enum ShowWindowCommands : int + { + SW_HIDE = 0, // 隐藏窗口 + SW_SHOWNORMAL = 1, // 正常显示窗口 + // SW_NORMAL = 1, // 与 SW_SHOWNORMAL 相同 + SW_SHOWMINIMIZED = 2, // 最小化窗口 + SW_SHOWMAXIMIZED = 3, // 最大化窗口 + // SW_MAXIMIZE = 3, // 与 SW_SHOWMAXIMIZED 相同 + SW_SHOWNOACTIVATE = 4, // 以激活状态显示窗口,但不激活它 + SW_SHOW = 5, // 以当前大小和位置显示窗口 + SW_MINIMIZE = 6, // 最小化窗口 + SW_SHOWMINNOACTIVE = 7, // 以最小化状态显示窗口,但不激活它 + SW_SHOWNA = 8, // 在当前位置以非激活状态显示窗口 + SW_RESTORE = 9, // 恢复窗口的大小和位置 + SW_SHOWDEFAULT = 10, // 使用创建时的默认大小和位置显示窗口 + SW_FORCEMINIMIZE = 11 // 最小化窗口,即使程序不响应 + } + + [Serializable] + [StructLayout(LayoutKind.Sequential)] + struct POINT + { + public int x; + public int y; + } + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + // 定义窗口位置和大小的标志 + const uint SWP_NOSIZE = 0x0001; // 不改变窗口大小 + const uint SWP_NOMOVE = 0x0002; // 不改变窗口位置 + const uint SWP_NOZORDER = 0x0004; // 不改变窗口Z顺序 + const uint SWP_NOACTIVATE = 0x0010; // 不激活窗口 + + // 指定置顶窗口 + static IntPtr HWND_TOPMOST = new IntPtr(-1); + // 指定取消置顶窗口 + static IntPtr HWND_NOTOPMOST = new IntPtr(-2); #endregion #region 钩子方式 @@ -66,6 +129,7 @@ private static void WinEventCallback(IntPtr hWinEventHook, uint eventType, IntPt if (processId == idProcess) { UpdateLocation(hwnd); + UpdateStatus(hwnd); } } } @@ -82,21 +146,68 @@ private static void UpdateLocation(IntPtr hwnd) Main._Setting.ScrcpyPointX = rect.Left + 8; Main._Setting.ScrcpyPointY = rect.Top + 31; - Main._Controller?.UpdateLocation(); + if (Main.ControllerPtr != IntPtr.Zero) + { + // 使用winapi更新控制器位置 + SetWindowPos(Main.ControllerPtr, IntPtr.Zero, + Main._Setting.ScrcpyPointX - 57, Main._Setting.ScrcpyPointY, + 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + } lastRect = rect; } } + + // 创建WINDOWPLACEMENT结构体 + private static WINDOWPLACEMENT placement = new WINDOWPLACEMENT() + { + length = Marshal.SizeOf(placement) + }; + + private static void UpdateStatus(IntPtr hwnd) + { + if (hwnd == IntPtr.Zero || Main.ControllerPtr == IntPtr.Zero) + return; + if (GetWindowPlacement(hwnd, ref placement)) + { + + switch (placement.showCmd) + { + case ShowWindowCommands.SW_HIDE: + case ShowWindowCommands.SW_SHOWMINIMIZED: + case ShowWindowCommands.SW_SHOWMAXIMIZED: + case ShowWindowCommands.SW_SHOWMINNOACTIVE: + case ShowWindowCommands.SW_FORCEMINIMIZE: + SetWindowPos(Main.ControllerPtr, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + ShowWindow(Main.ControllerPtr, ShowWindowCommands.SW_HIDE); + break; + case ShowWindowCommands.SW_SHOWNORMAL: + case ShowWindowCommands.SW_SHOWNOACTIVATE: + case ShowWindowCommands.SW_SHOW: + case ShowWindowCommands.SW_SHOWNA: + case ShowWindowCommands.SW_RESTORE: + case ShowWindowCommands.SW_SHOWDEFAULT: + SetWindowPos(Main.ControllerPtr, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + ShowWindow(Main.ControllerPtr, ShowWindowCommands.SW_SHOWNOACTIVATE); + break; + } + } + } #endregion private static Timer timer; + private static GCHandle handle; + public static void StartListening(Process process, bool isHook = true) { if (isHook)// 使用钩子监控scrcpy窗口移动 { + WinEventDelegate myDelegate = WinEventCallback; + handle = GCHandle.Alloc(myDelegate);// 手动管理内存 避免被回收 + idProcess = (uint)process.Id; - hWinEventHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, WinEventCallback, MoveListener.idProcess, 0, WINEVENT_OUTOFCONTEXT); + hWinEventHook = SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, (WinEventDelegate)handle.Target, MoveListener.idProcess, 0, WINEVENT_OUTOFCONTEXT); } else// 无线时 使用钩子无法监控到scrcpy窗口移动 暂时使用主动查询方式实现控制器吸附 { @@ -118,6 +229,7 @@ public static void StartListening(Process process, bool isHook = true) timer.Elapsed += (sender, e) => { UpdateLocation(hwnd); + UpdateStatus(hwnd); }; timer.Start(); } @@ -134,6 +246,9 @@ public static void StopListening() timer.Stop(); timer.Dispose(); } + + if (handle.IsAllocated) + handle.Free(); } } }