diff --git a/IPCamAppBar/CameraView.Designer.cs b/IPCamAppBar/CameraView.Designer.cs index de2c2e3..3461427 100644 --- a/IPCamAppBar/CameraView.Designer.cs +++ b/IPCamAppBar/CameraView.Designer.cs @@ -28,10 +28,13 @@ /// private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CameraView)); this.ConnectingLabel = new System.Windows.Forms.Label(); this.StreamView = new System.Windows.Forms.PictureBox(); this.IssueLabel = new System.Windows.Forms.Label(); + this.PausedOverlay = new System.Windows.Forms.PictureBox(); ((System.ComponentModel.ISupportInitialize)(this.StreamView)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.PausedOverlay)).BeginInit(); this.SuspendLayout(); // // ConnectingLabel @@ -71,17 +74,30 @@ this.IssueLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; this.IssueLabel.Visible = false; // + // PausedOverlay + // + this.PausedOverlay.BackColor = System.Drawing.Color.Transparent; + this.PausedOverlay.Image = ((System.Drawing.Image)(resources.GetObject("PausedOverlay.Image"))); + this.PausedOverlay.Location = new System.Drawing.Point(19, 40); + this.PausedOverlay.Name = "PausedOverlay"; + this.PausedOverlay.Size = new System.Drawing.Size(64, 64); + this.PausedOverlay.TabIndex = 3; + this.PausedOverlay.TabStop = false; + this.PausedOverlay.Visible = false; + // // CameraView // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BackColor = System.Drawing.Color.Gray; + this.Controls.Add(this.PausedOverlay); this.Controls.Add(this.IssueLabel); this.Controls.Add(this.StreamView); this.Controls.Add(this.ConnectingLabel); this.Margin = new System.Windows.Forms.Padding(0); this.Name = "CameraView"; ((System.ComponentModel.ISupportInitialize)(this.StreamView)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.PausedOverlay)).EndInit(); this.ResumeLayout(false); } @@ -91,5 +107,6 @@ private System.Windows.Forms.Label ConnectingLabel; private System.Windows.Forms.PictureBox StreamView; private System.Windows.Forms.Label IssueLabel; + private System.Windows.Forms.PictureBox PausedOverlay; } } diff --git a/IPCamAppBar/CameraView.cs b/IPCamAppBar/CameraView.cs index 4446ee0..05b643b 100644 --- a/IPCamAppBar/CameraView.cs +++ b/IPCamAppBar/CameraView.cs @@ -1,6 +1,7 @@ using System; using System.Drawing; using System.Drawing.Drawing2D; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; @@ -11,32 +12,50 @@ namespace IPCamAppBar { public partial class CameraView : UserControl, IRetryableCameraObserver { + private readonly ICamera camera; private readonly bool overlayDateTime; private Bitmap viewBitmap; - + private CancellationTokenSource streamCancellationTokenSource; + public CameraView(ICamera camera, bool overlayDateTime) { + this.camera = camera; this.overlayDateTime = overlayDateTime; InitializeComponent(); - var streamCancellationTokenSource = new CancellationTokenSource(); + StartCamera(); + + Disposed += (_, _) => + { + StopCamera(); + }; + } + + private void StartCamera() + { + streamCancellationTokenSource = new CancellationTokenSource(); + Task.Run(async () => { var retryableCamera = new RetryableCamera(camera); await retryableCamera.Fetch(this, streamCancellationTokenSource.Token); }); - - Disposed += (_, _) => - { - streamCancellationTokenSource.Cancel(); - }; + } + + + private void StopCamera() + { + streamCancellationTokenSource.Cancel(); } private Bitmap resizedBitmap; - + public Task OnFrame(Image image) { + if (streamCancellationTokenSource.IsCancellationRequested) + return Task.CompletedTask; + if (overlayDateTime || image is not Bitmap bitmap || image.Width != Width || image.Height != Height) { resizedBitmap ??= new Bitmap(Width, Height); @@ -100,6 +119,7 @@ namespace IPCamAppBar ConnectingLabel.Visible = false; StreamView.Visible = true; IssueLabel.Visible = false; + PausedOverlay.Visible = false; if (viewBitmap == null) @@ -130,12 +150,28 @@ namespace IPCamAppBar private void InvokeDisconnected(Exception exception, TimeSpan retryDelay) { - if (IsDisposed) + if (IsDisposed || streamCancellationTokenSource.IsCancellationRequested) return; IssueLabel.Text = (exception?.Message ?? "Camera disconnected") + $", retrying in {retryDelay.TotalSeconds} seconds"; IssueLabel.Visible = true; IssueLabel.BringToFront(); } + + + public void SetPaused(bool pause) + { + if (pause) + { + StopCamera(); + + PausedOverlay.Left = (Width - PausedOverlay.Width) / 2; + PausedOverlay.Top = (Height - PausedOverlay.Height) / 2; + PausedOverlay.Visible = true; + PausedOverlay.BringToFront(); + } + else + StartCamera(); + } } } diff --git a/IPCamAppBar/CameraView.resx b/IPCamAppBar/CameraView.resx index 1af7de1..efbee42 100644 --- a/IPCamAppBar/CameraView.resx +++ b/IPCamAppBar/CameraView.resx @@ -117,4 +117,18 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m + dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAF8SURBVHhetc8xCsAAEALB/P/TF9PaSZiBrcXnvG/j + TxQfiD60RvGB6ENrFB+IPrRG8YHoQ2sUH4g+tEbxgehDaxQfiD60RvGB6ENrFB+IPrRG8YHoQ2sUH4g+ + tEbxgehDaxQfiD60RvGB6ENrFB+IPrRG8YHoQ2sUH4g+tEbxgehDaxQfiD60RvGB6ENrFB+IPrRG8YHo + Q2sUH4g+tEbxgehDaxQfiD60RvGB6ENrFB+IPrRG8YHoQ2sUH4g+tEbxgehDaxQfiD60RvGB6ENrFB+I + PrRG8YHoQ2sUH4g+tEbxgehDaxQfiD60RvGB6ENrFB+IPrRG8YHoQ2sUH4g+tEbxgehDaxQfiD60RvGB + 6ENrFB+IPrRG8YHoQ2sUH4g+tEbxgehDaxQfiD60RvGB6ENrFB+IPrRG8YHoQ2sUH4g+tEbxgehDaxQf + iD60RvGB6ENrFB+IPrRG8YHoQ2sUH4g+tEbxgehDaxQfiD60RvGB6ENrFB+IPrQG3b1Awcd2Z2RJDQAA + AABJRU5ErkJggg== + + \ No newline at end of file diff --git a/IPCamAppBar/MainForm.cs b/IPCamAppBar/MainForm.cs index 66be7a0..f8d1977 100644 --- a/IPCamAppBar/MainForm.cs +++ b/IPCamAppBar/MainForm.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Windows.Forms; using IPCamLib; using IPCamLib.Concrete; +using Microsoft.Win32; using Newtonsoft.Json; namespace IPCamAppBar @@ -13,6 +14,7 @@ namespace IPCamAppBar { private readonly Config config; private readonly AppBar appBar; + private bool paused = false; public MainForm() @@ -39,6 +41,9 @@ namespace IPCamAppBar : FlowDirection.LeftToRight; config.Cameras?.ForEach(AddCamera); + + + SystemEvents.SessionSwitch += SystemEventsOnSessionSwitch; } @@ -65,15 +70,14 @@ namespace IPCamAppBar private static AppBarPosition GetAppBarPosition(ConfigSide side) { - switch (side) + return side switch { - case ConfigSide.Top: return AppBarPosition.Top; - case ConfigSide.Bottom: return AppBarPosition.Bottom; - case ConfigSide.Left: return AppBarPosition.Left; - case ConfigSide.Right: return AppBarPosition.Right; - default: - throw new ArgumentOutOfRangeException(nameof(side), side, @"Invalid side value"); - } + ConfigSide.Top => AppBarPosition.Top, + ConfigSide.Bottom => AppBarPosition.Bottom, + ConfigSide.Left => AppBarPosition.Left, + ConfigSide.Right => AppBarPosition.Right, + _ => throw new ArgumentOutOfRangeException(nameof(side), side, @"Invalid side value") + }; } @@ -113,5 +117,31 @@ namespace IPCamAppBar { Close(); } + + + private void SystemEventsOnSessionSwitch(object sender, SessionSwitchEventArgs e) + { + if (InvokeRequired) + { + Invoke(new Action(() => SystemEventsOnSessionSwitch(sender, e))); + return; + } + + var pause = e.Reason == SessionSwitchReason.ConsoleDisconnect || + e.Reason == SessionSwitchReason.RemoteDisconnect || + e.Reason == SessionSwitchReason.SessionLock || + e.Reason == SessionSwitchReason.SessionLogoff; + + if (pause == paused) + return; + + foreach (var control in CameraViewContainer.Controls) + { + if (control is CameraView cameraView) + cameraView.SetPaused(pause); + } + + paused = pause; + } } } diff --git a/IPCamAppBar/Resources/Paused.png b/IPCamAppBar/Resources/Paused.png new file mode 100644 index 0000000..9f19584 Binary files /dev/null and b/IPCamAppBar/Resources/Paused.png differ