diff --git a/FsConnector/FsConnector.csproj b/FsConnector/FsConnector.csproj index f5bc613..6b650b2 100644 --- a/FsConnector/FsConnector.csproj +++ b/FsConnector/FsConnector.csproj @@ -5,15 +5,15 @@ FsConnector MSFS 2020 Popout Panel Manager FsConnector MSFS 2020 Popout Panel Manager FsConnector - 3.3.3.0 + 3.4.0.0 Stanley Kwok Stanley Kwok Stanley Kwok 2021 https://github.com/hawkeye-stan/msfs-popout-panel-manager MSFSPopoutPanelManager.FsConnector x64;AnyCPU - 3.3.3.0 - 3.3.3.0 + 3.4.0.0 + 3.4.0.0 diff --git a/Model/Model.csproj b/Model/Model.csproj index 397c4f8..d89c978 100644 --- a/Model/Model.csproj +++ b/Model/Model.csproj @@ -5,15 +5,15 @@ MSFSPopoutPanelManager.Model Model MSFS 2020 Popout Panel Manager Model - 3.3.3.0 + 3.4.0.0 Stanley Kwok Stanley Kwok Stanley Kwok 2021 MSFS 2020 Popout Panel Manager Model https://github.com/hawkeye-stan/msfs-popout-panel-manager x64;AnyCPU - 3.3.3.0 - 3.3.3.0 + 3.4.0.0 + 3.4.0.0 diff --git a/Model/PanelConfig.cs b/Model/PanelConfig.cs index dae8d13..1fcfcd8 100644 --- a/Model/PanelConfig.cs +++ b/Model/PanelConfig.cs @@ -28,10 +28,45 @@ namespace MSFSPopoutPanelManager.Model public bool FullScreen { get; set; } + public bool TouchEnabled { get; set; } + [JsonIgnore] public bool IsCustomPopout { get { return PanelType == PanelType.CustomPopout; } } [JsonIgnore] public IntPtr PanelHandle { get; set; } + + [JsonIgnore] + public bool IsLockable + { + get + { + switch (PanelType) + { + case PanelType.CustomPopout: + case PanelType.BuiltInPopout: + case PanelType.MSFSTouchPanel: + return true; + default: + return false; + } + } + } + + [JsonIgnore] + public bool HasTouchableEvent + { + get + { + switch (PanelType) + { + case PanelType.CustomPopout: + case PanelType.BuiltInPopout: + return true; + default: + return false; + } + } + } } } diff --git a/Model/PanelConfigPropertyName.cs b/Model/PanelConfigPropertyName.cs index 5c95694..43606c2 100644 --- a/Model/PanelConfigPropertyName.cs +++ b/Model/PanelConfigPropertyName.cs @@ -12,6 +12,7 @@ namespace MSFSPopoutPanelManager.Model AlwaysOnTop, HideTitlebar, FullScreen, + TouchEnabled, Invalid } diff --git a/Provider/InputEmulationManager.cs b/Provider/InputEmulationManager.cs index fee2b07..9ea3fa4 100644 --- a/Provider/InputEmulationManager.cs +++ b/Provider/InputEmulationManager.cs @@ -28,6 +28,13 @@ namespace MSFSPopoutPanelManager.Provider PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0); } + public static void LeftClickFast(int x, int y) + { + PInvoke.mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0); + Thread.Sleep(50); + PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0); + } + public static void PopOutPanel(int x, int y) { LeftClick(x, y); diff --git a/Provider/PInvoke.cs b/Provider/PInvoke.cs index 39cf50d..cbfd721 100644 --- a/Provider/PInvoke.cs +++ b/Provider/PInvoke.cs @@ -16,6 +16,9 @@ namespace MSFSPopoutPanelManager.Provider public const int SW_MINIMIZE = 6; public const int SW_RESTORE = 9; + public const uint EVENT_SYSTEM_CAPTURESTART = 0x0008; + public const uint EVENT_SYSTEM_CAPTUREEND = 0x0009; + public const uint EVENT_SYSTEM_MOVESIZESTART = 0x000A; public const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B; public const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B; diff --git a/Provider/PanelConfigurationManager.cs b/Provider/PanelConfigurationManager.cs index a344e4f..69ece4c 100644 --- a/Provider/PanelConfigurationManager.cs +++ b/Provider/PanelConfigurationManager.cs @@ -1,7 +1,10 @@ using MSFSPopoutPanelManager.Model; using System; +using System.Diagnostics; using System.Drawing; using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MSFSPopoutPanelManager.Provider { @@ -11,6 +14,8 @@ namespace MSFSPopoutPanelManager.Provider private IntPtr _winEventHook; private static PInvoke.WinEventProc _winEvent; // keep this as static to prevent garbage collect or the app will crash private Rectangle _lastWindowRectangle; + private uint _prevWinEvent = PInvokeConstant.EVENT_SYSTEM_CAPTUREEND; + private int _winEventClickLock = 0; public UserProfile UserProfile { get; set; } @@ -26,7 +31,7 @@ namespace MSFSPopoutPanelManager.Provider public void HookWinEvent() { // Setup panel config event hooks - _winEventHook = PInvoke.SetWinEventHook(PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND, PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE, DiagnosticManager.GetApplicationProcess().Handle, _winEvent, 0, 0, PInvokeConstant.WINEVENT_OUTOFCONTEXT); + _winEventHook = PInvoke.SetWinEventHook(PInvokeConstant.EVENT_SYSTEM_CAPTURESTART, PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE, DiagnosticManager.GetApplicationProcess().Handle, _winEvent, 0, 0, PInvokeConstant.WINEVENT_OUTOFCONTEXT); } public void UnhookWinEvent() @@ -173,34 +178,46 @@ namespace MSFSPopoutPanelManager.Provider private void EventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) { - PanelConfig panelConfig; - - // check by priority to minimize escaping constraint - if (hWnd == IntPtr.Zero - || idObject != 0 - || hWinEventHook != _winEventHook - || !AllowEdit - || !(iEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE || iEvent == PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND) - || UserProfile.PanelConfigs == null || UserProfile.PanelConfigs.Count == 0) + switch(iEvent) { - return; - } - - if(UserProfile.IsLocked) - { - panelConfig = UserProfile.PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hWnd); - - if (panelConfig != null && panelConfig.PanelType == PanelType.CustomPopout) - { - // Move window back to original location if user profile is locked - if (iEvent == PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND) + case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE: + case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND: + case PInvokeConstant.EVENT_SYSTEM_CAPTURESTART: + case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND: + // check by priority to speed up comparing of escaping constraints + if (hWnd == IntPtr.Zero + || idObject != 0 + || hWinEventHook != _winEventHook + || !AllowEdit + || UserProfile.PanelConfigs == null + || UserProfile.PanelConfigs.Count == 0) { - PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, false); return; } - if (iEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE) - { + HandleEventCallback(hWnd, iEvent); + break; + default: + break; + } + } + + private void HandleEventCallback(IntPtr hWnd, uint iEvent) + { + var panelConfig = UserProfile.PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hWnd); + + if (panelConfig == null) + return; + + if (panelConfig.IsLockable && UserProfile.IsLocked) + { + switch (iEvent) + { + case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND: + // Move window back to original location + PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, false); + break; + case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE: // Detect if window is maximized, if so, save settings WINDOWPLACEMENT wp = new WINDOWPLACEMENT(); wp.length = System.Runtime.InteropServices.Marshal.SizeOf(wp); @@ -209,16 +226,19 @@ namespace MSFSPopoutPanelManager.Provider { PInvoke.ShowWindow(hWnd, PInvokeConstant.SW_RESTORE); } - return; - } + break; + case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND: + if (!panelConfig.TouchEnabled || _prevWinEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE) + break; + + if (!panelConfig.HasTouchableEvent || _prevWinEvent == PInvokeConstant.EVENT_SYSTEM_CAPTUREEND) + break; + + HandleTouchEvent(panelConfig); + break; } - - return; } - - panelConfig = UserProfile.PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hWnd); - - if (panelConfig != null) + else { switch (iEvent) { @@ -260,8 +280,54 @@ namespace MSFSPopoutPanelManager.Provider case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND: _userProfileManager.WriteUserProfiles(); break; + case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND: + if (!panelConfig.TouchEnabled || _prevWinEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE) + break; + + if (!panelConfig.HasTouchableEvent || _prevWinEvent == PInvokeConstant.EVENT_SYSTEM_CAPTUREEND) + break; + + HandleTouchEvent(panelConfig); + break; } } + + _prevWinEvent = iEvent; + } + + private void HandleTouchEvent(PanelConfig panelConfig) + { + Point point; + PInvoke.GetCursorPos(out point); + + // Disable left clicking if user is touching the title bar area + if (point.Y - panelConfig.Top > (panelConfig.HideTitlebar ? 5 : 31)) + { + var prevWinEventClickLock = ++_winEventClickLock; + + UnhookWinEvent(); + InputEmulationManager.LeftClickFast(point.X, point.Y); + HookWinEvent(); + + if (prevWinEventClickLock == _winEventClickLock) + { + Task.Run(() => RefocusMsfs(prevWinEventClickLock)); + } + } + } + + private async Task RefocusMsfs(int prevWinEventClickLock) + { + Thread.Sleep(1000); + + if (prevWinEventClickLock == _winEventClickLock) + { + var simulatorProcess = DiagnosticManager.GetSimulatorProcess(); + + Rectangle rectangle; + PInvoke.GetWindowRect(simulatorProcess.Handle, out rectangle); + PInvoke.SetCursorPos(rectangle.X + 18, rectangle.Y + 80); + } } } } diff --git a/Provider/PanelPopoutManager.cs b/Provider/PanelPopoutManager.cs index 1465688..2739302 100644 --- a/Provider/PanelPopoutManager.cs +++ b/Provider/PanelPopoutManager.cs @@ -215,13 +215,13 @@ namespace MSFSPopoutPanelManager.Provider }); // Remove pop out that do not exist for this pop out iteration - foreach(var panelConfig in UserProfile.PanelConfigs.ToList()) - { - if(panelConfig.PanelHandle == IntPtr.Zero) - { - UserProfile.PanelConfigs.Remove(panelConfig); - } - } + //foreach(var panelConfig in UserProfile.PanelConfigs.ToList()) + //{ + // if(panelConfig.PanelHandle == IntPtr.Zero) + // { + // UserProfile.PanelConfigs.Remove(panelConfig); + // } + //} Parallel.ForEach(UserProfile.PanelConfigs, panel => { @@ -254,6 +254,14 @@ namespace MSFSPopoutPanelManager.Provider PInvoke.MoveWindow(panel.PanelHandle, panel.Left, panel.Top, panel.Width, panel.Height, false); Thread.Sleep(1000); + // Built-in panels (ie. Checklist, ATC) needs another window resize since there is a bug where when move between + // monitors, it changes its size + if(panel.PanelType == PanelType.BuiltInPopout) + { + PInvoke.MoveWindow(panel.PanelHandle, panel.Left, panel.Top, panel.Width, panel.Height, false); + Thread.Sleep(1000); + } + // Apply always on top if (panel.AlwaysOnTop) { diff --git a/Provider/Provider.csproj b/Provider/Provider.csproj index 5de1d98..8173c39 100644 --- a/Provider/Provider.csproj +++ b/Provider/Provider.csproj @@ -7,7 +7,7 @@ MSFSPopoutPanelManager.Provider MSFS 2020 Popout Panel Manager Provider MSFS 2020 Popout Panel Manager Provider - 3.3.3.0 + 3.4.0.0 Stanley Kwok Stanley Kwok 2021 https://github.com/hawkeye-stan/msfs-popout-panel-manager diff --git a/Shared/Shared.csproj b/Shared/Shared.csproj index 9d161e7..bfdb97f 100644 --- a/Shared/Shared.csproj +++ b/Shared/Shared.csproj @@ -9,10 +9,10 @@ Stanley Kwok Stanley Kwok 2021 https://github.com/hawkeye-stan/msfs-popout-panel-manager - 3.3.3.0 + 3.4.0.0 AnyCPU;x64 true - 3.3.3.0 + 3.4.0.0 diff --git a/WpfApp/UserControlPanelConfiguration.xaml b/WpfApp/UserControlPanelConfiguration.xaml index d31165f..e9694b3 100644 --- a/WpfApp/UserControlPanelConfiguration.xaml +++ b/WpfApp/UserControlPanelConfiguration.xaml @@ -91,10 +91,10 @@ - + -