chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,404 @@
|
||||
// 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.
|
||||
|
||||
using Microsoft.Win32;
|
||||
using RunCat365.Properties;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using FormsTimer = System.Windows.Forms.Timer;
|
||||
|
||||
namespace RunCat365
|
||||
{
|
||||
internal static class Program
|
||||
{
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
#if DEBUG
|
||||
var defaultCultureInfo = SupportedLanguage.English.GetDefaultCultureInfo();
|
||||
#else
|
||||
var defaultCultureInfo = SupportedLanguageExtension.GetCurrentLanguage().GetDefaultCultureInfo();
|
||||
#endif
|
||||
CultureInfo.CurrentUICulture = defaultCultureInfo;
|
||||
CultureInfo.CurrentCulture = defaultCultureInfo;
|
||||
|
||||
using var procMutex = new Mutex(true, "_RUNCAT_MUTEX", out var result);
|
||||
if (!result) return;
|
||||
|
||||
try
|
||||
{
|
||||
ApplicationConfiguration.Initialize();
|
||||
Application.SetColorMode(SystemColorMode.System);
|
||||
Application.Run(new RunCat365ApplicationContext());
|
||||
}
|
||||
finally
|
||||
{
|
||||
procMutex?.ReleaseMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class RunCat365ApplicationContext : ApplicationContext
|
||||
{
|
||||
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 CPURepository cpuRepository;
|
||||
private readonly GPURepository gpuRepository;
|
||||
private readonly MemoryRepository memoryRepository;
|
||||
private readonly TemperatureRepository temperatureRepository;
|
||||
private readonly StorageRepository storageRepository;
|
||||
private readonly NetworkRepository networkRepository;
|
||||
private readonly CustomRunnerRepository customRunnerRepository;
|
||||
private readonly LaunchAtStartupManager launchAtStartupManager;
|
||||
private readonly ContextMenuManager contextMenuManager;
|
||||
private readonly FormsTimer fetchTimer;
|
||||
private readonly FormsTimer animateTimer;
|
||||
private Runner runner = Runner.Cat;
|
||||
private Theme manualTheme = Theme.System;
|
||||
private TemperatureUnit temperatureUnit = TemperatureUnit.System;
|
||||
private FPSMaxLimit fpsMaxLimit = FPSMaxLimit.FPS40;
|
||||
private SpeedSource speedSource = SpeedSource.CPU;
|
||||
private string? customRunnerName;
|
||||
private int fetchCounter = 5;
|
||||
private bool isFetching;
|
||||
|
||||
public RunCat365ApplicationContext()
|
||||
{
|
||||
UserSettings.Default.Reload();
|
||||
_ = Enum.TryParse(UserSettings.Default.Runner, out runner);
|
||||
_ = Enum.TryParse(UserSettings.Default.Theme, out manualTheme);
|
||||
_ = Enum.TryParse(UserSettings.Default.TemperatureUnit, out temperatureUnit);
|
||||
_ = Enum.TryParse(UserSettings.Default.FPSMaxLimit, out fpsMaxLimit);
|
||||
_ = Enum.TryParse(UserSettings.Default.SpeedSource, out speedSource);
|
||||
customRunnerName = string.IsNullOrEmpty(UserSettings.Default.CustomRunnerName)
|
||||
? null
|
||||
: UserSettings.Default.CustomRunnerName;
|
||||
|
||||
SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(UserPreferenceChanged);
|
||||
|
||||
cpuRepository = new CPURepository();
|
||||
gpuRepository = new GPURepository();
|
||||
memoryRepository = new MemoryRepository();
|
||||
temperatureRepository = new TemperatureRepository();
|
||||
storageRepository = new StorageRepository();
|
||||
networkRepository = new NetworkRepository();
|
||||
customRunnerRepository = new CustomRunnerRepository();
|
||||
launchAtStartupManager = new LaunchAtStartupManager();
|
||||
|
||||
ResolveSpeedSource();
|
||||
|
||||
contextMenuManager = new ContextMenuManager(
|
||||
() => runner,
|
||||
r => ChangeRunner(r),
|
||||
customRunnerRepository,
|
||||
() => customRunnerName,
|
||||
name => ApplyCustomRunner(name),
|
||||
deletedName => HandleCustomRunnerDeleted(deletedName),
|
||||
() => GetSystemTheme(),
|
||||
() => manualTheme,
|
||||
t => ChangeManualTheme(t),
|
||||
() => speedSource,
|
||||
s => ChangeSpeedSource(s),
|
||||
s => IsSpeedSourceAvailable(s),
|
||||
() => fpsMaxLimit,
|
||||
f => ChangeFPSMaxLimit(f),
|
||||
() => temperatureUnit,
|
||||
u => ChangeTemperatureUnit(u),
|
||||
() => launchAtStartupManager.GetStartup(),
|
||||
s => launchAtStartupManager.ToggleStartup(s),
|
||||
() => OpenProjectPage(),
|
||||
() => Application.Exit()
|
||||
);
|
||||
|
||||
if (customRunnerName is not null)
|
||||
{
|
||||
ApplyCustomRunner(customRunnerName);
|
||||
}
|
||||
|
||||
animateTimer = new FormsTimer
|
||||
{
|
||||
Interval = ANIMATE_TIMER_DEFAULT_INTERVAL
|
||||
};
|
||||
animateTimer.Tick += new EventHandler(AnimationTick);
|
||||
animateTimer.Start();
|
||||
|
||||
fetchTimer = new FormsTimer
|
||||
{
|
||||
Interval = FETCH_TIMER_DEFAULT_INTERVAL
|
||||
};
|
||||
fetchTimer.Tick += new EventHandler(FetchTick);
|
||||
fetchTimer.Start();
|
||||
|
||||
ShowBalloonTipIfNeeded();
|
||||
}
|
||||
|
||||
private static Theme GetSystemTheme()
|
||||
{
|
||||
var keyName = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize";
|
||||
using var rKey = Registry.CurrentUser.OpenSubKey(keyName);
|
||||
if (rKey is null) return Theme.Light;
|
||||
var value = rKey.GetValue("SystemUsesLightTheme");
|
||||
if (value is null) return Theme.Light;
|
||||
return (int)value == 0 ? Theme.Dark : Theme.Light;
|
||||
}
|
||||
|
||||
private bool IsSpeedSourceAvailable(SpeedSource speedSource)
|
||||
{
|
||||
return speedSource switch
|
||||
{
|
||||
SpeedSource.CPU => true,
|
||||
SpeedSource.GPU => gpuRepository.IsAvailable,
|
||||
SpeedSource.Memory => true,
|
||||
_ => false,
|
||||
};
|
||||
}
|
||||
|
||||
private void ResolveSpeedSource()
|
||||
{
|
||||
if (!IsSpeedSourceAvailable(speedSource))
|
||||
{
|
||||
ChangeSpeedSource(SpeedSource.CPU);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowBalloonTipIfNeeded()
|
||||
{
|
||||
if (!cpuRepository.IsAvailable)
|
||||
{
|
||||
contextMenuManager.ShowBalloonTip(BalloonTipType.CPUInfoUnavailable);
|
||||
}
|
||||
else if (UserSettings.Default.FirstLaunch)
|
||||
{
|
||||
contextMenuManager.ShowBalloonTip(BalloonTipType.AppLaunched);
|
||||
UserSettings.Default.FirstLaunch = false;
|
||||
UserSettings.Default.Save();
|
||||
}
|
||||
}
|
||||
|
||||
private void UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e)
|
||||
{
|
||||
if (e.Category != UserPreferenceCategory.General) return;
|
||||
var systemTheme = GetSystemTheme();
|
||||
if (contextMenuManager.HasActiveCustomIcons)
|
||||
{
|
||||
contextMenuManager.RecolorActiveCustomIcons(systemTheme, manualTheme);
|
||||
}
|
||||
else
|
||||
{
|
||||
contextMenuManager.SetIcons(systemTheme, manualTheme, runner);
|
||||
}
|
||||
}
|
||||
|
||||
private static void OpenProjectPage()
|
||||
{
|
||||
try
|
||||
{
|
||||
Process.Start(new ProcessStartInfo()
|
||||
{
|
||||
FileName = "https://github.com/runcat-dev/RunCat365",
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine($"Error: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ChangeRunner(Runner r)
|
||||
{
|
||||
runner = r;
|
||||
customRunnerName = null;
|
||||
UserSettings.Default.Runner = runner.ToString();
|
||||
UserSettings.Default.CustomRunnerName = string.Empty;
|
||||
UserSettings.Default.Save();
|
||||
}
|
||||
|
||||
private void ApplyCustomRunner(string name)
|
||||
{
|
||||
var frames = customRunnerRepository.LoadFrames(name);
|
||||
if (frames.Count == 0) return;
|
||||
customRunnerName = name;
|
||||
UserSettings.Default.CustomRunnerName = name;
|
||||
UserSettings.Default.Save();
|
||||
contextMenuManager.SetCustomIcons(frames, GetSystemTheme(), manualTheme);
|
||||
foreach (var frame in frames) frame.Dispose();
|
||||
}
|
||||
|
||||
private void RevertToBuiltInRunner()
|
||||
{
|
||||
customRunnerName = null;
|
||||
UserSettings.Default.CustomRunnerName = string.Empty;
|
||||
UserSettings.Default.Save();
|
||||
contextMenuManager.SetIcons(GetSystemTheme(), manualTheme, runner);
|
||||
}
|
||||
|
||||
private void HandleCustomRunnerDeleted(string deletedName)
|
||||
{
|
||||
if (!string.Equals(customRunnerName, deletedName, StringComparison.OrdinalIgnoreCase)) return;
|
||||
RevertToBuiltInRunner();
|
||||
}
|
||||
|
||||
private void ChangeManualTheme(Theme t)
|
||||
{
|
||||
manualTheme = t;
|
||||
UserSettings.Default.Theme = manualTheme.ToString();
|
||||
UserSettings.Default.Save();
|
||||
}
|
||||
|
||||
private void ChangeTemperatureUnit(TemperatureUnit u)
|
||||
{
|
||||
temperatureUnit = u;
|
||||
UserSettings.Default.TemperatureUnit = temperatureUnit.ToString();
|
||||
UserSettings.Default.Save();
|
||||
}
|
||||
|
||||
private void ChangeSpeedSource(SpeedSource s)
|
||||
{
|
||||
speedSource = s;
|
||||
UserSettings.Default.SpeedSource = speedSource.ToString();
|
||||
UserSettings.Default.Save();
|
||||
}
|
||||
|
||||
private void ChangeFPSMaxLimit(FPSMaxLimit f)
|
||||
{
|
||||
fpsMaxLimit = f;
|
||||
UserSettings.Default.FPSMaxLimit = fpsMaxLimit.ToString();
|
||||
UserSettings.Default.Save();
|
||||
}
|
||||
|
||||
private void AnimationTick(object? sender, EventArgs e)
|
||||
{
|
||||
contextMenuManager.AdvanceFrame();
|
||||
}
|
||||
|
||||
private string GetInfoDescription(CPUInfo cpuInfo, GPUInfo? gpuInfo, MemoryInfo memoryInfo, TemperatureInfo? temperatureInfo)
|
||||
{
|
||||
var baseDescription = speedSource switch
|
||||
{
|
||||
SpeedSource.CPU => cpuInfo.GetDescription(),
|
||||
SpeedSource.GPU => gpuInfo?.GetDescription() ?? "",
|
||||
SpeedSource.Memory => memoryInfo.GetDescription(),
|
||||
_ => "",
|
||||
};
|
||||
|
||||
var temperatureDescription = temperatureInfo?.GetDescription(temperatureUnit) ?? "";
|
||||
return string.IsNullOrEmpty(temperatureDescription) ? baseDescription : $"{baseDescription}\n{temperatureDescription}";
|
||||
}
|
||||
|
||||
private int CalculateInterval(CPUInfo cpuInfo, GPUInfo? gpuInfo, MemoryInfo memoryInfo)
|
||||
{
|
||||
var load = speedSource switch
|
||||
{
|
||||
SpeedSource.CPU => cpuInfo.Total,
|
||||
SpeedSource.GPU => gpuInfo?.Maximum ?? 0f,
|
||||
SpeedSource.Memory => memoryInfo.MemoryLoad,
|
||||
_ => 0f,
|
||||
};
|
||||
var speed = (float)Math.Max(1.0f, (load / 5.0f) * fpsMaxLimit.GetRate());
|
||||
return (int)(500.0f / speed);
|
||||
}
|
||||
|
||||
private int FetchSystemInfo()
|
||||
{
|
||||
var cpuInfo = cpuRepository.Get();
|
||||
var gpuInfo = gpuRepository.Get();
|
||||
var memoryInfo = memoryRepository.Get();
|
||||
var temperatureInfo = temperatureRepository.Get();
|
||||
var storageInfo = storageRepository.Get();
|
||||
var networkInfo = networkRepository.Get();
|
||||
|
||||
contextMenuManager.SetNotifyIconText(GetInfoDescription(cpuInfo, gpuInfo, memoryInfo, temperatureInfo));
|
||||
|
||||
var systemInfoValues = new List<string>();
|
||||
systemInfoValues.AddRange(cpuInfo.GenerateIndicator());
|
||||
if (gpuInfo.HasValue)
|
||||
{
|
||||
systemInfoValues.AddRange(gpuInfo.Value.GenerateIndicator());
|
||||
}
|
||||
systemInfoValues.AddRange(memoryInfo.GenerateIndicator());
|
||||
if (temperatureInfo.HasValue)
|
||||
{
|
||||
systemInfoValues.AddRange(temperatureInfo.Value.GenerateIndicator(temperatureUnit));
|
||||
}
|
||||
systemInfoValues.AddRange(storageInfo.GenerateIndicator());
|
||||
if (networkInfo.HasValue)
|
||||
{
|
||||
systemInfoValues.AddRange(networkInfo.Value.GenerateIndicator());
|
||||
}
|
||||
contextMenuManager.SetSystemInfoMenuText(string.Join("\n", [.. systemInfoValues]));
|
||||
|
||||
return CalculateInterval(cpuInfo, gpuInfo, memoryInfo);
|
||||
}
|
||||
|
||||
private async void FetchTick(object? sender, EventArgs e)
|
||||
{
|
||||
if (isFetching) return;
|
||||
isFetching = true;
|
||||
try
|
||||
{
|
||||
var doHeavySlice = await Task.Run(() =>
|
||||
{
|
||||
cpuRepository.Update();
|
||||
gpuRepository.Update();
|
||||
fetchCounter += 1;
|
||||
if (fetchCounter < FETCH_COUNTER_SIZE) return false;
|
||||
fetchCounter = 0;
|
||||
temperatureRepository.Update();
|
||||
memoryRepository.Update();
|
||||
storageRepository.Update();
|
||||
networkRepository.Update();
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!doHeavySlice) return;
|
||||
|
||||
var interval = FetchSystemInfo();
|
||||
animateTimer.Stop();
|
||||
animateTimer.Interval = interval;
|
||||
animateTimer.Start();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Console.WriteLine($"FetchTick failed: {exception.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
isFetching = false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
SystemEvents.UserPreferenceChanged -= UserPreferenceChanged;
|
||||
|
||||
animateTimer?.Stop();
|
||||
animateTimer?.Dispose();
|
||||
fetchTimer?.Stop();
|
||||
fetchTimer?.Dispose();
|
||||
|
||||
cpuRepository?.Close();
|
||||
gpuRepository?.Close();
|
||||
temperatureRepository?.Close();
|
||||
|
||||
contextMenuManager?.HideNotifyIcon();
|
||||
contextMenuManager?.Dispose();
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user