Skip to content

Commit aa1f375

Browse files
committed
feat: Release the keyboard when mouse leaves the RDP window, close #648
1 parent e16114f commit aa1f375

File tree

2 files changed

+140
-35
lines changed

2 files changed

+140
-35
lines changed

Ui/Utils/Tracing/SentryIoHelper.cs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,27 @@ public static void Init(string dsn)
2222
return;
2323
if (dsn?.Length > 0 && dsn != "===REPLACE_ME_WITH_SENTRY_IO_DEN===")
2424
{
25-
SentrySdk.Init(options =>
25+
try
2626
{
27-
options.Dsn = dsn; // Tells which project in Sentry to send events to: "https://22803c6274b266bbfb78e060f774883d@o4508311925686272.ingest.us.sentry.io/4508311950852096"
27+
SentrySdk.Init(options =>
28+
{
29+
options.Dsn = dsn; // Tells which project in Sentry to send events to: "https://22803c6274b266bbfb78e060f774883d@o4508311925686272.ingest.us.sentry.io/4508311950852096"
2830
#if DEBUG
29-
options.Debug = true; // When configuring for the first time, to see what the SDK is doing:
31+
options.Debug = true; // When configuring for the first time, to see what the SDK is doing:
3032
#else
31-
options.Debug = false; // When configuring for the first time, to see what the SDK is doing:
33+
options.Debug = false; // When configuring for the first time, to see what the SDK is doing:
3234
#endif
33-
options.TracesSampleRate = 1.0; // Set TracesSampleRate to 1.0 to capture 100% of transactions for tracing.
34-
options.IsGlobalModeEnabled = true; // Enabling this option is recommended for client applications only. It ensures all threads use the same global scope.
35-
options.AutoSessionTracking = true; // This option is recommended. It enables Sentry's "Release Health" feature.
36-
});
37-
_hasInit = true;
38-
SimpleLogHelper.Debug(nameof(SentryIoHelper) + " init...");
35+
options.TracesSampleRate = 1.0; // Set TracesSampleRate to 1.0 to capture 100% of transactions for tracing.
36+
options.IsGlobalModeEnabled = true; // Enabling this option is recommended for client applications only. It ensures all threads use the same global scope.
37+
options.AutoSessionTracking = true; // This option is recommended. It enables Sentry's "Release Health" feature.
38+
});
39+
_hasInit = true;
40+
SimpleLogHelper.Debug(nameof(SentryIoHelper) + " init...");
41+
}
42+
catch (Exception e)
43+
{
44+
SimpleLogHelper.Warning(e);
45+
}
3946
}
4047
}
4148

Ui/View/Host/TabWindowView.xaml_timer.cs

Lines changed: 123 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
using System.Runtime.InteropServices;
22
using System.Timers;
33
using System;
4+
using System.Windows;
45
using System.Windows.Forms;
6+
using _1RM.Model.Protocol;
57
using Shawn.Utils;
8+
using Stylet;
69
using ProtocolHostType = _1RM.View.Host.ProtocolHosts.ProtocolHostType;
710
using Timer = System.Timers.Timer;
811

@@ -31,37 +34,14 @@ private void TimerDispose()
3134
}
3235
}
3336

34-
35-
36-
3737
private IntPtr _lastActivatedWindowHandle = IntPtr.Zero;
3838
private void Timer4CheckForegroundWindowOnElapsed(object? sender, ElapsedEventArgs e)
3939
{
4040
_timer4CheckForegroundWindow.Stop();
4141
try
4242
{
43-
if (Vm?.SelectedItem?.Content?.GetProtocolHostType() != ProtocolHostType.Integrate)
44-
return;
45-
46-
var hWnd = this.Vm.SelectedItem.Content.GetHostHwnd();
47-
if (hWnd == IntPtr.Zero) return;
48-
49-
var nowActivatedWindowHandle = GetForegroundWindow();
50-
51-
// bring Tab window to top, when the host content is Integrate.
52-
if (nowActivatedWindowHandle == hWnd && nowActivatedWindowHandle != _lastActivatedWindowHandle)
53-
{
54-
SimpleLogHelper.Debug($@"TabWindowView: _lastActivatedWindowHandle = ({_lastActivatedWindowHandle})
55-
TabWindowView: nowActivatedWindowHandle = ({nowActivatedWindowHandle}), hWnd = {hWnd}
56-
TabWindowView: BringWindowToTop({_myHandle})");
57-
BringWindowToTop(_myHandle);
58-
}
59-
// focus content when tab is focused and host is Integrate and left mouse is not pressed
60-
else if (nowActivatedWindowHandle == _myHandle && System.Windows.Forms.Control.MouseButtons != MouseButtons.Left)
61-
{
62-
Vm?.SelectedItem?.Content?.FocusOnMe();
63-
}
64-
_lastActivatedWindowHandle = nowActivatedWindowHandle;
43+
RunForRdp();
44+
RunForIntegrate();
6545
}
6646
catch (Exception ex)
6747
{
@@ -79,5 +59,123 @@ private void Timer4CheckForegroundWindowOnElapsed(object? sender, ElapsedEventAr
7959

8060
[DllImport("user32.dll")]
8161
private static extern IntPtr GetForegroundWindow();
62+
[DllImport("user32.dll")]
63+
private static extern bool SetForegroundWindow(IntPtr hWnd);
64+
65+
66+
private void RunForIntegrate()
67+
{
68+
if (Vm?.SelectedItem?.Content?.GetProtocolHostType() != ProtocolHostType.Integrate)
69+
return;
70+
71+
var hWnd = this.Vm.SelectedItem.Content.GetHostHwnd();
72+
if (hWnd == IntPtr.Zero) return;
73+
74+
var nowActivatedWindowHandle = GetForegroundWindow();
75+
76+
// bring Tab window to top, when the host content is Integrate.
77+
if (nowActivatedWindowHandle == hWnd && nowActivatedWindowHandle != _lastActivatedWindowHandle)
78+
{
79+
SimpleLogHelper.Debug($@"TabWindowView: _lastActivatedWindowHandle = ({_lastActivatedWindowHandle})
80+
TabWindowView: nowActivatedWindowHandle = ({nowActivatedWindowHandle}), hWnd = {hWnd}
81+
TabWindowView: BringWindowToTop({_myHandle})");
82+
BringWindowToTop(_myHandle);
83+
}
84+
// focus content when tab is focused and host is Integrate and left mouse is not pressed
85+
else if (nowActivatedWindowHandle == _myHandle && System.Windows.Forms.Control.MouseButtons != MouseButtons.Left)
86+
{
87+
Vm?.SelectedItem?.Content?.FocusOnMe();
88+
}
89+
_lastActivatedWindowHandle = nowActivatedWindowHandle;
90+
}
91+
92+
93+
#region RunForRdp
94+
95+
[StructLayout(LayoutKind.Sequential)]
96+
internal struct Win32Point
97+
{
98+
public Int32 X;
99+
public Int32 Y;
100+
};
101+
[DllImport("user32.dll")]
102+
[return: MarshalAs(UnmanagedType.Bool)]
103+
internal static extern bool GetCursorPos(ref Win32Point pt);
104+
private static Point GetMousePosition()
105+
{
106+
var w32Mouse = new Win32Point();
107+
GetCursorPos(ref w32Mouse);
108+
return new Point(w32Mouse.X, w32Mouse.Y);
109+
}
110+
[DllImport("user32.dll")]
111+
private static extern IntPtr GetDesktopWindow();
112+
113+
private static bool IsMouseInside(Window window)
114+
{
115+
Point mousePos = GetMousePosition();
116+
Point windowPos = new Point(-1, -1);
117+
Point windowBottomRight = new Point(-1, -1);
118+
Execute.OnUIThreadSync(() =>
119+
{
120+
windowPos = window.PointToScreen(new Point(0, 0));
121+
windowBottomRight = window.PointToScreen(new Point(window.Width, window.Height));
122+
});
123+
#if DEBUG
124+
var r = mousePos.X >= windowPos.X && mousePos.X <= windowBottomRight.X && mousePos.Y >= windowPos.Y &&
125+
mousePos.Y <= windowBottomRight.Y;
126+
SimpleLogHelper.Debug($@"TabWindowView IsMouseInside = {r}: mousePos = ({mousePos.X}, {mousePos.Y}), windowPos = ({windowPos.X}, {windowPos.Y}), windowBottomRight = ({windowBottomRight.X}, {windowBottomRight.Y})");
127+
#endif
128+
return mousePos.X >= windowPos.X && mousePos.X <= windowBottomRight.X && mousePos.Y >= windowPos.Y && mousePos.Y <= windowBottomRight.Y;
129+
}
130+
131+
132+
private int _rdpStage = 0; // 0 - not connected, 1 - RDP got focus, 2 - RDP lost focus desk got focus(focus can rollback to RDP), 3 - RDP lost focus desk lost focus (focus can not rollback to RDP)
133+
private void RunForRdp()
134+
{
135+
if (Vm?.SelectedItem?.Content?.ProtocolServer.Protocol != RDP.ProtocolName)
136+
return;
137+
if (Vm?.SelectedItem?.Content?.Status != ProtocolHosts.ProtocolHostStatus.Connected)
138+
return;
139+
140+
var nowActivatedWindowHandle = GetForegroundWindow();
141+
var desktopHandle = GetDesktopWindow();
142+
143+
#if DEBUG
144+
SimpleLogHelper.Debug($"tabHwnd = {_myHandle}, nowActivatedWindowHandle = {nowActivatedWindowHandle}, desktopHandle = {desktopHandle}");
145+
#endif
146+
147+
bool isMouseInside = IsMouseInside(this);
148+
149+
if (_rdpStage == 1 && !isMouseInside)
150+
{
151+
// 1 - RDP got focus AND mouse is not inside the tab window, then switch focus to desktop, user input will not be sent to RDP
152+
_rdpStage = 2;
153+
SetForegroundWindow(desktopHandle);
154+
}
155+
else if (_rdpStage == 2)
156+
{
157+
// if focus to other window, then stage = 3
158+
if (nowActivatedWindowHandle != desktopHandle)
159+
{
160+
_rdpStage = 3;
161+
}
162+
// mouse back to tab window, then focus back to RDP
163+
else if (isMouseInside)
164+
{
165+
SetForegroundWindow(_myHandle);
166+
_rdpStage = 1;
167+
}
168+
}
169+
else if (_rdpStage == 3)
170+
{
171+
// 3 - neither RDP nor local desk lost focus, can not rollback to RDP, do nothing
172+
}
173+
174+
if (_rdpStage != 1 && isMouseInside && _myHandle == nowActivatedWindowHandle)
175+
{
176+
_rdpStage = 1;
177+
}
178+
}
179+
#endregion
82180
}
83181
}

0 commit comments

Comments
 (0)