1
0
Fork 0
mirror of https://github.com/hawkeye-stan/msfs-popout-panel-manager.git synced 2024-11-29 09:10:09 +00:00
msfs-popout-panel-manager/Orchestration/PanelConfigurationOrchestrator.cs

405 lines
18 KiB
C#
Raw Normal View History

2022-07-23 19:23:32 +00:00
using MSFSPopoutPanelManager.Shared;
using MSFSPopoutPanelManager.UserDataAgent;
using MSFSPopoutPanelManager.WindowsAgent;
2022-01-27 13:40:04 +00:00
using System;
using System.Drawing;
using System.Linq;
2022-09-25 14:49:49 +00:00
using System.Threading;
using System.Threading.Tasks;
2022-01-27 13:40:04 +00:00
2022-07-23 19:23:32 +00:00
namespace MSFSPopoutPanelManager.Orchestration
2022-01-27 13:40:04 +00:00
{
2022-07-23 19:23:32 +00:00
public class PanelConfigurationOrchestrator : ObservableObject
2022-01-27 13:40:04 +00:00
{
2022-10-11 13:37:49 +00:00
private static WindowProcess _simulatorProcess;
2022-01-27 13:40:04 +00:00
private static PInvoke.WinEventProc _winEvent; // keep this as static to prevent garbage collect or the app will crash
2022-07-23 19:23:32 +00:00
private static IntPtr _winEventHook;
private Rectangle _lastWindowRectangle;
2022-08-13 06:14:49 +00:00
private IntPtr _panelHandleDisableRefresh = IntPtr.Zero;
2022-01-27 13:40:04 +00:00
2022-09-25 14:49:49 +00:00
private uint _prevWinEvent = PInvokeConstant.EVENT_SYSTEM_CAPTUREEND;
private int _winEventClickLock = 0;
private object _hookLock = new object();
private bool _isHookMouseDown = false;
2022-07-23 19:23:32 +00:00
public PanelConfigurationOrchestrator()
2022-01-27 13:40:04 +00:00
{
_winEvent = new PInvoke.WinEventProc(EventCallback);
AllowEdit = true;
}
2022-07-23 19:23:32 +00:00
internal ProfileData ProfileData { get; set; }
internal AppSettingData AppSettingData { get; set; }
private Profile ActiveProfile { get { return ProfileData == null ? null : ProfileData.ActiveProfile; } }
public bool AllowEdit { get; set; }
public void StartConfiguration()
2022-01-27 13:40:04 +00:00
{
2022-10-11 13:37:49 +00:00
_simulatorProcess = WindowProcessManager.GetSimulatorProcess();
2022-07-23 19:23:32 +00:00
HookWinEvent();
2022-07-25 12:40:21 +00:00
TouchEventManager.ActiveProfile = ProfileData.ActiveProfile;
TouchEventManager.AppSetting = AppSettingData.AppSetting;
if (ActiveProfile.PanelConfigs.Any(p => p.TouchEnabled) && !TouchEventManager.IsHooked)
{
TouchEventManager.Hook();
}
2022-01-27 13:40:04 +00:00
}
2022-07-23 19:23:32 +00:00
public void EndConfiguration()
2022-01-27 13:40:04 +00:00
{
2022-07-23 19:23:32 +00:00
UnhookWinEvent();
2022-07-25 12:40:21 +00:00
TouchEventManager.UnHook();
2022-01-27 13:40:04 +00:00
}
2022-07-23 19:23:32 +00:00
public void LockStatusUpdated()
2022-01-27 13:40:04 +00:00
{
2022-07-23 19:23:32 +00:00
ActiveProfile.IsLocked = !ActiveProfile.IsLocked;
ProfileData.WriteProfiles();
2022-01-27 13:40:04 +00:00
}
2022-09-11 13:50:04 +00:00
public void PanelConfigPropertyUpdated(IntPtr panelHandle, PanelConfigPropertyName configPropertyName)
2022-01-27 13:40:04 +00:00
{
2022-09-11 13:50:04 +00:00
if (panelHandle == IntPtr.Zero || !AllowEdit || ActiveProfile.IsLocked)
2022-01-27 13:40:04 +00:00
return;
2022-09-11 13:50:04 +00:00
var panelConfig = ActiveProfile.PanelConfigs.FirstOrDefault(p => p.PanelHandle == panelHandle);
2022-01-27 13:40:04 +00:00
if (panelConfig != null)
{
2022-07-23 19:23:32 +00:00
if (configPropertyName == PanelConfigPropertyName.FullScreen)
2022-01-27 13:40:04 +00:00
{
2022-04-18 19:52:39 +00:00
InputEmulationManager.ToggleFullScreenPanel(panelConfig.PanelHandle);
2022-09-11 13:50:04 +00:00
// Set full screen mode panel coordinate
var windowRectangle = WindowActionManager.GetWindowRect(panelConfig.PanelHandle);
var clientRectangle = WindowActionManager.GetClientRect(panelConfig.PanelHandle);
panelConfig.FullScreenLeft = panelConfig.FullScreen ? windowRectangle.Left : 0;
panelConfig.FullScreenTop = panelConfig.FullScreen ? windowRectangle.Top : 0;
panelConfig.FullScreenWidth = panelConfig.FullScreen ? clientRectangle.Width : 0;
panelConfig.FullScreenHeight = panelConfig.FullScreen ? clientRectangle.Height : 0;
2022-04-18 19:52:39 +00:00
panelConfig.HideTitlebar = false;
panelConfig.AlwaysOnTop = false;
}
2022-07-23 19:23:32 +00:00
else if (configPropertyName == PanelConfigPropertyName.PanelName)
2022-04-18 19:52:39 +00:00
{
var name = panelConfig.PanelName;
2022-07-23 19:23:32 +00:00
if (panelConfig.PanelType == PanelType.CustomPopout && name.IndexOf("(Custom)") == -1)
{
2022-04-18 19:52:39 +00:00
name = name + " (Custom)";
2022-07-23 19:23:32 +00:00
PInvoke.SetWindowText(panelConfig.PanelHandle, name);
}
2022-04-18 19:52:39 +00:00
}
2022-06-30 23:53:08 +00:00
else if (!panelConfig.FullScreen)
2022-04-18 19:52:39 +00:00
{
2022-07-23 19:23:32 +00:00
switch (configPropertyName)
2022-04-18 19:52:39 +00:00
{
case PanelConfigPropertyName.Left:
case PanelConfigPropertyName.Top:
2022-08-13 06:14:49 +00:00
_panelHandleDisableRefresh = panelConfig.PanelHandle;
WindowActionManager.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height);
2022-04-18 19:52:39 +00:00
break;
case PanelConfigPropertyName.Width:
case PanelConfigPropertyName.Height:
2022-08-13 06:14:49 +00:00
_panelHandleDisableRefresh = panelConfig.PanelHandle;
2022-04-18 19:52:39 +00:00
if (panelConfig.HideTitlebar)
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, false);
2022-04-18 19:52:39 +00:00
2022-08-13 06:14:49 +00:00
WindowActionManager.MoveWindowWithMsfsBugOverrirde(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height);
2022-04-18 19:52:39 +00:00
if (panelConfig.HideTitlebar)
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, true);
2022-04-18 19:52:39 +00:00
break;
case PanelConfigPropertyName.AlwaysOnTop:
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyAlwaysOnTop(panelConfig.PanelHandle, panelConfig.PanelType, panelConfig.AlwaysOnTop, new Rectangle(panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height));
2022-04-18 19:52:39 +00:00
break;
case PanelConfigPropertyName.HideTitlebar:
2022-08-13 06:14:49 +00:00
_panelHandleDisableRefresh = panelConfig.PanelHandle;
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, panelConfig.HideTitlebar);
2022-04-18 19:52:39 +00:00
break;
2022-07-25 12:40:21 +00:00
case PanelConfigPropertyName.TouchEnabled:
if (ActiveProfile.PanelConfigs.Any(p => p.TouchEnabled) && !TouchEventManager.IsHooked)
TouchEventManager.Hook();
else if (ActiveProfile.PanelConfigs.All(p => !p.TouchEnabled) && TouchEventManager.IsHooked)
TouchEventManager.UnHook();
2022-07-30 02:29:20 +00:00
if (!panelConfig.TouchEnabled)
panelConfig.DisableGameRefocus = false;
2022-07-25 12:40:21 +00:00
break;
2022-04-18 19:52:39 +00:00
}
2022-01-27 13:40:04 +00:00
}
2022-07-23 19:23:32 +00:00
ProfileData.WriteProfiles();
2022-01-27 13:40:04 +00:00
}
}
2022-09-11 13:50:04 +00:00
public void PanelConfigIncreaseDecrease(IntPtr panelHandle, PanelConfigPropertyName configPropertyName, int changeAmount)
2022-01-27 13:40:04 +00:00
{
2022-09-11 13:50:04 +00:00
if (panelHandle == IntPtr.Zero || !AllowEdit || ActiveProfile.IsLocked || ActiveProfile.PanelConfigs == null || ActiveProfile.PanelConfigs.Count == 0)
2022-01-27 13:40:04 +00:00
return;
2022-09-11 13:50:04 +00:00
var panelConfig = ActiveProfile.PanelConfigs.FirstOrDefault(p => p.PanelHandle == panelHandle);
2022-01-27 13:40:04 +00:00
2022-07-23 19:23:32 +00:00
if (panelConfig != null)
2022-01-27 13:40:04 +00:00
{
2022-07-23 19:23:32 +00:00
// Should not apply any other settings if panel is full screen mode
2022-04-18 19:52:39 +00:00
if (panelConfig.FullScreen)
return;
2022-01-27 13:40:04 +00:00
int orignalLeft = panelConfig.Left;
2022-07-23 19:23:32 +00:00
switch (configPropertyName)
2022-01-27 13:40:04 +00:00
{
case PanelConfigPropertyName.Left:
2022-08-13 06:14:49 +00:00
_panelHandleDisableRefresh = panelConfig.PanelHandle;
2022-01-27 13:40:04 +00:00
panelConfig.Left += changeAmount;
2022-08-13 06:14:49 +00:00
WindowActionManager.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height);
2022-01-27 13:40:04 +00:00
break;
case PanelConfigPropertyName.Top:
2022-08-13 06:14:49 +00:00
_panelHandleDisableRefresh = panelConfig.PanelHandle;
2022-01-27 13:40:04 +00:00
panelConfig.Top += changeAmount;
2022-08-13 06:14:49 +00:00
WindowActionManager.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height);
2022-01-27 13:40:04 +00:00
break;
case PanelConfigPropertyName.Width:
2022-08-13 06:14:49 +00:00
_panelHandleDisableRefresh = panelConfig.PanelHandle;
2022-01-27 13:40:04 +00:00
panelConfig.Width += changeAmount;
2022-04-18 03:38:33 +00:00
if (panelConfig.HideTitlebar)
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, false);
2022-04-18 03:38:33 +00:00
2022-08-13 06:14:49 +00:00
WindowActionManager.MoveWindowWithMsfsBugOverrirde(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height);
2022-04-18 03:38:33 +00:00
if (panelConfig.HideTitlebar)
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, true);
2022-04-18 03:38:33 +00:00
2022-01-27 13:40:04 +00:00
break;
case PanelConfigPropertyName.Height:
2022-08-13 06:14:49 +00:00
_panelHandleDisableRefresh = panelConfig.PanelHandle;
2022-01-27 13:40:04 +00:00
panelConfig.Height += changeAmount;
2022-04-18 03:38:33 +00:00
if (panelConfig.HideTitlebar)
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, false);
2022-04-18 03:38:33 +00:00
2022-08-13 06:14:49 +00:00
WindowActionManager.MoveWindowWithMsfsBugOverrirde(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height);
2022-04-18 03:38:33 +00:00
if (panelConfig.HideTitlebar)
2022-07-23 19:23:32 +00:00
WindowActionManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, true);
2022-04-18 03:38:33 +00:00
2022-01-27 13:40:04 +00:00
break;
default:
return;
}
2022-07-23 19:23:32 +00:00
ProfileData.WriteProfiles();
2022-01-27 13:40:04 +00:00
}
}
2022-07-23 19:23:32 +00:00
private void HookWinEvent()
{
2022-08-08 06:07:24 +00:00
if (ActiveProfile == null || ActiveProfile.PanelConfigs == null || ActiveProfile.PanelConfigs.Count == 0)
return;
2022-07-23 19:23:32 +00:00
// Setup panel config event hooks
2022-10-11 13:37:49 +00:00
if (ActiveProfile.RealSimGearGTN750Gen1Override && AppSettingData.AppSetting.TouchScreenSettings.RealSimGearGTN750Gen1Override)
2022-09-25 14:49:49 +00:00
_winEventHook = PInvoke.SetWinEventHook(PInvokeConstant.EVENT_SYSTEM_CAPTURESTART, PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, _winEvent, 0, 0, PInvokeConstant.WINEVENT_OUTOFCONTEXT);
2022-10-11 13:37:49 +00:00
else
_winEventHook = PInvoke.SetWinEventHook(PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND, PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, _winEvent, 0, 0, PInvokeConstant.WINEVENT_OUTOFCONTEXT);
2022-07-23 19:23:32 +00:00
}
private void UnhookWinEvent()
{
// Unhook all Win API events
PInvoke.UnhookWinEvent(_winEventHook);
}
private void EventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hwnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime)
2022-01-27 13:40:04 +00:00
{
2022-06-30 23:53:08 +00:00
switch (iEvent)
2022-01-27 13:40:04 +00:00
{
2022-06-13 03:49:56 +00:00
case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE:
case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND:
2022-09-25 14:49:49 +00:00
case PInvokeConstant.EVENT_SYSTEM_CAPTURESTART:
case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND:
2022-06-13 03:49:56 +00:00
// check by priority to speed up comparing of escaping constraints
2022-08-08 06:07:24 +00:00
if (hwnd == IntPtr.Zero || idObject != 0 || hWinEventHook != _winEventHook || !AllowEdit)
2022-06-13 03:49:56 +00:00
return;
2022-07-23 19:23:32 +00:00
HandleEventCallback(hwnd, iEvent);
2022-06-13 03:49:56 +00:00
break;
default:
break;
2022-01-27 13:40:04 +00:00
}
2022-06-13 03:49:56 +00:00
}
2022-01-27 13:40:04 +00:00
2022-07-23 19:23:32 +00:00
private void HandleEventCallback(IntPtr hwnd, uint iEvent)
2022-06-13 03:49:56 +00:00
{
2022-07-23 19:23:32 +00:00
var panelConfig = ActiveProfile.PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hwnd);
2022-06-13 03:49:56 +00:00
if (panelConfig == null)
return;
2022-01-27 13:40:04 +00:00
2022-07-23 19:23:32 +00:00
// Should not apply any other settings if panel is full screen mode
if (panelConfig.FullScreen)
return;
if (panelConfig.IsLockable && ActiveProfile.IsLocked)
2022-06-13 03:49:56 +00:00
{
switch (iEvent)
2022-01-27 13:40:04 +00:00
{
2022-06-13 03:49:56 +00:00
case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND:
// Move window back to original location
2022-08-13 06:14:49 +00:00
WindowActionManager.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height);
2022-06-13 03:49:56 +00:00
break;
case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE:
2022-01-27 13:40:04 +00:00
WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
wp.length = System.Runtime.InteropServices.Marshal.SizeOf(wp);
2022-07-23 19:23:32 +00:00
PInvoke.GetWindowPlacement(hwnd, ref wp);
2022-01-27 13:40:04 +00:00
if (wp.showCmd == PInvokeConstant.SW_SHOWMAXIMIZED || wp.showCmd == PInvokeConstant.SW_SHOWMINIMIZED || wp.showCmd == PInvokeConstant.SW_SHOWNORMAL)
{
2022-07-23 19:23:32 +00:00
PInvoke.ShowWindow(hwnd, PInvokeConstant.SW_RESTORE);
2022-01-27 13:40:04 +00:00
}
2022-06-13 03:49:56 +00:00
break;
2022-09-25 14:49:49 +00:00
case PInvokeConstant.EVENT_SYSTEM_CAPTURESTART:
if (!panelConfig.HasTouchableEvent || _prevWinEvent == PInvokeConstant.EVENT_SYSTEM_CAPTURESTART)
break;
HandleTouchDownEvent(panelConfig);
break;
case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND:
if (!panelConfig.TouchEnabled || _prevWinEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE)
break;
HandleTouchUpEvent(panelConfig);
break;
2022-06-13 03:49:56 +00:00
}
}
else
2022-01-27 13:40:04 +00:00
{
switch (iEvent)
{
case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE:
Rectangle winRectangle;
PInvoke.GetWindowRect(panelConfig.PanelHandle, out winRectangle);
if (_lastWindowRectangle == winRectangle) // ignore duplicate callback messages
return;
_lastWindowRectangle = winRectangle;
2022-08-13 06:14:49 +00:00
if (_panelHandleDisableRefresh != IntPtr.Zero)
{
_panelHandleDisableRefresh = IntPtr.Zero;
return;
}
2022-01-27 13:40:04 +00:00
panelConfig.Left = winRectangle.Left;
panelConfig.Top = winRectangle.Top;
2022-04-18 03:38:33 +00:00
2022-08-13 06:14:49 +00:00
if (!panelConfig.HideTitlebar)
2022-04-18 03:38:33 +00:00
{
2022-08-13 06:14:49 +00:00
panelConfig.Width = winRectangle.Width - winRectangle.Left;
panelConfig.Height = winRectangle.Height - winRectangle.Top;
2022-04-18 03:38:33 +00:00
}
else
{
2022-08-13 06:14:49 +00:00
panelConfig.Width = winRectangle.Width - winRectangle.Left - 16;
panelConfig.Height = winRectangle.Height - winRectangle.Top - 39;
2022-04-18 03:38:33 +00:00
}
2022-01-27 13:40:04 +00:00
// Detect if window is maximized, if so, save settings
WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
wp.length = System.Runtime.InteropServices.Marshal.SizeOf(wp);
2022-07-23 19:23:32 +00:00
PInvoke.GetWindowPlacement(hwnd, ref wp);
2022-01-27 13:40:04 +00:00
if (wp.showCmd == PInvokeConstant.SW_SHOWMAXIMIZED || wp.showCmd == PInvokeConstant.SW_SHOWMINIMIZED)
{
2022-07-23 19:23:32 +00:00
ProfileData.WriteProfiles();
2022-01-27 13:40:04 +00:00
}
break;
case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND:
2022-07-23 19:23:32 +00:00
ProfileData.WriteProfiles();
break;
2022-09-25 14:49:49 +00:00
case PInvokeConstant.EVENT_SYSTEM_CAPTURESTART:
if (!panelConfig.HasTouchableEvent || _prevWinEvent == PInvokeConstant.EVENT_SYSTEM_CAPTURESTART)
break;
HandleTouchDownEvent(panelConfig);
break;
case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND:
if (!panelConfig.TouchEnabled || _prevWinEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE)
break;
HandleTouchUpEvent(panelConfig);
break;
}
}
}
private void HandleTouchDownEvent(PanelConfig panelConfig)
{
if (!_isHookMouseDown)
{
lock (_hookLock)
{
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))
_isHookMouseDown = true;
}
}
}
private void HandleTouchUpEvent(PanelConfig panelConfig)
{
if (_isHookMouseDown)
{
Thread.Sleep(AppSettingData.AppSetting.TouchScreenSettings.TouchDownUpDelay);
lock (_hookLock)
{
_isHookMouseDown = false;
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;
if (prevWinEventClickLock == _winEventClickLock && AppSettingData.AppSetting.TouchScreenSettings.RefocusGameWindow)
{
2022-10-11 13:37:49 +00:00
Task.Run(() => RefocusMsfs(prevWinEventClickLock));
2022-09-25 14:49:49 +00:00
}
}
}
}
}
2022-10-11 13:37:49 +00:00
private void RefocusMsfs(int prevWinEventClickLock)
2022-09-25 14:49:49 +00:00
{
Thread.Sleep(AppSettingData.AppSetting.TouchScreenSettings.RefocusGameWindowDelay);
if (prevWinEventClickLock == _winEventClickLock)
{
if (!_isHookMouseDown)
{
2022-10-11 13:37:49 +00:00
var rectangle = WindowActionManager.GetWindowRect(_simulatorProcess.Handle);
var clientRectangle = WindowActionManager.GetClientRect(_simulatorProcess.Handle);
PInvoke.SetCursorPos(rectangle.X + clientRectangle.Width / 2, rectangle.Y + clientRectangle.Height / 2);
2022-06-13 03:49:56 +00:00
}
}
}
2022-01-27 13:40:04 +00:00
}
}