mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-17 14:07:46 +08:00
improved ability to determine if external proc has focus
This commit is contained in:
@@ -12,7 +12,7 @@ namespace mRemoteNGTests.Connection.Protocol
|
||||
{
|
||||
public class IntegratedProgramTests
|
||||
{
|
||||
private readonly ExternalTool _extTool = new ExternalTool(new ConnectionInitiator())
|
||||
private readonly ExternalTool _extTool = new ExternalTool(new ConnectionInitiator(new ProtocolFactory()))
|
||||
{
|
||||
DisplayName = "notepad",
|
||||
FileName = @"%windir%\system32\notepad.exe",
|
||||
@@ -50,7 +50,7 @@ namespace mRemoteNGTests.Connection.Protocol
|
||||
|
||||
private InterfaceControl BuildInterfaceControl(string extAppName, ProtocolBase sut)
|
||||
{
|
||||
var connectionWindow = new ConnectionWindow(new DockContent(), new ConnectionInitiator());
|
||||
var connectionWindow = new ConnectionWindow(new DockContent(), new ConnectionInitiator(new ProtocolFactory()));
|
||||
var connectionInfo = new ConnectionInfo {ExtApp = extAppName, Protocol = ProtocolType.IntApp};
|
||||
return new InterfaceControl(connectionWindow, sut, connectionInfo);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using mRemoteNG.Connection;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
using NUnit.Framework;
|
||||
using mRemoteNG.UI.Forms;
|
||||
|
||||
@@ -16,7 +17,7 @@ namespace mRemoteNGTests.UI.Forms
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_optionsForm = new FrmOptions(new ConnectionInitiator());
|
||||
_optionsForm = new FrmOptions(new ConnectionInitiator(new ProtocolFactory()));
|
||||
_optionsForm.Show();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Threading;
|
||||
using mRemoteNG.Connection;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
using mRemoteNG.UI.Window;
|
||||
using NUnit.Framework;
|
||||
using WeifenLuo.WinFormsUI.Docking;
|
||||
@@ -14,7 +15,7 @@ namespace mRemoteNGTests.UI.Window
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_connectionTreeWindow = new ConnectionTreeWindow(new DockContent(), new ConnectionInitiator());
|
||||
_connectionTreeWindow = new ConnectionTreeWindow(new DockContent(), new ConnectionInitiator(new ProtocolFactory()));
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
|
||||
@@ -16,12 +16,14 @@ namespace mRemoteNG.Connection
|
||||
{
|
||||
public class ConnectionInitiator : IConnectionInitiator
|
||||
{
|
||||
private readonly ProtocolFactory _protocolFactory;
|
||||
private readonly List<ProtocolBase> _activeConnections = new List<ProtocolBase>();
|
||||
|
||||
public IEnumerable<ProtocolBase> ActiveConnections => _activeConnections;
|
||||
|
||||
public ConnectionInitiator()
|
||||
public ConnectionInitiator(ProtocolFactory protocolFactory)
|
||||
{
|
||||
_protocolFactory = protocolFactory;
|
||||
}
|
||||
|
||||
public bool SwitchToOpenConnection(ConnectionInfo connectionInfo)
|
||||
@@ -35,6 +37,8 @@ namespace mRemoteNG.Connection
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void OpenConnection(
|
||||
ContainerInfo containerInfo,
|
||||
ConnectionInfo.Force force = ConnectionInfo.Force.None,
|
||||
@@ -77,8 +81,7 @@ namespace mRemoteNG.Connection
|
||||
return;
|
||||
}
|
||||
|
||||
var protocolFactory = new ProtocolFactory();
|
||||
var newProtocol = protocolFactory.CreateProtocol(connectionInfo);
|
||||
var newProtocol = _protocolFactory.CreateProtocol(connectionInfo);
|
||||
|
||||
var connectionPanel = SetConnectionPanel(connectionInfo, force);
|
||||
if (string.IsNullOrEmpty(connectionPanel)) return;
|
||||
@@ -96,6 +99,7 @@ namespace mRemoteNG.Connection
|
||||
return;
|
||||
}
|
||||
|
||||
OnConnectionStarting(newProtocol.InterfaceControl.Info, newProtocol);
|
||||
if (newProtocol.Connect() == false)
|
||||
{
|
||||
newProtocol.Close();
|
||||
@@ -225,11 +229,11 @@ namespace mRemoteNG.Connection
|
||||
}
|
||||
|
||||
Runtime.MessageCollector.AddMessage(msgClass,
|
||||
string.Format(
|
||||
Language.strProtocolEventDisconnected,
|
||||
disconnectedMessage,
|
||||
prot.InterfaceControl.Info.Hostname,
|
||||
prot.InterfaceControl.Info.Protocol.ToString()));
|
||||
string.Format(
|
||||
Language.strProtocolEventDisconnected,
|
||||
disconnectedMessage,
|
||||
prot.InterfaceControl.Info.Hostname,
|
||||
prot.InterfaceControl.Info.Protocol.ToString()));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -271,7 +275,7 @@ namespace mRemoteNG.Connection
|
||||
}
|
||||
}
|
||||
|
||||
private static void Prot_Event_Connected(object sender)
|
||||
private void Prot_Event_Connected(object sender)
|
||||
{
|
||||
var prot = (ProtocolBase)sender;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.strConnectionEventConnected,
|
||||
@@ -304,5 +308,11 @@ namespace mRemoteNG.Connection
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public event EventHandler<ConnectionStartingEvent> ConnectionStarting;
|
||||
protected virtual void OnConnectionStarting(ConnectionInfo connectionInfo, ProtocolBase protocolBase)
|
||||
{
|
||||
ConnectionStarting?.Invoke(this, new ConnectionStartingEvent(connectionInfo, protocolBase));
|
||||
}
|
||||
}
|
||||
}
|
||||
18
mRemoteV1/Connection/ConnectionStartingEvent.cs
Normal file
18
mRemoteV1/Connection/ConnectionStartingEvent.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
|
||||
namespace mRemoteNG.Connection
|
||||
{
|
||||
[Serializable]
|
||||
public class ConnectionStartingEvent : EventArgs
|
||||
{
|
||||
public ConnectionInfo ConnectionInfo { get; }
|
||||
public ProtocolBase Protocol { get; }
|
||||
|
||||
public ConnectionStartingEvent(ConnectionInfo connectionInfo, ProtocolBase protocol)
|
||||
{
|
||||
ConnectionInfo = connectionInfo;
|
||||
Protocol = protocol;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
using mRemoteNG.Container;
|
||||
using mRemoteNG.UI.Window;
|
||||
@@ -20,5 +21,7 @@ namespace mRemoteNG.Connection
|
||||
ConnectionWindow conForm = null);
|
||||
|
||||
bool SwitchToOpenConnection(ConnectionInfo connectionInfo);
|
||||
|
||||
event EventHandler<ConnectionStartingEvent> ConnectionStarting;
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,11 @@ using mRemoteNG.Messages;
|
||||
|
||||
namespace mRemoteNG.Connection.Protocol
|
||||
{
|
||||
public class ExternalProcessProtocolBase : ProtocolBase
|
||||
public abstract class ExternalProcessProtocolBase : ProtocolBase, IFocusable
|
||||
{
|
||||
private IntPtr _winEventHook;
|
||||
private NativeMethods.WinEventDelegate _setForegroundDelegate;
|
||||
private bool _hasFocus;
|
||||
|
||||
public override bool IsExternalProcess { get; } = true;
|
||||
|
||||
@@ -16,6 +17,26 @@ namespace mRemoteNG.Connection.Protocol
|
||||
|
||||
protected IntPtr ProcessHandle { get; set; }
|
||||
|
||||
public bool HasFocus
|
||||
{
|
||||
get => _hasFocus;
|
||||
private set
|
||||
{
|
||||
if (_hasFocus == value)
|
||||
return;
|
||||
|
||||
_hasFocus = value;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
string.Format("External protocol window set to {0}. name:'{1}', protocol:'{2}', pid:{3}, hwnd:{4}",
|
||||
_hasFocus ? "foreground" : "background",
|
||||
InterfaceControl.Info.Name,
|
||||
InterfaceControl.Info.Protocol,
|
||||
ProtocolProcess.Id,
|
||||
ProcessHandle));
|
||||
OnFocusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public int ThreadId => (int)NativeMethods.GetWindowThreadProcessId(ProcessHandle, IntPtr.Zero);
|
||||
|
||||
|
||||
@@ -27,7 +48,7 @@ namespace mRemoteNG.Connection.Protocol
|
||||
NativeMethods.EVENT_SYSTEM_FOREGROUND,
|
||||
IntPtr.Zero,
|
||||
_setForegroundDelegate,
|
||||
Convert.ToUInt32(ProtocolProcess.Id),
|
||||
/*Convert.ToUInt32(ProtocolProcess.Id)*/0,
|
||||
0,
|
||||
NativeMethods.WINEVENT_OUTOFCONTEXT);
|
||||
|
||||
@@ -63,16 +84,15 @@ namespace mRemoteNG.Connection.Protocol
|
||||
|
||||
var setForegroundSuccessful = NativeMethods.SetForegroundWindow(ProcessHandle);
|
||||
|
||||
var logMsg = setForegroundSuccessful
|
||||
? "External protocol window set to foreground. "
|
||||
: "Failed to set external protocol window to foreground. ";
|
||||
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
logMsg +
|
||||
$"name:'{InterfaceControl.Info.Name}', " +
|
||||
$"protocol:'{InterfaceControl.Info.Protocol}', " +
|
||||
$"pid:{ProtocolProcess.Id}, " +
|
||||
$"hwnd:{ProcessHandle}");
|
||||
if (!setForegroundSuccessful)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.WarningMsg,
|
||||
"Failed to set external protocol window to foreground. " +
|
||||
$"name:'{InterfaceControl.Info.Name}', " +
|
||||
$"protocol:'{InterfaceControl.Info.Protocol}', " +
|
||||
$"pid:{ProtocolProcess.Id}, " +
|
||||
$"hwnd:{ProcessHandle}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -88,14 +108,13 @@ namespace mRemoteNG.Connection.Protocol
|
||||
/// <param name="dwmsEventTime"></param>
|
||||
void OnWinEventSetForeground(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
|
||||
{
|
||||
if (hwnd != ProtocolProcess.MainWindowHandle)
|
||||
return;
|
||||
HasFocus = hwnd == ProcessHandle;
|
||||
}
|
||||
|
||||
//Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
// "Exernal protocol window set to foreground. " +
|
||||
// $"protocol:{InterfaceControl.Info.Protocol} " +
|
||||
// $"pid:{ProtocolProcess.Id}, " +
|
||||
// $"hwnd:{ProtocolProcess.MainWindowHandle}");
|
||||
public event EventHandler FocusChanged;
|
||||
protected virtual void OnFocusChanged()
|
||||
{
|
||||
FocusChanged?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
mRemoteV1/Connection/Protocol/IFocusable.cs
Normal file
11
mRemoteV1/Connection/Protocol/IFocusable.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace mRemoteNG.Connection.Protocol
|
||||
{
|
||||
public interface IFocusable
|
||||
{
|
||||
bool HasFocus { get; }
|
||||
void Focus();
|
||||
event EventHandler FocusChanged;
|
||||
}
|
||||
}
|
||||
@@ -341,8 +341,7 @@ namespace mRemoteNG.Connection.Protocol
|
||||
{
|
||||
if (!disposing) return;
|
||||
|
||||
if(tmrReconnect != null)
|
||||
tmrReconnect.Dispose();
|
||||
tmrReconnect?.Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
using System;
|
||||
using mRemoteNG.App;
|
||||
using mRemoteNG.Messages;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace mRemoteNG.UI.FocusHelpers
|
||||
{
|
||||
public class ExternalProcessAltTabFocusHelper : IDisposable
|
||||
{
|
||||
private readonly SystemKeyboardHook _keyboardHook;
|
||||
private bool _currentlyFixingAltTab;
|
||||
|
||||
/// <summary>
|
||||
/// TRUE if any part of mrng has focus - the main window or child processes
|
||||
/// </summary>
|
||||
public bool MrngFocused { get; set; }
|
||||
public bool ChildProcessHeldLastFocus { get; set; }
|
||||
public Action FocusMainWindowAction { get; }
|
||||
public Action FocusConnectionAction { get; }
|
||||
|
||||
public ExternalProcessAltTabFocusHelper(Action focusMainWindowAction, Action focusConnectionAction)
|
||||
{
|
||||
FocusMainWindowAction = focusMainWindowAction;
|
||||
FocusConnectionAction = focusConnectionAction;
|
||||
_keyboardHook = new SystemKeyboardHook(KeyboardHookCallback);
|
||||
}
|
||||
|
||||
public int KeyboardHookCallback(int msg, NativeMethods.KBDLLHOOKSTRUCT kbd)
|
||||
{
|
||||
var key = (Keys)kbd.vkCode;
|
||||
if (key.HasFlag(Keys.Tab) && kbd.flags.HasFlag(NativeMethods.KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN))
|
||||
{
|
||||
if (msg == NativeMethods.WM_SYSKEYDOWN || msg == NativeMethods.WM_KEYDOWN)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"ALT-TAB PRESSED (CHILDPROC_FOCUSED={ChildProcessHeldLastFocus}, MRNG_FOCUSED={MrngFocused}, CURR_FIXING={_currentlyFixingAltTab})");
|
||||
if (ChildProcessHeldLastFocus && MrngFocused && !_currentlyFixingAltTab)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "FIXING ALT-TAB FOR EXTAPP");
|
||||
_currentlyFixingAltTab = true;
|
||||
|
||||
// simulate an extra TAB key press. This skips focus of the mrng main window.
|
||||
NativeMethods.keybd_event((byte)Keys.Tab, 0, (uint)NativeMethods.KEYEVENTF.KEYUP, 0);
|
||||
NativeMethods.keybd_event((byte)Keys.Tab, 0, 0, 0);
|
||||
|
||||
// WndProc will never get an event when we switch from a child proc to a completely different program since the main mrng window never had focus to begin with.
|
||||
// Assume mrng as a whole will lose focus, even though the user could choose to retain focus on us. When Alt-tab completes, the mrng main window will
|
||||
// receive the focus event and we will handle the child process focusing as necessary.
|
||||
MrngFocused = false;
|
||||
_currentlyFixingAltTab = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// alt + right-shift
|
||||
if (key.HasFlag(Keys.RShiftKey) && kbd.flags.HasFlag(NativeMethods.KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN))
|
||||
{
|
||||
if (msg != NativeMethods.WM_SYSKEYUP && msg != NativeMethods.WM_KEYUP)
|
||||
return 0;
|
||||
|
||||
if (!MrngFocused)
|
||||
return 0;
|
||||
|
||||
if (ChildProcessHeldLastFocus)
|
||||
{
|
||||
FocusMainWindowAction();
|
||||
return 1;
|
||||
}
|
||||
|
||||
FocusConnectionAction();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_keyboardHook?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
199
mRemoteV1/UI/FocusHelpers/ExternalProcessFocusHelper.cs
Normal file
199
mRemoteV1/UI/FocusHelpers/ExternalProcessFocusHelper.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using System;
|
||||
using System.Windows.Forms;
|
||||
using mRemoteNG.App;
|
||||
using mRemoteNG.Connection;
|
||||
using mRemoteNG.Connection.Protocol;
|
||||
using mRemoteNG.Messages;
|
||||
using mRemoteNG.UI.Tabs;
|
||||
|
||||
namespace mRemoteNG.UI.FocusHelpers
|
||||
{
|
||||
public class ExternalProcessFocusHelper : IDisposable
|
||||
{
|
||||
private readonly IntPtr _mainWindowHandle;
|
||||
private int _extFocusCount;
|
||||
private readonly SystemKeyboardHook _keyboardHook;
|
||||
private bool _currentlyFixingAltTab;
|
||||
private bool _childProcessHeldLastFocus;
|
||||
private bool _mainWindowFocused;
|
||||
private bool _connectionReleasingFocus;
|
||||
|
||||
/// <summary>
|
||||
/// TRUE if any part of mrng has focus - the main window or child processes
|
||||
/// </summary>
|
||||
public bool MrngFocused => MainWindowFocused || ChildProcessFocused;
|
||||
|
||||
public bool FixingMainWindowFocus { get; private set; }
|
||||
|
||||
public bool MainWindowFocused
|
||||
{
|
||||
get => _mainWindowFocused;
|
||||
set
|
||||
{
|
||||
if (_mainWindowFocused == value)
|
||||
return;
|
||||
|
||||
_mainWindowFocused = value;
|
||||
// main window is receiving focus
|
||||
if (ChildProcessHeldLastFocus && _mainWindowFocused && !_connectionReleasingFocus)
|
||||
ActivateConnection();
|
||||
}
|
||||
}
|
||||
|
||||
public bool ChildProcessFocused => _extFocusCount > 0;
|
||||
|
||||
public bool ChildProcessHeldLastFocus
|
||||
{
|
||||
get => _childProcessHeldLastFocus;
|
||||
private set
|
||||
{
|
||||
_childProcessHeldLastFocus = value;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"_childProcessHeldLastFocus={_childProcessHeldLastFocus}");
|
||||
}
|
||||
}
|
||||
|
||||
public ExternalProcessFocusHelper(
|
||||
IConnectionInitiator connectionInitiator,
|
||||
IntPtr mainWindowHandle)
|
||||
{
|
||||
connectionInitiator.ConnectionStarting += ConnectionInitiatorOnConnectionStarting;
|
||||
_mainWindowHandle = mainWindowHandle;
|
||||
_keyboardHook = new SystemKeyboardHook(KeyboardHookCallback);
|
||||
}
|
||||
|
||||
private void ConnectionInitiatorOnConnectionStarting(object sender, ConnectionStartingEvent e)
|
||||
{
|
||||
if (!(e.Protocol is ExternalProcessProtocolBase extProc))
|
||||
return;
|
||||
|
||||
extProc.FocusChanged += ExtProcOnFocusChanged;
|
||||
extProc.Disconnected += ProtocolOnDisconnected;
|
||||
}
|
||||
|
||||
private void ExtProcOnFocusChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (!(sender is IFocusable extProc))
|
||||
return;
|
||||
|
||||
if (extProc.HasFocus)
|
||||
ExternalWindowFocused();
|
||||
else
|
||||
ExternalWindowDefocused();
|
||||
}
|
||||
|
||||
private void ProtocolOnDisconnected(object sender, string disconnectedmessage, int? reasoncode)
|
||||
{
|
||||
if (!(sender is ExternalProcessProtocolBase prot))
|
||||
return;
|
||||
|
||||
prot.FocusChanged -= ExtProcOnFocusChanged;
|
||||
prot.Disconnected -= ProtocolOnDisconnected;
|
||||
}
|
||||
|
||||
public void ActivateConnection()
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Performing special connection focus logic");
|
||||
|
||||
//var cw = pnlDock.ActiveDocument as ConnectionWindow;
|
||||
//var dp = cw?.ActiveControl as DockPane;
|
||||
|
||||
//if (!(dp?.ActiveContent is ConnectionTab tab))
|
||||
//{
|
||||
// Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Active content is not a tab. We won't focus a specific connection.");
|
||||
// return;
|
||||
//}
|
||||
|
||||
//var ifc = InterfaceControl.FindInterfaceControl(tab);
|
||||
var tab = TabHelper.Instance.CurrentTab;
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
var ifc = tab.InterfaceControl;
|
||||
if (ifc == null)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
|
||||
$"InterfaceControl for tab '{tab.Name}' was not found. We won't focus that connection.");
|
||||
return;
|
||||
}
|
||||
|
||||
//FixingMainWindowFocus = true;
|
||||
//_focusMainWindowAction();
|
||||
//FixingMainWindowFocus = false;
|
||||
|
||||
ifc.Protocol.Focus();
|
||||
//var conFormWindow = ifc.FindForm();
|
||||
//((ConnectionTab) conFormWindow)?.RefreshInterfaceController();
|
||||
}
|
||||
|
||||
public int KeyboardHookCallback(int msg, NativeMethods.KBDLLHOOKSTRUCT kbd)
|
||||
{
|
||||
var key = (Keys)kbd.vkCode;
|
||||
// Alt + Tab
|
||||
if (key.HasFlag(Keys.Tab) && kbd.flags.HasFlag(NativeMethods.KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN))
|
||||
{
|
||||
if (msg == NativeMethods.WM_SYSKEYDOWN || msg == NativeMethods.WM_KEYDOWN)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"ALT-TAB PRESSED (CHILDPROC_FOCUSED={ChildProcessFocused}, CHILDPROC_LAST_FOCUSED={ChildProcessHeldLastFocus}, MRNG_FOCUSED={MrngFocused}, CURR_FIXING={_currentlyFixingAltTab})");
|
||||
if (ChildProcessHeldLastFocus && MrngFocused && !_currentlyFixingAltTab)
|
||||
{
|
||||
FixExternalAppAltTab();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 (ChildProcessFocused)
|
||||
{
|
||||
_connectionReleasingFocus = true;
|
||||
var focused = NativeMethods.SetForegroundWindow(_mainWindowHandle);
|
||||
_connectionReleasingFocus = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!MainWindowFocused)
|
||||
return 0;
|
||||
ActivateConnection();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void FixExternalAppAltTab()
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "FIXING ALT-TAB FOR EXTAPP");
|
||||
_currentlyFixingAltTab = true;
|
||||
|
||||
// simulate an extra TAB key press. This skips focus of the mrng main window.
|
||||
NativeMethods.keybd_event((byte) Keys.Tab, 0, (uint) NativeMethods.KEYEVENTF.KEYUP, 0);
|
||||
NativeMethods.keybd_event((byte) Keys.Tab, 0, 0, 0);
|
||||
|
||||
// WndProc will never get an event when we switch from a child proc to a completely different program since the main mrng window never had focus to begin with.
|
||||
// Assume mrng as a whole will lose focus, even though the user could choose to retain focus on us. When Alt-tab completes, the mrng main window will
|
||||
// receive the focus event and we will handle the child process focusing as necessary.
|
||||
MainWindowFocused = false;
|
||||
_currentlyFixingAltTab = false;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_keyboardHook?.Dispose();
|
||||
}
|
||||
|
||||
private void ExternalWindowFocused()
|
||||
{
|
||||
_extFocusCount++;
|
||||
}
|
||||
|
||||
private void ExternalWindowDefocused()
|
||||
{
|
||||
_extFocusCount--;
|
||||
ChildProcessHeldLastFocus = !MainWindowFocused && !ChildProcessFocused;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +53,8 @@ namespace mRemoteNG.UI.Forms
|
||||
private readonly IList<IMessageWriter> _messageWriters = new List<IMessageWriter>();
|
||||
private readonly ThemeManager _themeManager;
|
||||
private readonly FileBackupPruner _backupPruner = new FileBackupPruner();
|
||||
private readonly IConnectionInitiator _connectionInitiator = new ConnectionInitiator();
|
||||
private readonly ExternalProcessAltTabFocusHelper _altTabFocusHelper;
|
||||
private readonly IConnectionInitiator _connectionInitiator;
|
||||
private readonly ExternalProcessFocusHelper _focusHelper;
|
||||
|
||||
internal FullscreenHandler Fullscreen { get; set; }
|
||||
|
||||
@@ -73,7 +73,11 @@ namespace mRemoteNG.UI.Forms
|
||||
ApplyTheme();
|
||||
|
||||
_screenSystemMenu = new ScreenSelectionSystemMenu(this);
|
||||
_altTabFocusHelper = new ExternalProcessAltTabFocusHelper(() => Focus(), ActivateConnection);
|
||||
var protocolFactory = new ProtocolFactory();
|
||||
_connectionInitiator = new ConnectionInitiator(protocolFactory);
|
||||
|
||||
Debug.Assert(IsHandleCreated, "Expected main window handle to be created by now");
|
||||
_focusHelper = new ExternalProcessFocusHelper(_connectionInitiator, Handle);
|
||||
}
|
||||
|
||||
#region Properties
|
||||
@@ -218,12 +222,12 @@ namespace mRemoteNG.UI.Forms
|
||||
|
||||
private void OnTabClicked(object sender, EventArgs e)
|
||||
{
|
||||
ActivateConnection();
|
||||
_focusHelper.ActivateConnection();
|
||||
}
|
||||
|
||||
private void OnActiveConnectionTabChanged(object sender, EventArgs e)
|
||||
{
|
||||
ActivateConnection();
|
||||
_focusHelper.ActivateConnection();
|
||||
}
|
||||
|
||||
private void ApplyLanguage()
|
||||
@@ -383,7 +387,7 @@ namespace mRemoteNG.UI.Forms
|
||||
|
||||
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
|
||||
{
|
||||
_altTabFocusHelper?.Dispose();
|
||||
_focusHelper?.Dispose();
|
||||
|
||||
if (!(Runtime.WindowList == null || Runtime.WindowList.Count == 0))
|
||||
{
|
||||
@@ -483,7 +487,7 @@ namespace mRemoteNG.UI.Forms
|
||||
_inSizeMove = false;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "End app window move/resize");
|
||||
// This handles activations from clicks that started a size/move operation
|
||||
ActivateConnection();
|
||||
_focusHelper.ActivateConnection();
|
||||
}
|
||||
|
||||
|
||||
@@ -510,28 +514,19 @@ namespace mRemoteNG.UI.Forms
|
||||
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
|
||||
{
|
||||
var threadWhichIsActivating = m.LParam.ToInt32();
|
||||
_altTabFocusHelper.ChildProcessHeldLastFocus = _connectionInitiator
|
||||
.ActiveConnections
|
||||
.OfType<ExternalProcessProtocolBase>()
|
||||
.Any(proc => proc.ThreadId == threadWhichIsActivating);
|
||||
|
||||
_inMouseActivate = false;
|
||||
_altTabFocusHelper.MrngFocused = _altTabFocusHelper.ChildProcessHeldLastFocus;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window lost focus (_childProcessHeldLastFocus={_altTabFocusHelper.ChildProcessHeldLastFocus})");
|
||||
_focusHelper.MainWindowFocused = false;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window lost focus (_childProcessHeldLastFocus={_focusHelper.ChildProcessHeldLastFocus})");
|
||||
break;
|
||||
}
|
||||
|
||||
if (_altTabFocusHelper.ChildProcessHeldLastFocus && !_altTabFocusHelper.MrngFocused)
|
||||
{
|
||||
ActivateConnection();
|
||||
}
|
||||
|
||||
_altTabFocusHelper.ChildProcessHeldLastFocus = false;
|
||||
_altTabFocusHelper.MrngFocused = true;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window received focus (_childProcessHeldLastFocus={_altTabFocusHelper.ChildProcessHeldLastFocus})");
|
||||
_focusHelper.MainWindowFocused = true;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window received focus (_childProcessHeldLastFocus={_focusHelper.ChildProcessHeldLastFocus})");
|
||||
|
||||
//var candidateTabToFocus = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition))
|
||||
// ?? GetChildAtPoint(MousePosition);
|
||||
@@ -583,14 +578,10 @@ namespace mRemoteNG.UI.Forms
|
||||
|
||||
break;
|
||||
case NativeMethods.WM_NCACTIVATE:
|
||||
//if (m.WParam.ToInt32() == 1)
|
||||
// break;
|
||||
|
||||
//// 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;
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Fixed main app NCACTIVATE");
|
||||
return;
|
||||
case NativeMethods.WM_WINDOWPOSCHANGED:
|
||||
// Ignore this message if the window wasn't activated
|
||||
@@ -603,7 +594,7 @@ namespace mRemoteNG.UI.Forms
|
||||
if (!_inMouseActivate && !_inSizeMove)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "WM_WINDOWPOSCHANGED DONE");
|
||||
ActivateConnection();
|
||||
_focusHelper.ActivateConnection();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -643,38 +634,9 @@ namespace mRemoteNG.UI.Forms
|
||||
clientMousePosition.Y = temp_wHigh;
|
||||
}
|
||||
|
||||
private void ActivateConnection()
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Performing special connection focus logic");
|
||||
//var cw = pnlDock.ActiveDocument as ConnectionWindow;
|
||||
//var dp = cw?.ActiveControl as DockPane;
|
||||
|
||||
//if (!(dp?.ActiveContent is ConnectionTab tab))
|
||||
//{
|
||||
// Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Active content is not a tab. We won't focus a specific connection.");
|
||||
// return;
|
||||
//}
|
||||
|
||||
//var ifc = InterfaceControl.FindInterfaceControl(tab);
|
||||
var tab = TabHelper.Instance.CurrentTab;
|
||||
if (tab == null)
|
||||
return;
|
||||
|
||||
var ifc = tab.InterfaceControl;
|
||||
if (ifc == null)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"InterfaceControl for tab '{tab.Name}' was not found. We won't focus that connection.");
|
||||
return;
|
||||
}
|
||||
|
||||
ifc.Protocol.Focus();
|
||||
var conFormWindow = ifc.FindForm();
|
||||
((ConnectionTab)conFormWindow)?.RefreshInterfaceController();
|
||||
}
|
||||
|
||||
private void pnlDock_ActiveDocumentChanged(object sender, EventArgs e)
|
||||
{
|
||||
//ActivateConnection();
|
||||
//_focusHelper.ActivateConnection();
|
||||
}
|
||||
|
||||
internal void UpdateWindowTitle()
|
||||
|
||||
@@ -48,16 +48,7 @@ namespace mRemoteNG.UI.Tabs
|
||||
private void ConnectionTab_GotFocus(object sender, EventArgs e)
|
||||
{
|
||||
Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Tab received focused: '{TabText}'");
|
||||
//TabHelper.Instance.CurrentTab = this;
|
||||
//if (TabHelper.Instance.FocusConnection)
|
||||
//{
|
||||
// Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Focusing connection in tab: '{TabText}'");
|
||||
// InterfaceControl?.Protocol.Focus();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Dont focus connection");
|
||||
//}
|
||||
TabHelper.Instance.CurrentTab = this;
|
||||
}
|
||||
|
||||
protected override void OnFormClosing(FormClosingEventArgs e)
|
||||
|
||||
@@ -134,7 +134,7 @@ namespace mRemoteNG.UI.Window
|
||||
|
||||
var conTab = new ConnectionTab
|
||||
{
|
||||
Tag = connectionInfo,
|
||||
Tag = connectionInfo, // BUG: the Tag gets set to an InterfaceControl later on. Is this right?
|
||||
DockAreas = DockAreas.Document | DockAreas.Float,
|
||||
Icon = ConnectionIcon.FromString(connectionInfo.Icon),
|
||||
TabText = titleText,
|
||||
|
||||
@@ -252,6 +252,7 @@
|
||||
<Compile Include="App\ProgramRoot.cs" />
|
||||
<Compile Include="Connection\ConnectionInitiator.cs" />
|
||||
<Compile Include="Connection\ConnectionsService.cs" />
|
||||
<Compile Include="Connection\ConnectionStartingEvent.cs" />
|
||||
<Compile Include="Connection\Converter.cs" />
|
||||
<Compile Include="Connection\DefaultConnectionInfo.cs" />
|
||||
<Compile Include="Connection\DefaultConnectionInheritance.cs" />
|
||||
@@ -260,6 +261,7 @@
|
||||
<Compile Include="Connection\IHasParent.cs" />
|
||||
<Compile Include="Connection\Protocol\ExternalProcessProtocolBase.cs" />
|
||||
<Compile Include="Connection\Protocol\Http\Connection.Protocol.HTTPS.CertEvent.cs" />
|
||||
<Compile Include="Connection\Protocol\IFocusable.cs" />
|
||||
<Compile Include="Connection\Protocol\ProtocolFactory.cs" />
|
||||
<Compile Include="Connection\Protocol\RDP\AuthenticationLevel.cs" />
|
||||
<Compile Include="Connection\Protocol\RDP\AzureLoadBalanceInfoEncoder.cs" />
|
||||
@@ -538,7 +540,7 @@
|
||||
</Compile>
|
||||
<Compile Include="UI\DialogFactory.cs" />
|
||||
<Compile Include="UI\DisplayProperties.cs" />
|
||||
<Compile Include="UI\FocusHelpers\ExternalProcessAltTabFocusHelper.cs" />
|
||||
<Compile Include="UI\FocusHelpers\ExternalProcessFocusHelper.cs" />
|
||||
<Compile Include="UI\FontOverrider.cs" />
|
||||
<Compile Include="UI\FormExtensions.cs" />
|
||||
<Compile Include="UI\Forms\FrmOptions.cs">
|
||||
|
||||
Reference in New Issue
Block a user