diff --git a/mRemoteV1/UI/FocusHelpers/ExternalProcessFocusHelper.cs b/mRemoteV1/UI/FocusHelpers/ExternalProcessFocusHelper.cs index 3ea1614f..85cc9719 100644 --- a/mRemoteV1/UI/FocusHelpers/ExternalProcessFocusHelper.cs +++ b/mRemoteV1/UI/FocusHelpers/ExternalProcessFocusHelper.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.InteropServices; using System.Windows.Forms; using mRemoteNG.App; using mRemoteNG.Connection; using mRemoteNG.Connection.Protocol; using mRemoteNG.Messages; using mRemoteNG.UI.Tabs; +using Message = System.Windows.Forms.Message; namespace mRemoteNG.UI.FocusHelpers { @@ -17,13 +19,14 @@ namespace mRemoteNG.UI.FocusHelpers private bool _childProcessHeldLastFocus; private bool _mainWindowFocused; private bool _connectionReleasingFocus; + private bool _inSizeMove; + private bool _fixingMainWindowFocus; /// /// TRUE if any part of mrng has focus - the main window or child processes /// public bool MrngFocused => MainWindowFocused || ChildProcessFocused; - public bool FixingMainWindowFocus { get; private set; } public bool MainWindowFocused { @@ -81,7 +84,7 @@ namespace mRemoteNG.UI.FocusHelpers ExternalWindowDefocused(); } - private void ProtocolOnDisconnected(object sender, string disconnectedmessage, int? reasoncode) + private void ProtocolOnDisconnected(object sender, string disconnectedMessage, int? reasonCode) { if (!(sender is ExternalProcessProtocolBase prot)) return; @@ -116,16 +119,16 @@ namespace mRemoteNG.UI.FocusHelpers return; } - //FixingMainWindowFocus = true; + _fixingMainWindowFocus = true; //_focusMainWindowAction(); - //FixingMainWindowFocus = false; + _fixingMainWindowFocus = false; ifc.Protocol.Focus(); //var conFormWindow = ifc.FindForm(); //((ConnectionTab) conFormWindow)?.RefreshInterfaceController(); } - public int KeyboardHookCallback(int msg, NativeMethods.KBDLLHOOKSTRUCT kbd) + private int KeyboardHookCallback(int msg, NativeMethods.KBDLLHOOKSTRUCT kbd) { var key = (Keys)kbd.vkCode; // Alt + Tab @@ -142,28 +145,37 @@ namespace mRemoteNG.UI.FocusHelpers } // Alt + ` - if (key.HasFlag(Keys.Oem3) && kbd.flags.HasFlag(NativeMethods.KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN)) - { - if (msg != NativeMethods.WM_SYSKEYUP && msg != NativeMethods.WM_KEYUP) - return 0; + //if (key.HasFlag(Keys.Oem3) && kbd.flags.HasFlag(NativeMethods.KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN)) + //{ + // if (msg != NativeMethods.WM_SYSKEYUP && msg != NativeMethods.WM_KEYUP) + // return 0; - if (ChildProcessFocused) - { - _connectionReleasingFocus = true; - var focused = NativeMethods.SetForegroundWindow(_mainWindowHandle); - _connectionReleasingFocus = false; - return 1; - } + // if (ChildProcessFocused) + // { + // _connectionReleasingFocus = true; + // var focused = NativeMethods.SetForegroundWindow(_mainWindowHandle); + // _connectionReleasingFocus = false; + // return 1; + // } - if (!MainWindowFocused) - return 0; - ActivateConnection(); - return 1; - } + // if (!MainWindowFocused) + // return 0; + // ActivateConnection(); + // return 1; + //} return 0; } + private void ForceExtAppToGiveUpFocus() + { + if (!ChildProcessHeldLastFocus) + return; + + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Forcing extpp to give up focus."); + ChildProcessHeldLastFocus = false; + } + private void FixExternalAppAltTab() { Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "FIXING ALT-TAB FOR EXTAPP"); @@ -188,6 +200,7 @@ namespace mRemoteNG.UI.FocusHelpers private void ExternalWindowFocused() { _extFocusCount++; + ChildProcessHeldLastFocus = true; } private void ExternalWindowDefocused() @@ -195,5 +208,86 @@ namespace mRemoteNG.UI.FocusHelpers _extFocusCount--; ChildProcessHeldLastFocus = !MainWindowFocused && !ChildProcessFocused; } + + /// + /// Give the a chance to + /// hook into a system window's WndProc message handler. Returns + /// true if an action has been taken which should override the + /// base windows own WndProc handling. + /// + /// + /// + /// Returns true if an action has been taken which should override the + /// base windows own WndProc handling. + /// + public bool HandleWndProc(ref Message m) + { + // ReSharper disable once SwitchStatementMissingSomeCases + switch (m.Msg) + { + case NativeMethods.WM_PARENTNOTIFY: + var notifyType = m.WParam.ToInt32(); + // ignore non-click notify events + if (notifyType == NativeMethods.WM_CREATE || notifyType == NativeMethods.WM_DESTROY) + break; + + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Main window clicked"); + // when the main window is clicked, assume the user wants the main window focused + ForceExtAppToGiveUpFocus(); + break; + case NativeMethods.WM_ACTIVATEAPP: + if (_fixingMainWindowFocus) + break; + + // main window is being deactivated + if (m.WParam.ToInt32() == 0) + { + MainWindowFocused = false; + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window lost focus (_childProcessHeldLastFocus={ChildProcessHeldLastFocus})"); + break; + } + + MainWindowFocused = true; + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window received focus (_childProcessHeldLastFocus={ChildProcessHeldLastFocus})"); + + break; + case NativeMethods.WM_NCACTIVATE: + // Never allow the mRemoteNG window to display itself as inactive. By doing this, + // we ensure focus events can propagate to child connection windows + NativeMethods.DefWindowProc(_mainWindowHandle, Convert.ToUInt32(m.Msg), (IntPtr)1, m.LParam); + m.Result = (IntPtr)1; + return true; + + // handle re-focusing connection when the main window moves or resizes + case NativeMethods.WM_ENTERSIZEMOVE: + _inSizeMove = true; + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Begin app window move/resize"); + break; + case NativeMethods.WM_EXITSIZEMOVE: + _inSizeMove = false; + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "End app window move/resize"); + // This handles activations from clicks that started a size/move operation + ActivateConnection(); + break; + case NativeMethods.WM_WINDOWPOSCHANGED: + // Ignore this message if the window wasn't activated + if (!MainWindowFocused) + break; + + var windowPos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.WINDOWPOS)); + if ((windowPos.flags & NativeMethods.SWP_NOACTIVATE) == 0) + { + if (!MainWindowFocused && !_inSizeMove) + { + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "WM_WINDOWPOSCHANGED DONE"); + ActivateConnection(); + } + } + + break; + } + + return false; + } } } diff --git a/mRemoteV1/UI/Forms/frmMain.Designer.cs b/mRemoteV1/UI/Forms/frmMain.Designer.cs index de63b224..454aba97 100644 --- a/mRemoteV1/UI/Forms/frmMain.Designer.cs +++ b/mRemoteV1/UI/Forms/frmMain.Designer.cs @@ -205,8 +205,6 @@ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmMain_FormClosing); this.Load += new System.EventHandler(this.frmMain_Load); this.Shown += new System.EventHandler(this.frmMain_Shown); - this.ResizeBegin += new System.EventHandler(this.frmMain_ResizeBegin); - this.ResizeEnd += new System.EventHandler(this.frmMain_ResizeEnd); this.Resize += new System.EventHandler(this.frmMain_Resize); this.msMain.ResumeLayout(false); this.msMain.PerformLayout(); diff --git a/mRemoteV1/UI/Forms/frmMain.cs b/mRemoteV1/UI/Forms/frmMain.cs index 5e5c8a56..1b86f61e 100644 --- a/mRemoteV1/UI/Forms/frmMain.cs +++ b/mRemoteV1/UI/Forms/frmMain.cs @@ -8,11 +8,14 @@ using mRemoteNG.Config.DataProviders; using mRemoteNG.Config.Putty; using mRemoteNG.Config.Settings; using mRemoteNG.Connection; +using mRemoteNG.Connection.Protocol; using mRemoteNG.Messages; using mRemoteNG.Messages.MessageWriters; using mRemoteNG.Themes; using mRemoteNG.Tools; +using mRemoteNG.UI.FocusHelpers; using mRemoteNG.UI.Menu; +using mRemoteNG.UI.Panels; using mRemoteNG.UI.Tabs; using mRemoteNG.UI.TaskDialog; using mRemoteNG.UI.Window; @@ -23,13 +26,8 @@ using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; -using System.Linq; -using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; -using mRemoteNG.Connection.Protocol; -using mRemoteNG.UI.FocusHelpers; -using mRemoteNG.UI.Panels; using WeifenLuo.WinFormsUI.Docking; using Message = System.Windows.Forms.Message; @@ -42,8 +40,6 @@ namespace mRemoteNG.UI.Forms public static FrmMain Default { get; } = new FrmMain(); private static ClipboardchangeEventHandler _clipboardChangedEvent; - private bool _inSizeMove; - private bool _inMouseActivate; private IntPtr _fpChainedWindowHandle; private bool _usingSqlServer; private string _connectionsFileName; @@ -457,13 +453,6 @@ namespace mRemoteNG.UI.Forms #endregion #region Window Overrides and DockPanel Stuff - - private void frmMain_ResizeBegin(object sender, EventArgs e) - { - _inSizeMove = true; - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Begin app window move/resize"); - } - private void frmMain_Resize(object sender, EventArgs e) { if (WindowState == FormWindowState.Minimized) @@ -482,15 +471,6 @@ namespace mRemoteNG.UI.Forms } } - private void frmMain_ResizeEnd(object sender, EventArgs e) - { - _inSizeMove = false; - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "End app window move/resize"); - // This handles activations from clicks that started a size/move operation - _focusHelper.ActivateConnection(); - } - - // Maybe after starting putty, remove its ability to show up in alt-tab? // SetWindowLong(this.Handle, GWL_EXSTYLE, (GetWindowLong(this.Handle,GWL_EXSTYLE) | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW); protected override void WndProc(ref Message m) @@ -498,13 +478,13 @@ namespace mRemoteNG.UI.Forms // Listen for and handle operating system messages try { + if (_focusHelper?.HandleWndProc(ref m) == true) + return; + // ReSharper disable once SwitchStatementMissingSomeCases switch (m.Msg) { case NativeMethods.WM_MOUSEACTIVATE: - _inMouseActivate = true; - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"_inMouseActivate = {_inMouseActivate}"); - var controlThatWasClicked2 = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition)) ?? GetChildAtPoint(MousePosition); @@ -512,29 +492,6 @@ namespace mRemoteNG.UI.Forms break; Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Clicked control: {controlThatWasClicked2}"); - break; - case NativeMethods.WM_ACTIVATEAPP: - if (_focusHelper.FixingMainWindowFocus) - break; - - if (m.WParam.ToInt32() == 0) // mRemoteNG is being deactivated - { - _inMouseActivate = false; - _focusHelper.MainWindowFocused = false; - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window lost focus (_childProcessHeldLastFocus={_focusHelper.ChildProcessHeldLastFocus})"); - break; - } - - _focusHelper.MainWindowFocused = true; - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window received focus (_childProcessHeldLastFocus={_focusHelper.ChildProcessHeldLastFocus})"); - - //var candidateTabToFocus = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition)) - // ?? GetChildAtPoint(MousePosition); - //if (candidateTabToFocus is InterfaceControl) - //{ - // candidateTabToFocus.Parent.Focus(); - //} - break; case NativeMethods.WM_ACTIVATE: if (NativeMethods.LOWORD(m.WParam) == NativeMethods.WA_ACTIVE) @@ -554,50 +511,28 @@ namespace mRemoteNG.UI.Forms Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Click activate: {controlThatWasClicked}"); - if (controlThatWasClicked is TreeView || - controlThatWasClicked is ComboBox || - controlThatWasClicked is TextBox || - controlThatWasClicked is FrmMain || - controlThatWasClicked is AutoHideStripBase) - { - controlThatWasClicked.Focus(); - } - else if (controlThatWasClicked.CanSelect || - controlThatWasClicked is MenuStrip || - controlThatWasClicked is ToolStrip) - { - // Simulate a mouse event since one wasn't generated by Windows - SimulateClick(controlThatWasClicked); - controlThatWasClicked.Focus(); - } + //if (controlThatWasClicked is TreeView || + // controlThatWasClicked is ComboBox || + // controlThatWasClicked is TextBox || + // controlThatWasClicked is FrmMain || + // controlThatWasClicked is AutoHideStripBase) + //{ + // controlThatWasClicked.Focus(); + //} + //else if (controlThatWasClicked.CanSelect || + // controlThatWasClicked is MenuStrip || + // controlThatWasClicked is ToolStrip) + //{ + // // Simulate a mouse event since one wasn't generated by Windows + // SimulateClick(controlThatWasClicked); + // controlThatWasClicked.Focus(); + //} //else //{ // // This handles activations from clicks that did not start a size/move operation // ActivateConnection(); //} - break; - case NativeMethods.WM_NCACTIVATE: - //// Never allow the mRemoteNG window to display itself as inactive. By doing this, - //// we ensure focus events can propagate to child connection windows - NativeMethods.DefWindowProc(Handle, Convert.ToUInt32(m.Msg), (IntPtr)1, m.LParam); - m.Result = (IntPtr)1; - return; - case NativeMethods.WM_WINDOWPOSCHANGED: - // Ignore this message if the window wasn't activated - if (!_inMouseActivate) - break; - - var windowPos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.WINDOWPOS)); - if ((windowPos.flags & NativeMethods.SWP_NOACTIVATE) == 0) - { - if (!_inMouseActivate && !_inSizeMove) - { - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "WM_WINDOWPOSCHANGED DONE"); - _focusHelper.ActivateConnection(); - } - } - break; case NativeMethods.WM_SYSCOMMAND: var screen = _screenSystemMenu.GetScreenById(m.WParam.ToInt32());