Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions RunCat365/CPURepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2020 Takuto Nakamura
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

global using CPUInfo = float;
using System.Diagnostics;

namespace RunCat365
{
internal static class CPUInfoExtension
{
internal static string GenerateIndicator(this CPUInfo cpuInfo)
{
return $"CPU: {cpuInfo:f1}%";
}
}

internal class CPURepository
{
private readonly PerformanceCounter cpuCounter;
private readonly List<CPUInfo> cpuInfoList = [];
private const int CPU_INFO_LIST_LIMIT_SIZE = 5;

internal CPURepository()
{
cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
_ = cpuCounter.NextValue(); // discards first return value
}

internal void Update()
{
// Range of CPU percentage: 0-100 (%)
var value = Math.Min(100, cpuCounter.NextValue());
cpuInfoList.Add(value);
if (CPU_INFO_LIST_LIMIT_SIZE < cpuInfoList.Count)
{
cpuInfoList.RemoveAt(0);
}
}

internal CPUInfo Get()
{
return cpuInfoList.Average();
}

internal void Close()
{
cpuCounter.Close();
}
}
}
77 changes: 40 additions & 37 deletions RunCat365/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
using Microsoft.Win32;
using RunCat365.Properties;
using System.ComponentModel;
using System.Diagnostics;

namespace RunCat365
{
Expand Down Expand Up @@ -44,25 +43,25 @@ static void Main()

public class RunCat365ApplicationContext : ApplicationContext
{
private const int CPU_TIMER_DEFAULT_INTERVAL = 1000;
private const int CPU_VALUES_LIMIT_SIZE = 5;
private const int FETCH_TIMER_DEFAULT_INTERVAL = 1000;
private const int FETCH_COUNTER_SIZE = 5;
private const int ANIMATE_TIMER_DEFAULT_INTERVAL = 200;
private readonly PerformanceCounter cpuCounter;
private readonly CPURepository cpuRepository;
private readonly StorageRepository storageRepository;
private readonly CustomToolStripMenuItem systemInfoMenu;
private readonly CustomToolStripMenuItem runnerMenu;
private readonly CustomToolStripMenuItem themeMenu;
private readonly CustomToolStripMenuItem fpsMaxLimitMenu;
private readonly CustomToolStripMenuItem startupMenu;
private readonly NotifyIcon notifyIcon;
private readonly FormsTimer fetchTimer;
private readonly FormsTimer animateTimer;
private readonly FormsTimer cpuTimer;
private readonly List<float> cpuValues = [];
private readonly List<Icon> icons = [];
private Runner runner = Runner.Cat;
private Theme manualTheme = Theme.System;
private FPSMaxLimit fpsMaxLimit = FPSMaxLimit.FPS40;
private int fetchCounter = 5;
private int current = 0;
private float interval;

public RunCat365ApplicationContext()
{
Expand All @@ -75,8 +74,8 @@ public RunCat365ApplicationContext()

SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChanged);

cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
_ = cpuCounter.NextValue(); // discards first return value
cpuRepository = new CPURepository();
storageRepository = new StorageRepository();

systemInfoMenu = new CustomToolStripMenuItem("-\n-\n-\n-\n-")
{
Expand Down Expand Up @@ -148,14 +147,12 @@ public RunCat365ApplicationContext()
animateTimer.Tick += new EventHandler(AnimationTick);
animateTimer.Start();

cpuTimer = new FormsTimer
fetchTimer = new FormsTimer
{
Interval = CPU_TIMER_DEFAULT_INTERVAL
Interval = FETCH_TIMER_DEFAULT_INTERVAL
};
cpuTimer.Tick += new EventHandler(CPUTick);
cpuTimer.Start();

FetchSystemInfo(0);
fetchTimer.Tick += new EventHandler(FetchTick);
fetchTimer.Start();
}

private static Bitmap? GetRunnerThumbnailBitmap(Runner runner)
Expand Down Expand Up @@ -322,9 +319,9 @@ private void SetStartup(object? sender, EventArgs e)

private void Exit(object? sender, EventArgs e)
{
cpuCounter.Close();
cpuRepository.Close();
animateTimer.Stop();
cpuTimer.Stop();
fetchTimer.Stop();
notifyIcon.Visible = false;
Application.Exit();
}
Expand All @@ -336,34 +333,40 @@ private void AnimationTick(object? sender, EventArgs e)
current = (current + 1) % icons.Count;
}

private void CPUTick(object? state, EventArgs e)
private void FetchSystemInfo(CPUInfo cpuInfo, List<StorageInfo> storageValue)
{
// Range of CPU percentage: 0-100 (%)
var value = Math.Min(100, cpuCounter.NextValue());
cpuValues.Add(value);
if (cpuValues.Count < CPU_VALUES_LIMIT_SIZE) return;
var cpuIndicator = cpuInfo.GenerateIndicator();
notifyIcon.Text = cpuIndicator;

var averageValue = cpuValues.Average();
cpuValues.Clear();
FetchSystemInfo(averageValue);
var systemInfoValues = new List<string>
{
cpuIndicator
};
systemInfoValues.AddRange(storageValue.GenerateIndicator());
systemInfoMenu.Text = string.Join("\n", [.. systemInfoValues]);
}

private int CalculateInterval(CPUInfo cpuInfo)
{
// Range of interval: 25-500 (ms) = 2-40 (fps)
interval = 500.0f / (float)Math.Max(1.0f, (averageValue / 5.0f) * fpsMaxLimit.GetRate());
animateTimer.Stop();
animateTimer.Interval = (int)interval;
animateTimer.Start();
var speed = (float)Math.Max(1.0f, (cpuInfo / 5.0f) * fpsMaxLimit.GetRate());
return (int)(500.0f / speed);
}

private void FetchSystemInfo(float cpuValue)
private void FetchTick(object? state, EventArgs e)
{
notifyIcon.Text = $"CPU: {cpuValue:f1}%";
cpuRepository.Update();
fetchCounter += 1;
if (fetchCounter < FETCH_COUNTER_SIZE) return;
fetchCounter = 0;

var systemInfoValues = new List<string>
{
$"CPU: {cpuValue:f1}%"
};
systemInfoValues.AddRange(StorageRepository.Get().GenerateTree());
systemInfoMenu.Text = string.Join("\n", [.. systemInfoValues]);
var cpuInfo = cpuRepository.Get();
var storageInfo = storageRepository.Get();
FetchSystemInfo(cpuInfo, storageInfo);

animateTimer.Stop();
animateTimer.Interval = CalculateInterval(cpuInfo);
animateTimer.Start();
}
}

Expand Down
64 changes: 37 additions & 27 deletions RunCat365/StorageRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using RunCat365;

namespace RunCat365
{
enum Drive
Expand Down Expand Up @@ -53,6 +51,33 @@ struct StorageInfo
internal long UsedSpaceSize { get; set; }
}

internal static class StorageInfoExtension
{
internal static List<string> GenerateIndicator(this List<StorageInfo> storageInfoList)
{
var resultLines = new List<string>
{
"Storage:"
};

if (storageInfoList.Count == 0) return resultLines;

for (int i = 0; i < storageInfoList.Count; i++)
{
var info = storageInfoList[i];
var isLastItem = (i == storageInfoList.Count - 1);
var parentPrefix = isLastItem ? " └─ " : " ├─ ";
var childIndent = isLastItem ? " " : " │ ";
var percentage = ((double)info.UsedSpaceSize / info.TotalSize) * 100.0;
resultLines.Add($"{parentPrefix}{info.Drive.GetString()}: {percentage:f1}%");
resultLines.Add($"{childIndent} ├─ Used: {info.UsedSpaceSize.ToByteFormatted()}");
resultLines.Add($"{childIndent} └─ Available: {info.AvailableSpaceSize.ToByteFormatted()}");
}

return resultLines;
}
}

static class ByteFormatter
{
internal static string ToByteFormatted(this long bytes)
Expand All @@ -69,11 +94,15 @@ internal static string ToByteFormatted(this long bytes)
}
}

static class StorageRepository
internal class StorageRepository
{
internal static List<StorageInfo> Get()
private readonly List<StorageInfo> storageInfoList = [];

internal StorageRepository() { }

internal void Update()
{
var storageInfoList = new List<StorageInfo>();
storageInfoList.Clear();
var allDrives = DriveInfo.GetDrives();
foreach (DriveInfo driveInfo in allDrives)
{
Expand All @@ -96,31 +125,12 @@ internal static List<StorageInfo> Get()
}
}
}
return storageInfoList;
}

internal static List<string> GenerateTree(this List<StorageInfo> storageInfoList)
internal List<StorageInfo> Get()
{
var resultLines = new List<string>
{
"Storage:"
};

if (storageInfoList.Count == 0) return resultLines;

for (int i = 0; i < storageInfoList.Count; i++)
{
var info = storageInfoList[i];
var isLastItem = (i == storageInfoList.Count - 1);
var parentPrefix = isLastItem ? " └─ " : " ├─ ";
var childIndent = isLastItem ? " " : " │ ";
var percentage = ((double)info.UsedSpaceSize / info.TotalSize) * 100.0;
resultLines.Add($"{parentPrefix}{info.Drive.GetString()}: {percentage:f1}%");
resultLines.Add($"{childIndent} ├─ Used: {info.UsedSpaceSize.ToByteFormatted()}");
resultLines.Add($"{childIndent} └─ Available: {info.AvailableSpaceSize.ToByteFormatted()}");
}

return resultLines;
Update();
return storageInfoList;
}
}
}