diff --git a/PowerToys.slnx b/PowerToys.slnx
index dd79e518fb..c93236d9a1 100644
--- a/PowerToys.slnx
+++ b/PowerToys.slnx
@@ -333,6 +333,10 @@
+
+
+
+
diff --git a/src/modules/cmdpal/CommandPalette - no UI tests.slnf b/src/modules/cmdpal/CommandPalette - no UI tests.slnf
index 74ad9d58a7..57caced6f4 100644
--- a/src/modules/cmdpal/CommandPalette - no UI tests.slnf
+++ b/src/modules/cmdpal/CommandPalette - no UI tests.slnf
@@ -19,7 +19,9 @@
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Apps.UnitTests\\Microsoft.CmdPal.Ext.Apps.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests\\Microsoft.CmdPal.Ext.Bookmarks.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Calc.UnitTests\\Microsoft.CmdPal.Ext.Calc.UnitTests.csproj",
- "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj",
+ "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj",
+ "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Indexer.UnitTests\\Microsoft.CmdPal.Ext.Indexer.UnitTests.csproj",
+ "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Registry.UnitTests\\Microsoft.CmdPal.Ext.Registry.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.WindowsSettings.UnitTests\\Microsoft.CmdPal.Ext.WindowsSettings.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests.csproj",
diff --git a/src/modules/cmdpal/CommandPalette.slnf b/src/modules/cmdpal/CommandPalette.slnf
index 2d2ef72ae9..fd543f357d 100644
--- a/src/modules/cmdpal/CommandPalette.slnf
+++ b/src/modules/cmdpal/CommandPalette.slnf
@@ -24,6 +24,7 @@
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Calc.UnitTests\\Microsoft.CmdPal.Ext.Calc.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests\\Microsoft.CmdPal.Ext.ClipboardHistory.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Indexer.UnitTests\\Microsoft.CmdPal.Ext.Indexer.UnitTests.csproj",
+ "src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests\\Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Registry.UnitTests\\Microsoft.CmdPal.Ext.Registry.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests\\Microsoft.CmdPal.Ext.RemoteDesktop.UnitTests.csproj",
"src\\modules\\cmdpal\\Tests\\Microsoft.CmdPal.Ext.Shell.UnitTests\\Microsoft.CmdPal.Ext.Shell.UnitTests.csproj",
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/FormatIncomingDataTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/FormatIncomingDataTests.cs
new file mode 100644
index 0000000000..806be3407a
--- /dev/null
+++ b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/FormatIncomingDataTests.cs
@@ -0,0 +1,69 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests;
+
+[TestClass]
+public class FormatIncomingDataTests
+{
+ [DataTestMethod]
+ [DataRow(0f, "0.0 Kbps")]
+ [DataRow(6400f, "50.0 Kbps")]
+ [DataRow(12793f, "99.9 Kbps")]
+ [DataRow(12800f, "100 Kbps")]
+ [DataRow(127872f, "999 Kbps")]
+ [DataRow(131072f, "1.0 Mbps")]
+ [DataRow(6553600f, "50.0 Mbps")]
+ [DataRow(13107200f, "100 Mbps")]
+ [DataRow(134217728f, "1.0 Gbps")]
+ [DataRow(6710886400f, "50.0 Gbps")]
+ [DataRow(13421772800f, "100 Gbps")]
+ public void AsBitsPerSecString_FormatsBoundaryValues(float bytesPerSecond, string expected)
+ {
+ Assert.AreEqual(expected, FormatIncomingData.AsBitsPerSecString(bytesPerSecond));
+ }
+
+ [DataTestMethod]
+ [DataRow(0f, "0.0 KB/s")]
+ [DataRow(50000f, "50.0 KB/s")]
+ [DataRow(100000f, "100 KB/s")]
+ [DataRow(999000f, "999 KB/s")]
+ [DataRow(1000000f, "1.0 MB/s")]
+ [DataRow(50000000f, "50.0 MB/s")]
+ [DataRow(100000000f, "100 MB/s")]
+ [DataRow(1000000000f, "1.0 GB/s")]
+ [DataRow(50000000000f, "50.0 GB/s")]
+ [DataRow(100000000000f, "100 GB/s")]
+ public void AsBytesPerSecString_FormatsBoundaryValues(float bytesPerSecond, string expected)
+ {
+ Assert.AreEqual(expected, FormatIncomingData.AsBytesPerSecString(bytesPerSecond));
+ }
+
+ [TestMethod]
+ public void AsBytesPerSecString_UsesDecimalScalingDistinctFromBinary()
+ {
+ const float bytesPerSecond = 51200f;
+
+ Assert.AreEqual("51.2 KB/s", FormatIncomingData.AsBytesPerSecString(bytesPerSecond));
+ Assert.AreEqual("50.0 KiB/s", FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSecond));
+ }
+
+ [DataTestMethod]
+ [DataRow(0f, "0.0 KiB/s")]
+ [DataRow(51200f, "50.0 KiB/s")]
+ [DataRow(102400f, "100 KiB/s")]
+ [DataRow(1022976f, "999 KiB/s")]
+ [DataRow(1048576f, "1.0 MiB/s")]
+ [DataRow(52428800f, "50.0 MiB/s")]
+ [DataRow(104857600f, "100 MiB/s")]
+ [DataRow(1073741824f, "1.0 GiB/s")]
+ [DataRow(53687091200f, "50.0 GiB/s")]
+ [DataRow(107374182400f, "100 GiB/s")]
+ public void AsBinaryBytesPerSecString_FormatsBoundaryValues(float bytesPerSecond, string expected)
+ {
+ Assert.AreEqual(expected, FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSecond));
+ }
+}
diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj
new file mode 100644
index 0000000000..979bfc6190
--- /dev/null
+++ b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests/Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+
+ false
+ true
+ Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests
+ $(SolutionDir)$(Platform)\$(Configuration)\WinUI3Apps\CmdPal\tests\
+ false
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs
index bec3398c8b..4cc56866ef 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/ChartHelper.cs
@@ -17,6 +17,7 @@ internal sealed class ChartHelper
GPU,
Mem,
Net,
+ Dis,
}
public const int ChartHeight = 86;
@@ -28,6 +29,7 @@ internal sealed class ChartHelper
private const string GPULineStyle = "fill:none;stroke:rgb(222,104,242);stroke-width:1";
private const string MemLineStyle = "fill:none;stroke:rgb(92,158,250);stroke-width:1";
private const string NetLineStyle = "fill:none;stroke:rgb(245,98,142);stroke-width:1";
+ private const string DisLineStyle = "fill:none;stroke:rgb(103,153,24);stroke-width:1";
private const string FillStyle = "fill:url(#gradientId);stroke:transparent";
@@ -43,6 +45,9 @@ internal sealed class ChartHelper
private const string NetBrushStop1Style = "stop-color:rgb(245,98,142);stop-opacity:0.4";
private const string NetBrushStop2Style = "stop-color:rgb(130,0,47);stop-opacity:0.25";
+ private const string DisBrushStop1Style = "stop-color:rgb(103,153,24);stop-opacity:0.4";
+ private const string DisBrushStop2Style = "stop-color:rgb(45,62,15);stop-opacity:0.25";
+
private const string SvgElement = "svg";
private const string RectElement = "rect";
private const string PolylineElement = "polyline";
@@ -174,6 +179,10 @@ internal sealed class ChartHelper
stop1Style = NetBrushStop1Style;
stop2Style = NetBrushStop2Style;
break;
+ case ChartType.Dis:
+ stop1Style = DisBrushStop1Style;
+ stop2Style = DisBrushStop2Style;
+ break;
case ChartType.CPU:
default:
stop1Style = CPUBrushStop1Style;
@@ -213,6 +222,7 @@ internal sealed class ChartHelper
ChartType.GPU => GPULineStyle,
ChartType.Mem => MemLineStyle,
ChartType.Net => NetLineStyle,
+ ChartType.Dis => DisLineStyle,
_ => CPULineStyle,
};
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs
index 8dc80db698..aefc001de9 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataManager.cs
@@ -45,6 +45,14 @@ internal sealed partial class DataManager : IDisposable
}
}
+ private void GetDiskData()
+ {
+ lock (_systemData.DiskStats)
+ {
+ _systemData.DiskStats.GetData();
+ }
+ }
+
private void GetGPUData()
{
lock (_systemData.GPUStats)
@@ -107,6 +115,13 @@ internal sealed partial class DataManager : IDisposable
break;
}
+ case DataType.Disk:
+ {
+ // disk
+ GetDiskData();
+ break;
+ }
+
case DataType.Battery:
{
GetBatteryData();
@@ -146,6 +161,7 @@ internal sealed partial class DataManager : IDisposable
DataType.GPU => "GPU.FirstUpdate",
DataType.Memory => "Memory.FirstUpdate",
DataType.Network => "Network.FirstUpdate",
+ DataType.Disk => "Disk.FirstUpdate",
DataType.Battery => "Battery.FirstUpdate",
_ => null,
};
@@ -167,6 +183,14 @@ internal sealed partial class DataManager : IDisposable
}
}
+ internal DiskStats GetDiskStats()
+ {
+ lock (_systemData.DiskStats)
+ {
+ return _systemData.DiskStats;
+ }
+ }
+
internal GPUStats GetGPUStats()
{
lock (_systemData.GPUStats)
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs
index e9af0f6986..700e9ccf4c 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DataType.cs
@@ -37,4 +37,9 @@ public enum DataType
/// Battery related data.
///
Battery,
+
+ ///
+ /// Disk related data.
+ ///
+ Disk,
}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DiskStats.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DiskStats.cs
new file mode 100644
index 0000000000..8f5e629fa1
--- /dev/null
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/DiskStats.cs
@@ -0,0 +1,197 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using Microsoft.CmdPal.Common;
+
+namespace CoreWidgetProvider.Helpers;
+
+internal sealed partial class DiskStats : PerformanceCounterSourceBase, IDisposable
+{
+ private readonly Dictionary> _diskCounters = new();
+ private bool _diskCounterReadFailureLogged;
+
+ private Dictionary DiskUsages { get; set; } = new();
+
+ private Dictionary> DiskChartValues { get; set; } = new();
+
+ public sealed class Data
+ {
+ public float Usage
+ {
+ get; set;
+ }
+
+ public float Read
+ {
+ get; set;
+ }
+
+ public float Written
+ {
+ get; set;
+ }
+ }
+
+ public DiskStats()
+ {
+ InitDiskPerfCounters();
+ }
+
+ private void InitDiskPerfCounters()
+ {
+ try
+ {
+ var perfCounterCategory = CreatePerformanceCounterCategory("PhysicalDisk");
+ if (perfCounterCategory is null)
+ {
+ return;
+ }
+
+ var instanceNames = perfCounterCategory.GetInstanceNames();
+ foreach (var instanceName in instanceNames)
+ {
+ if (string.Equals(instanceName, "_Total", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ try
+ {
+ var bytesRead = CreatePerformanceCounter("PhysicalDisk", "Disk Read Bytes/sec", instanceName, logFailure: false);
+ var bytesWritten = CreatePerformanceCounter("PhysicalDisk", "Disk Write Bytes/sec", instanceName, logFailure: false);
+ var diskTime = CreatePerformanceCounter("PhysicalDisk", "% Disk Time", instanceName, logFailure: false);
+ if (bytesRead is null || bytesWritten is null || diskTime is null)
+ {
+ bytesRead?.Dispose();
+ bytesWritten?.Dispose();
+ diskTime?.Dispose();
+ continue;
+ }
+
+ var instanceCounters = new List { bytesRead, bytesWritten, diskTime };
+ _diskCounters.Add(instanceName, instanceCounters);
+ DiskChartValues.Add(instanceName, new List());
+ DiskUsages.Add(instanceName, new Data());
+ }
+ catch (Exception)
+ {
+ // Skip interfaces whose counters cannot be initialized.
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ CoreLogger.LogError("Failed to initialize disk performance counters.", ex);
+ }
+ }
+
+ public void GetData()
+ {
+ foreach (var diskCounterWithName in _diskCounters)
+ {
+ try
+ {
+ var read = diskCounterWithName.Value[0].NextValue();
+ var written = diskCounterWithName.Value[1].NextValue();
+
+ var diskTimePercent = Math.Clamp(diskCounterWithName.Value[2].NextValue(), 0f, 100f);
+ var name = diskCounterWithName.Key;
+
+ DiskUsages[name].Read = read;
+ DiskUsages[name].Written = written;
+ DiskUsages[name].Usage = diskTimePercent / 100f;
+
+ var chartValues = DiskChartValues[name];
+ lock (chartValues)
+ {
+ ChartHelper.AddNextChartValue(diskTimePercent, chartValues);
+ }
+ }
+ catch (Exception ex)
+ {
+ LogFailureOnce(ref _diskCounterReadFailureLogged, "Failed while reading disk performance counters.", ex);
+ }
+ }
+ }
+
+ public string CreateDiskImageUrl(int diskChartIndex)
+ {
+ var chartValues = DiskChartValues.ElementAt(diskChartIndex).Value;
+ lock (chartValues)
+ {
+ return ChartHelper.CreateImageUrl(chartValues, ChartHelper.ChartType.Dis);
+ }
+ }
+
+ public string GetDiskName(int diskIndex)
+ {
+ if (DiskChartValues.Count <= diskIndex)
+ {
+ return string.Empty;
+ }
+
+ return DiskChartValues.ElementAt(diskIndex).Key;
+ }
+
+ public Data GetDiskUsage(int diskIndex)
+ {
+ if (DiskChartValues.Count <= diskIndex)
+ {
+ return new Data();
+ }
+
+ var currDiskName = DiskChartValues.ElementAt(diskIndex).Key;
+ if (!DiskUsages.TryGetValue(currDiskName, out var value))
+ {
+ return new Data();
+ }
+
+ return value;
+ }
+
+ public int GetPrevDiskIndex(int diskIndex)
+ {
+ if (DiskChartValues.Count == 0)
+ {
+ return 0;
+ }
+
+ if (diskIndex == 0)
+ {
+ return DiskChartValues.Count - 1;
+ }
+
+ return diskIndex - 1;
+ }
+
+ public int GetNextDiskIndex(int diskIndex)
+ {
+ if (DiskChartValues.Count == 0)
+ {
+ return 0;
+ }
+
+ if (diskIndex == DiskChartValues.Count - 1)
+ {
+ return 0;
+ }
+
+ return diskIndex + 1;
+ }
+
+ public void Dispose()
+ {
+ foreach (var counterPair in _diskCounters)
+ {
+ foreach (var counter in counterPair.Value)
+ {
+ counter.Dispose();
+ }
+ }
+ }
+}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs
index 55fdbc53db..1abab760d2 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Helpers/SystemData.cs
@@ -13,6 +13,7 @@ internal sealed partial class SystemData
private readonly Lazy _memoryStats = new(() => CreateGuarded("Memory.Initialize", static () => new MemoryStats()));
private readonly Lazy _networkStats = new(() => CreateGuarded("Network.Initialize", static () => new NetworkStats()));
+ private readonly Lazy _diskStats = new(() => CreateGuarded("Disk.Initialize", static () => new DiskStats()));
private readonly Lazy _gpuStats = new(() => CreateGuarded("GPU.Initialize", static () => new GPUStats()));
private readonly Lazy _cpuStats = new(() => CreateGuarded("CPU.Initialize", static () => new CPUStats()));
private readonly Lazy _batteryStats = new(() => CreateGuarded("Battery.Initialize", static () => new BatteryStats()));
@@ -21,6 +22,8 @@ internal sealed partial class SystemData
public NetworkStats NetworkStats => _networkStats.Value;
+ public DiskStats DiskStats => _diskStats.Value;
+
public GPUStats GPUStats => _gpuStats.Value;
public CPUStats CpuStats => _cpuStats.Value;
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Templates/SystemDiskUsageTemplate.json b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Templates/SystemDiskUsageTemplate.json
new file mode 100644
index 0000000000..851f689b0c
--- /dev/null
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/DevHome/Templates/SystemDiskUsageTemplate.json
@@ -0,0 +1,106 @@
+{
+ "type": "AdaptiveCard",
+ "body": [
+ {
+ "type": "Container",
+ "$when": "${errorMessage != null}",
+ "items": [
+ {
+ "type": "TextBlock",
+ "text": "${errorMessage}",
+ "wrap": true,
+ "size": "small"
+ }
+ ],
+ "style": "warning"
+ },
+ {
+ "type": "Container",
+ "$when": "${errorMessage == null}",
+ "items": [
+ {
+ "type": "Image",
+ "url": "${diskGraphUrl}",
+ "height": "${chartHeight}",
+ "width": "${chartWidth}",
+ "$when": "${$host.widgetSize != \"small\"}",
+ "horizontalAlignment": "center"
+ },
+ {
+ "type": "ColumnSet",
+ "columns": [
+ {
+ "type": "Column",
+ "items": [
+ {
+ "text": "%DiskUsage_Widget_Template/Disk_Usage%",
+ "type": "TextBlock",
+ "spacing": "none",
+ "size": "small",
+ "isSubtle": true
+ },
+ {
+ "text": "${diskUsage}",
+ "type": "TextBlock",
+ "size": "large",
+ "weight": "bolder"
+ }
+ ]
+ },
+ {
+ "type": "Column",
+ "items": [
+ {
+ "text": "%DiskUsage_Widget_Template/Read%",
+ "type": "TextBlock",
+ "spacing": "none",
+ "size": "small",
+ "isSubtle": true
+ },
+ {
+ "text": "${diskRead}",
+ "type": "TextBlock",
+ "size": "large",
+ "weight": "bolder"
+ }
+ ]
+ },
+ {
+ "type": "Column",
+ "items": [
+ {
+ "text": "%DiskUsage_Widget_Template/Write%",
+ "type": "TextBlock",
+ "spacing": "none",
+ "size": "small",
+ "isSubtle": true,
+ "horizontalAlignment": "right"
+ },
+ {
+ "text": "${diskWrite}",
+ "type": "TextBlock",
+ "size": "large",
+ "weight": "bolder",
+ "horizontalAlignment": "right"
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "text": "%DiskUsage_Widget_Template/Disk_Name%",
+ "type": "TextBlock",
+ "size": "small",
+ "isSubtle": true
+ },
+ {
+ "text": "${diskName}",
+ "type": "TextBlock",
+ "size": "medium"
+ }
+ ]
+ }
+ ],
+ "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
+ "version": "1.5"
+}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs
index 02b704d3d4..56fb01896c 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Icons.cs
@@ -19,6 +19,10 @@ internal static class Icons
internal static IconInfo HardDriveIcon => new("\uEDA2"); // HardDrive icon
+ internal static IconInfo FileReadIcon => new("\uE890"); // FileRead icon
+
+ internal static IconInfo FileWriteIcon => new("\uE70F"); // FileWrite icon
+
internal static IconInfo NetworkIcon => new("\uEC05"); // Network icon
internal static IconInfo NetworkUpIcon => new("\uE74A"); // Up arrow icon
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj
index 9ec357605e..319296f540 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Microsoft.CmdPal.Ext.PerformanceMonitor.csproj
@@ -50,6 +50,9 @@
PreserveNewest
+
+ PreserveNewest
+
PreserveNewest
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs
index aaf21373a1..ffdea05b1c 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceMonitorCommandsProvider.cs
@@ -39,6 +39,7 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider
private PerformanceWidgetsPage? _bandPage;
private PerformanceWidgetsPage? _cpuBandPage;
private PerformanceWidgetsPage? _memoryBandPage;
+ private PerformanceWidgetsPage? _diskBandPage;
private PerformanceWidgetsPage? _networkBandPage;
private PerformanceWidgetsPage? _gpuBandPage;
private PerformanceWidgetsPage? _batteryBandPage;
@@ -198,6 +199,7 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider
_cpuBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Cpu);
_memoryBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Memory);
_networkBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Network);
+ _diskBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Disk);
_gpuBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Gpu);
_batteryBandPage = new PerformanceWidgetsPage(_settingsManager, true, PerformanceMetricKind.Battery);
@@ -206,6 +208,7 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider
new CommandItem(_cpuBandPage) { Title = Resources.GetResource("CPU_Usage_Title") },
new CommandItem(_memoryBandPage) { Title = Resources.GetResource("Memory_Usage_Title") },
new CommandItem(_networkBandPage) { Title = Resources.GetResource("Network_Usage_Title") },
+ new CommandItem(_diskBandPage) { Title = Resources.GetResource("Disk_Usage_Title") },
new CommandItem(_gpuBandPage) { Title = Resources.GetResource("GPU_Usage_Title") }
];
var batteryStats = new BatteryStats();
@@ -242,6 +245,9 @@ public partial class PerformanceMonitorCommandsProvider : CommandProvider
_memoryBandPage?.Dispose();
_memoryBandPage = null;
+ _diskBandPage?.Dispose();
+ _diskBandPage = null;
+
_networkBandPage?.Dispose();
_networkBandPage = null;
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs
index 1459e5db28..90fd33d412 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/PerformanceWidgetsPage.cs
@@ -31,6 +31,7 @@ internal enum PerformanceMetricKind
Cpu,
Memory,
Network,
+ Disk,
Gpu,
Battery,
}
@@ -59,6 +60,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
PerformanceMetricKind.Cpu => Icons.CpuIcon,
PerformanceMetricKind.Memory => Icons.MemoryIcon,
PerformanceMetricKind.Network => Icons.NetworkIcon,
+ PerformanceMetricKind.Disk => Icons.HardDriveIcon,
PerformanceMetricKind.Gpu => Icons.GpuIcon,
PerformanceMetricKind.Battery => _batteryPage?.CurrentIcon ?? Icons.BatteryIcon,
_ => Icons.PerformanceMonitorIcon,
@@ -74,6 +76,9 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
private readonly SystemMemoryUsageWidgetPage? _memoryPage;
private readonly ListItem? _memoryItem;
+ private readonly SystemDiskUsageWidgetPage? _diskPage;
+ private readonly ListItem? _diskItem;
+
private readonly SystemNetworkUsageWidgetPage? _networkPage;
private readonly ListItem? _networkItem;
@@ -89,6 +94,12 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
private string _networkUpSpeed = string.Empty;
private string _networkDownSpeed = string.Empty;
+ // For bands, we want two bands, one for read and one for write
+ private ListItem? _diskReadItem;
+ private ListItem? _diskWriteItem;
+ private string _diskReadSpeed = string.Empty;
+ private string _diskWriteSpeed = string.Empty;
+
public PerformanceWidgetsPage(SettingsManager settingsManager, bool isBandPage = false, PerformanceMetricKind? singleMetric = null)
{
_isBandPage = isBandPage;
@@ -163,6 +174,26 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
};
}
+ if (IncludesMetric(PerformanceMetricKind.Disk))
+ {
+ _diskPage = new SystemDiskUsageWidgetPage(settingsManager);
+ _diskItem = new ListItem(_diskPage)
+ {
+ Title = _diskPage.GetItemTitle(isBandPage),
+ MoreCommands = _diskPage.Commands,
+ };
+
+ _diskPage.Updated += (s, e) =>
+ {
+ _diskItem.Title = _diskPage.GetItemTitle(isBandPage);
+ _diskReadSpeed = _diskPage.GetReadSpeed();
+ _diskWriteSpeed = _diskPage.GetWriteSpeed();
+ _diskReadItem?.Title = $"{_diskReadSpeed}";
+ _diskWriteItem?.Title = $"{_diskWriteSpeed}";
+ RaiseItemsChanged();
+ };
+ }
+
if (IncludesMetric(PerformanceMetricKind.Gpu))
{
_gpuPage = new SystemGPUUsageWidgetPage();
@@ -225,6 +256,12 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
_networkItem.Subtitle = Resources.GetResource("Network_Usage_Subtitle");
}
+ if (_diskItem is not null)
+ {
+ _diskItem.Subtitle = Resources.GetResource("Disk_Active_Time_Subtitle");
+ _diskItem.Icon = Icons.HardDriveIcon;
+ }
+
if (_gpuItem is not null)
{
_gpuItem.Subtitle = Resources.GetResource("GPU_Usage_Subtitle");
@@ -242,6 +279,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
_cpuPage?.PushActivate();
_memoryPage?.PushActivate();
_networkPage?.PushActivate();
+ _diskPage?.PushActivate();
_gpuPage?.PushActivate();
_batteryPage?.PushActivate();
}
@@ -251,6 +289,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
_cpuPage?.PopActivate();
_memoryPage?.PopActivate();
_networkPage?.PopActivate();
+ _diskPage?.PopActivate();
_gpuPage?.PopActivate();
_batteryPage?.PopActivate();
}
@@ -260,11 +299,17 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
// Per-metric pages just return the single matching item.
if (_singleMetric is PerformanceMetricKind metric)
{
+ if (metric == PerformanceMetricKind.Disk && _isBandPage)
+ {
+ return CreateDiskBandItems();
+ }
+
return metric switch
{
PerformanceMetricKind.Cpu => new IListItem[] { _cpuItem! },
PerformanceMetricKind.Memory => new IListItem[] { _memoryItem! },
PerformanceMetricKind.Network => new IListItem[] { _networkItem! },
+ PerformanceMetricKind.Disk => new IListItem[] { _diskItem! },
PerformanceMetricKind.Gpu => new IListItem[] { _gpuItem! },
PerformanceMetricKind.Battery => new IListItem[] { _batteryItem! },
_ => Array.Empty(),
@@ -275,22 +320,71 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
{
// TODO add details
return _batteryItem is not null
- ? new[] { _cpuItem!, _memoryItem!, _networkItem!, _gpuItem!, _batteryItem! }
- : new[] { _cpuItem!, _memoryItem!, _networkItem!, _gpuItem! };
+ ? new[] { _cpuItem!, _memoryItem!, _networkItem!, _diskItem!, _gpuItem!, _batteryItem! }
+ : new[] { _cpuItem!, _memoryItem!, _networkItem!, _diskItem!, _gpuItem! };
}
else
{
+ if (_networkUpItem is null)
+ {
+ _networkUpItem = new ListItem(_networkPage!)
+ {
+ Title = $"{_networkUpSpeed}",
+ Subtitle = Resources.GetResource("Network_Send_Subtitle"),
+ Icon = Icons.NetworkUpIcon,
+ MoreCommands = _networkPage!.Commands,
+ };
+ }
+
+ _networkUpItem.Title = _networkUpSpeed;
+
+ if (_networkDownItem is null)
+ {
+ _networkDownItem = new ListItem(_networkPage!)
+ {
+ Title = $"{_networkDownSpeed}",
+ Subtitle = Resources.GetResource("Network_Receive_Subtitle"),
+ Icon = Icons.NetworkDownIcon,
+ MoreCommands = _networkPage!.Commands,
+ };
+ }
+
+ _networkDownItem.Title = _networkDownSpeed;
+
+ CreateDiskBandItems();
return _batteryItem is not null
- ? new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _gpuItem!, _batteryItem! }
- : new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _gpuItem! };
+ ? new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _diskReadItem!, _diskWriteItem!, _diskItem!, _gpuItem!, _batteryItem! }
+ : new[] { _cpuItem!, _memoryItem!, _networkUpItem!, _networkDownItem!, _diskReadItem!, _diskWriteItem!, _diskItem!, _gpuItem! };
}
}
+ private IListItem[] CreateDiskBandItems()
+ {
+ _diskReadItem ??= new ListItem(_diskPage!)
+ {
+ Subtitle = Resources.GetResource("Disk_Read_Subtitle"),
+ Icon = Icons.FileReadIcon,
+ MoreCommands = _diskPage!.Commands,
+ };
+ _diskReadItem.Title = _diskReadSpeed;
+
+ _diskWriteItem ??= new ListItem(_diskPage!)
+ {
+ Subtitle = Resources.GetResource("Disk_Write_Subtitle"),
+ Icon = Icons.FileWriteIcon,
+ MoreCommands = _diskPage!.Commands,
+ };
+ _diskWriteItem.Title = _diskWriteSpeed;
+
+ return [_diskReadItem, _diskWriteItem, _diskItem!];
+ }
+
public void Dispose()
{
_cpuPage?.Dispose();
_memoryPage?.Dispose();
_networkPage?.Dispose();
+ _diskPage?.Dispose();
_gpuPage?.Dispose();
_batteryPage?.Dispose();
}
@@ -312,6 +406,7 @@ internal sealed partial class PerformanceWidgetsPage : OnLoadStaticListPage, IDi
PerformanceMetricKind.Cpu => "cpu",
PerformanceMetricKind.Memory => "memory",
PerformanceMetricKind.Network => "network",
+ PerformanceMetricKind.Disk => "disk",
PerformanceMetricKind.Gpu => "gpu",
PerformanceMetricKind.Battery => "battery",
_ => "unknown",
@@ -674,6 +769,198 @@ internal sealed partial class SystemMemoryUsageWidgetPage : WidgetPage, IDisposa
}
}
+internal sealed partial class SystemDiskUsageWidgetPage : WidgetPage, IDisposable
+{
+ public override string Id => "com.microsoft.cmdpal.disk_widget";
+
+ public override string Title => Resources.GetResource("Disk_Usage_Title");
+
+ public override IconInfo Icon => Icons.HardDriveIcon;
+
+ private readonly DataManager _dataManager;
+ private readonly SettingsManager _settingsManager;
+ private int _diskIndex;
+
+ public SystemDiskUsageWidgetPage(SettingsManager settingsManager)
+ {
+ _settingsManager = settingsManager;
+ _dataManager = new(DataType.Disk, () => UpdateWidget());
+ Commands = [
+ new CommandContextItem(new PrevDiskCommand(this) { Name = Resources.GetResource("Previous_Disk_Title") }),
+ new CommandContextItem(new NextDiskCommand(this) { Name = Resources.GetResource("Next_Disk_Title") }),
+ new CommandContextItem(OpenTaskManagerCommand.Instance),
+ ];
+ }
+
+ protected override void LoadContentData()
+ {
+ // CoreLogger.LogDebug("Getting Disk stats");
+ try
+ {
+ ContentData.Clear();
+
+ var timer = Stopwatch.StartNew();
+
+ var currentData = _dataManager.GetDiskStats();
+
+ var dataDuration = timer.ElapsedMilliseconds;
+
+ var diskName = currentData.GetDiskName(_diskIndex);
+ var diskStats = currentData.GetDiskUsage(_diskIndex);
+
+ ContentData["diskUsage"] = FloatToPercentString(diskStats.Usage);
+ ContentData["diskRead"] = SpeedToString(diskStats.Read);
+ ContentData["diskWrite"] = SpeedToString(diskStats.Written);
+ ContentData["diskName"] = diskName;
+ ContentData["diskGraphUrl"] = currentData.CreateDiskImageUrl(_diskIndex);
+ ContentData["chartHeight"] = ChartHelper.ChartHeight + "px";
+ ContentData["chartWidth"] = ChartHelper.ChartWidth + "px";
+
+ var contentDuration = timer.ElapsedMilliseconds - dataDuration;
+
+ // CoreLogger.LogDebug($"Disk stats retrieved in {dataDuration} ms, content prepared in {contentDuration} ms. (Total {timer.ElapsedMilliseconds} ms)");
+ }
+ catch (Exception e)
+ {
+ ContentData.Clear();
+ ContentData["errorMessage"] = e.Message;
+ return;
+ }
+ }
+
+ protected override string GetTemplatePath(WidgetPageState page)
+ {
+ return page switch
+ {
+ WidgetPageState.Content => @"DevHome\Templates\SystemDiskUsageTemplate.json",
+ WidgetPageState.Loading => @"DevHome\Templates\SystemDiskUsageTemplate.json",
+ _ => throw new NotImplementedException(),
+ };
+ }
+
+ public string GetItemTitle(bool isBandPage)
+ {
+ if (ContentData.TryGetValue("diskName", out var name) && ContentData.TryGetValue("diskUsage", out var usage))
+ {
+ return isBandPage ? usage : string.Format(CultureInfo.CurrentCulture, Resources.GetResource("Disk_Usage_Label"), name, usage);
+ }
+ else
+ {
+ return isBandPage ? Resources.GetResource("Disk_Usage_Unknown") : Resources.GetResource("Disk_Usage_Unknown_Label");
+ }
+ }
+
+ // read/write speed is always used for bands
+ public string GetReadSpeed()
+ {
+ if (ContentData.TryGetValue("diskRead", out var readSpeed))
+ {
+ return readSpeed;
+ }
+ else
+ {
+ return "???";
+ }
+ }
+
+ public string GetWriteSpeed()
+ {
+ if (ContentData.TryGetValue("diskWrite", out var writeSpeed))
+ {
+ return writeSpeed;
+ }
+ else
+ {
+ return "???";
+ }
+ }
+
+ private string SpeedToString(float bytesPerSec)
+ {
+ return _settingsManager.DiskSpeedUnit switch
+ {
+ SpeedUnit.BytesPerSecond => FormatIncomingData.AsBytesPerSecString(bytesPerSec),
+ SpeedUnit.BinaryBytesPerSecond => FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSec),
+ _ => FormatIncomingData.AsBitsPerSecString(bytesPerSec),
+ };
+ }
+
+ internal override void PushActivate()
+ {
+ base.PushActivate();
+ if (IsActive)
+ {
+ _dataManager.Start();
+ }
+ }
+
+ internal override void PopActivate()
+ {
+ base.PopActivate();
+ if (!IsActive)
+ {
+ _dataManager.Stop();
+ }
+ }
+
+ private void HandlePrevDisk()
+ {
+ _diskIndex = _dataManager.GetDiskStats().GetPrevDiskIndex(_diskIndex);
+ UpdateWidget();
+ }
+
+ private void HandleNextDisk()
+ {
+ _diskIndex = _dataManager.GetDiskStats().GetNextDiskIndex(_diskIndex);
+ UpdateWidget();
+ }
+
+ public void Dispose()
+ {
+ _dataManager.Dispose();
+ }
+
+ private sealed partial class PrevDiskCommand : InvokableCommand
+ {
+ private readonly SystemDiskUsageWidgetPage _page;
+
+ public PrevDiskCommand(SystemDiskUsageWidgetPage page)
+ {
+ _page = page;
+ }
+
+ public override string Id => "com.microsoft.cmdpal.disk_widget.prev";
+
+ public override IconInfo Icon => Icons.NavigateBackwardIcon;
+
+ public override ICommandResult Invoke()
+ {
+ _page.HandlePrevDisk();
+ return CommandResult.KeepOpen();
+ }
+ }
+
+ private sealed partial class NextDiskCommand : InvokableCommand
+ {
+ private readonly SystemDiskUsageWidgetPage _page;
+
+ public NextDiskCommand(SystemDiskUsageWidgetPage page)
+ {
+ _page = page;
+ }
+
+ public override string Id => "com.microsoft.cmdpal.disk_widget.next";
+
+ public override IconInfo Icon => Icons.NavigateForwardIcon;
+
+ public override ICommandResult Invoke()
+ {
+ _page.HandleNextDisk();
+ return CommandResult.KeepOpen();
+ }
+ }
+}
+
internal sealed partial class SystemNetworkUsageWidgetPage : WidgetPage, IDisposable
{
public override string Id => "com.microsoft.cmdpal.network_widget";
@@ -784,123 +1071,12 @@ internal sealed partial class SystemNetworkUsageWidgetPage : WidgetPage, IDispos
{
return _settingsManager.NetworkSpeedUnit switch
{
- NetworkSpeedUnit.BytesPerSecond => FormatAsBytesPerSecString(bytesPerSec),
- NetworkSpeedUnit.BinaryBytesPerSecond => FormatAsBinaryBytesPerSecString(bytesPerSec),
- _ => FormatAsBitsPerSecString(bytesPerSec),
+ SpeedUnit.BytesPerSecond => FormatIncomingData.AsBytesPerSecString(bytesPerSec),
+ SpeedUnit.BinaryBytesPerSecond => FormatIncomingData.AsBinaryBytesPerSecString(bytesPerSec),
+ _ => FormatIncomingData.AsBitsPerSecString(bytesPerSec),
};
}
- private static string FormatAsBitsPerSecString(float value)
- {
- // Bytes to bits
- value *= 8;
-
- // bits to Kbits
- value /= 1024;
- if (value < 1024)
- {
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Kbps", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} Kbps", value);
- }
-
- // Kbits to Mbits
- value /= 1024;
- if (value < 1024)
- {
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Mbps", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} Mbps", value);
- }
-
- // Mbits to Gbits
- value /= 1024;
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Gbps", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} Gbps", value);
- }
-
- private static string FormatAsBytesPerSecString(float value)
- {
- // Bytes to KB
- value /= 1024;
- if (value < 1024)
- {
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KB/s", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} KB/s", value);
- }
-
- // KB to MB
- value /= 1024;
- if (value < 1024)
- {
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MB/s", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} MB/s", value);
- }
-
- // MB to GB
- value /= 1024;
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GB/s", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} GB/s", value);
- }
-
- private static string FormatAsBinaryBytesPerSecString(float value)
- {
- // Bytes to KiB
- value /= 1024;
- if (value < 1024)
- {
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KiB/s", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} KiB/s", value);
- }
-
- // KiB to MiB
- value /= 1024;
- if (value < 1024)
- {
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MiB/s", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} MiB/s", value);
- }
-
- // MiB to GiB
- value /= 1024;
- if (value < 100)
- {
- return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GiB/s", value);
- }
-
- return string.Format(CultureInfo.InvariantCulture, "{0:0} GiB/s", value);
- }
-
internal override void PushActivate()
{
base.PushActivate();
@@ -1327,3 +1503,117 @@ internal sealed partial class OpenTaskManagerCommand : InvokableCommand
return CommandResult.Hide();
}
}
+
+internal static class FormatIncomingData
+{
+ public static string AsBitsPerSecString(float value)
+ {
+ // Bytes to bits
+ value *= 8;
+
+ // bits to Kbits
+ value /= 1024;
+ if (value < 1024)
+ {
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Kbps", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} Kbps", value);
+ }
+
+ // Kbits to Mbits
+ value /= 1024;
+ if (value < 1024)
+ {
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Mbps", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} Mbps", value);
+ }
+
+ // Mbits to Gbits
+ value /= 1024;
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} Gbps", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} Gbps", value);
+ }
+
+ public static string AsBytesPerSecString(float value)
+ {
+ // Bytes to KB (SI decimal, 1000-based)
+ value /= 1000;
+ if (value < 1000)
+ {
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KB/s", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} KB/s", value);
+ }
+
+ // KB to MB
+ value /= 1000;
+ if (value < 1000)
+ {
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MB/s", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} MB/s", value);
+ }
+
+ // MB to GB
+ value /= 1000;
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GB/s", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} GB/s", value);
+ }
+
+ public static string AsBinaryBytesPerSecString(float value)
+ {
+ // Bytes to KiB
+ value /= 1024;
+ if (value < 1024)
+ {
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} KiB/s", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} KiB/s", value);
+ }
+
+ // KiB to MiB
+ value /= 1024;
+ if (value < 1024)
+ {
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} MiB/s", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} MiB/s", value);
+ }
+
+ // MiB to GiB
+ value /= 1024;
+ if (value < 100)
+ {
+ return string.Format(CultureInfo.InvariantCulture, "{0:0.0} GiB/s", value);
+ }
+
+ return string.Format(CultureInfo.InvariantCulture, "{0:0} GiB/s", value);
+ }
+}
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Properties/AssemblyInfo.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..c9f8493e56
--- /dev/null
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Properties/AssemblyInfo.cs
@@ -0,0 +1,7 @@
+// Copyright (c) Microsoft Corporation
+// The Microsoft Corporation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Microsoft.CmdPal.Ext.PerformanceMonitor.UnitTests")]
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs
index fd975a65e7..8adf7120c4 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SettingsManager.cs
@@ -20,15 +20,30 @@ internal sealed class SettingsManager : JsonSettingsManager
Resources.GetResource("Network_Speed_Unit_Setting_Title"),
Resources.GetResource("Network_Speed_Unit_Setting_Description"),
[
- new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BitsPerSec"), NetworkSpeedUnit.BitsPerSecond.ToString("G")),
- new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BytesPerSec"), NetworkSpeedUnit.BytesPerSecond.ToString("G")),
- new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BinaryBytesPerSec"), NetworkSpeedUnit.BinaryBytesPerSecond.ToString("G")),
+ new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BitsPerSec"), SpeedUnit.BitsPerSecond.ToString("G")),
+ new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BytesPerSec"), SpeedUnit.BytesPerSecond.ToString("G")),
+ new ChoiceSetSetting.Choice(Resources.GetResource("Network_Speed_Unit_BinaryBytesPerSec"), SpeedUnit.BinaryBytesPerSecond.ToString("G")),
]);
- public NetworkSpeedUnit NetworkSpeedUnit =>
- Enum.TryParse(_networkSpeedUnit.Value, out var unit)
+ public SpeedUnit NetworkSpeedUnit =>
+ Enum.TryParse(_networkSpeedUnit.Value, out var unit)
? unit
- : NetworkSpeedUnit.BitsPerSecond;
+ : SpeedUnit.BitsPerSecond;
+
+ private readonly ChoiceSetSetting _diskSpeedUnit = new(
+ Namespaced(nameof(DiskSpeedUnit)),
+ Resources.GetResource("Disk_Speed_Unit_Setting_Title"),
+ Resources.GetResource("Disk_Speed_Unit_Setting_Description"),
+ [
+ new ChoiceSetSetting.Choice(Resources.GetResource("Disk_Speed_Unit_BitsPerSec"), SpeedUnit.BitsPerSecond.ToString("G")),
+ new ChoiceSetSetting.Choice(Resources.GetResource("Disk_Speed_Unit_BytesPerSec"), SpeedUnit.BytesPerSecond.ToString("G")),
+ new ChoiceSetSetting.Choice(Resources.GetResource("Disk_Speed_Unit_BinaryBytesPerSec"), SpeedUnit.BinaryBytesPerSecond.ToString("G")),
+ ]);
+
+ public SpeedUnit DiskSpeedUnit =>
+ Enum.TryParse(_diskSpeedUnit.Value, out var unit)
+ ? unit
+ : SpeedUnit.BytesPerSecond;
private static string SettingsJsonPath()
{
@@ -42,6 +57,7 @@ internal sealed class SettingsManager : JsonSettingsManager
FilePath = SettingsJsonPath();
Settings.Add(_networkSpeedUnit);
+ Settings.Add(_diskSpeedUnit);
LoadSettings();
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/NetworkSpeedUnit.cs b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SpeedUnit.cs
similarity index 86%
rename from src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/NetworkSpeedUnit.cs
rename to src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SpeedUnit.cs
index 0578bb1d88..2bfaa994d4 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/NetworkSpeedUnit.cs
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/SpeedUnit.cs
@@ -5,9 +5,9 @@
namespace Microsoft.CmdPal.Ext.PerformanceMonitor;
///
-/// Controls the unit used to display network transmission speed.
+/// Controls the unit used to display network or disk transmission speed.
///
-internal enum NetworkSpeedUnit
+internal enum SpeedUnit
{
/// Bits per second (Kbps, Mbps, Gbps) — SI decimal prefixes.
BitsPerSecond,
diff --git a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw
index 5d88b51a9a..0ed930d5e4 100644
--- a/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw
+++ b/src/modules/cmdpal/ext/Microsoft.CmdPal.Ext.PerformanceMonitor/Strings/en-US/Resources.resw
@@ -1,5 +1,64 @@
+
@@ -135,6 +194,24 @@
Ethernet
+
+ Active time
+
+
+ Read
+
+
+ Write
+
+
+ Name
+
+
+ Previous disk
+
+
+ Next disk
+
Utilization
@@ -183,6 +260,9 @@
Network
+
+ Disk
+
GPU
@@ -257,6 +337,19 @@
Network Usage: ???
+
+ Disk Usage
+
+
+ Disk ({0}): {1}
+ {0} is the disk interface name, {1} is the usage percentage
+
+
+ ???
+
+
+ Disk Usage: ???
+
GPU Usage
@@ -331,6 +424,15 @@
Receive
+
+ Read
+
+
+ Write
+
+
+ Active time
+
Network speed unit
@@ -346,4 +448,19 @@
Binary bytes per second (KiB/s, MiB/s, GiB/s)
-
+
+ Disk speed unit
+
+
+ Choose the unit used to display disk transfer speed
+
+
+ Bits per second (Kbps, Mbps, Gbps)
+
+
+ Bytes per second (KB/s, MB/s, GB/s)
+
+
+ Binary bytes per second (KiB/s, MiB/s, GiB/s)
+
+
\ No newline at end of file