123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- using BsWidget.BeatSaberHttpStatus;
- using BsWidget.BsYurHttpStatus;
- using BsWidgetShareCodes;
- using OpenHardwareMonitor.Hardware;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Drawing2D;
- using System.IO;
- using System.Linq;
- using System.Windows.Forms;
- namespace BsWidget
- {
- internal class MainForm : BaseForm
- {
- private const string FontName = "arial";
- private Timer _updateTimer;
- private Timer _pcTimer;
- private BlockingCollection<UpdateFlags> _queue;
- private BeatSaberHttpStatusClient _client;
- private BsYurHttpStatusClient _yurClient;
- private Computer _computer;
- private Font _smallFont = new Font(FontName, 12, FontStyle.Regular, GraphicsUnit.Pixel);
- private Font _mediumFont = new Font(FontName, 20, FontStyle.Regular, GraphicsUnit.Pixel);
- [Flags]
- private enum UpdateFlags
- {
- BeatMap = 1 << 0,
- Performance = 1 << 1,
- NoteFullyCut = 1 << 2,
- ClearAll = 1 << 3,
- RefreshAll = 1 << 4,
- CpuAndGpu = 1 << 5,
- }
- public Image SongIcon { get; set; } = Properties.Resources.sample_cover;
- public string SongName { get; set; } = "Song Name";
- public string SongSubName { get; set; } = "Song Sub Name";
- public string SongArtist { get; set; } = "Song Artist";
- public string BeatMapper { get; set; } = "Mapper";
- public string Difficulty { get; set; } = "Difficulty";
- public double SongBpm { get; set; } = 123;
- public double SongNjs { get; set; } = 23;
- public int CurrentScore { get; set; } = 12345;
- public int CurrentCombo { get; set; } = 120;
- public string CurrentRank { get; set; } = "XX";
- public int CurrentMaxScore { get; set; }
- public int CurrentCutPerSecond { get; set; }
- public int CurrentMissed { get; set; }
- public float? CpuUsage { get; set; } = 12.3f;
- public float? CpuGhz { get; set; } = 2.333f;
- public float? GpuUsage { get; set; } = 45.6f;
- public YurStatus YurStatus { get; set; } = new YurStatus { HeartRate = 0, KcalPerMin = 0 };
- /// <summary> FinalScore,CurrentCombo </summary>
- public List<HistoryModel> CutHistory { get; set; }
- public class HistoryModel
- {
- public DateTime Time { get; set; }
- public int CutScore { get; set; }
- public int CurrentCombo { get; set; }
- public float Percent { get; set; }
- public int CutsPerSecond { get; set; }
- public float HeartRate { get; set; }
- public float KcalPerMin { get; set; }
- }
- public MainForm()
- {
- KeyPreview = true;
- FormBorderStyle = FormBorderStyle.None;
- _client = new BeatSaberHttpStatusClient();
- _client.Event += BeatSaber_Event;
- _yurClient = new BsYurHttpStatusClient();
- _yurClient.Event += Yur_Event;
- if (Program.NoPerf)
- {
- }
- else
- {
- _computer = new Computer();
- _computer.CPUEnabled = true;
- _computer.GPUEnabled = true;
- _computer.FanControllerEnabled = false;
- _computer.HDDEnabled = false;
- _computer.MainboardEnabled = false;
- _computer.RAMEnabled = false;
- _computer.Open();
- _pcTimer = new Timer { Interval = 750 };
- _pcTimer.Tick += PcTimer_Tick;
- }
- _queue = new BlockingCollection<UpdateFlags>();
- CutHistory = new List<HistoryModel>();
- _updateTimer = new Timer { Interval = 25 };
- _updateTimer.Tick += UpdateTimer_Tick;
- }
- protected override void OnLoad(EventArgs e)
- {
- Text = "Beat Saber Status Widget";
- base.OnLoad(e);
- Font = new Font(FontName, 30, FontStyle.Bold, GraphicsUnit.Pixel);
- TopMost = false;
- WindowState = FormWindowState.Normal;
- _updateTimer.Start();
- _client.Start();
- _yurClient.Start();
- _pcTimer?.Start();
- }
- protected override void OnMouseDoubleClick(MouseEventArgs e)
- {
- base.OnMouseDoubleClick(e);
- _queue.Add(UpdateFlags.ClearAll);
- }
- protected override void OnShown(EventArgs e)
- {
- WindowState = FormWindowState.Normal;
- Location = Screen.PrimaryScreen.Bounds.Location;
- ClientSize = Screen.PrimaryScreen.Bounds.Size;
- TopMost = true;
- base.OnShown(e);
- if (_queue.Count == 0)
- {
- _queue.Add(UpdateFlags.RefreshAll);
- }
- }
- protected override void OnFormClosing(FormClosingEventArgs e)
- {
- _updateTimer.Stop();
- _pcTimer.Stop();
- base.OnFormClosing(e);
- }
- protected override void OnFormClosed(FormClosedEventArgs e)
- {
- _client.Stop();
- _yurClient.Stop();
- _computer?.Close();
- base.OnFormClosed(e);
- }
- protected override void OnKeyDown(KeyEventArgs e)
- {
- base.OnKeyDown(e);
- if (e.KeyCode == Keys.Escape) Application.Exit();
- }
- private void BeatSaber_Event(object sender, BeatSaberStatusEventArgs e)
- {
- var flags = (UpdateFlags)0;
- if (e.Event == "menu" && CutHistory.Count > 0)
- {
- lock (CutHistory)
- {
- CutHistory.Clear();
- }
- flags |= UpdateFlags.ClearAll;
- CurrentMissed = 0;
- }
- if (null != e.Status.Beatmap)
- {
- CurrentMissed = 0;
- var bytes = Convert.FromBase64String(e.Status.Beatmap.SongCover);
- using var stream = new MemoryStream(bytes);
- var newImg = Image.FromStream(stream);
- var old = SongIcon;
- SongIcon = newImg;
- old?.Dispose();
- SongName = e.Status.Beatmap.SongName;
- SongSubName = e.Status.Beatmap.SongSubName;
- SongArtist = e.Status.Beatmap.SongAuthorName;
- BeatMapper = e.Status.Beatmap.LevelAuthorName;
- if (string.IsNullOrEmpty(BeatMapper)) BeatMapper = "Unknown Mapper";
- Difficulty = e.Status.Beatmap.Difficulty.ToUpper();
- if (Difficulty == "EXPERTPLUS") Difficulty = "EXPERT+";
- SongBpm = e.Status.Beatmap.SongBPM;
- SongNjs = e.Status.Beatmap.NoteJumpSpeed;
- flags |= UpdateFlags.BeatMap;
- }
- if (null != e.Status.Performance)
- {
- CurrentScore = e.Status.Performance.Score;
- CurrentCombo = e.Status.Performance.Combo;
- CurrentRank = e.Status.Performance.Rank;
- CurrentMaxScore = e.Status.Performance.CurrentMaxScore;
- flags |= UpdateFlags.Performance;
- }
- if (e.Event == "noteMissed")
- {
- ++CurrentMissed;
- flags |= UpdateFlags.Performance;
- }
- if (e.Event == "noteFullyCut" && null != e.NoteCut?.FinalScore)
- {
- lock (CutHistory)
- {
- var now = DateTime.Now;
- var preNow = now.AddSeconds(-1);
- CurrentCutPerSecond = CutHistory.Count(p => p.Time > preNow);
- CutHistory.Add(new HistoryModel
- {
- Time = now,
- CutScore = e.NoteCut.FinalScore.Value,
- CurrentCombo = CurrentCombo,
- Percent = CurrentMaxScore == 0 ? 0 : ((float)CurrentScore / CurrentMaxScore),
- CutsPerSecond = CurrentCutPerSecond,
- HeartRate = YurStatus.HeartRate,
- KcalPerMin = YurStatus.KcalPerMin,
- });
- }
- flags |= UpdateFlags.NoteFullyCut;
- }
- if (flags == 0)
- {
- }
- else
- {
- _queue.Add(flags);
- }
- }
- private void Yur_Event(object sender, YurStatus e)
- {
- YurStatus = e;
- _queue.Add(UpdateFlags.Performance);
- }
- private void PcTimer_Tick(object sender, EventArgs e)
- {
- try
- {
- foreach (var hardware in _computer.Hardware.Where(p => p.HardwareType == HardwareType.CPU || p.HardwareType == HardwareType.GpuNvidia || p.HardwareType == HardwareType.GpuAti))
- {
- hardware.Update();
- }
- }
- catch (Exception)
- {
- return;
- }
- try
- {
- var mhz = _computer.Hardware.Where(p => p.HardwareType == HardwareType.CPU).SelectMany(p => p.Sensors)
- .Where(p => p.SensorType == SensorType.Clock).Select(p => p.Value).Max();
- if (mhz.HasValue)
- {
- CpuGhz = mhz / 1000f;
- }
- else
- {
- CpuGhz = null;
- }
- }
- catch
- {
- CpuGhz = null;
- }
- try
- {
- CpuUsage = _computer.Hardware.Where(p => p.HardwareType == HardwareType.CPU).SelectMany(p => p.Sensors)
- .Where(p => p.SensorType == SensorType.Load && p.Name == "CPU Total").Select(p => p.Value).Max();
- }
- catch
- {
- CpuUsage = null;
- }
- try
- {
- GpuUsage = _computer.Hardware.Where(p => p.HardwareType == HardwareType.GpuNvidia || p.HardwareType == HardwareType.GpuAti).SelectMany(p => p.Sensors)
- .Where(p => p.SensorType == SensorType.Load && p.Name == "GPU Core").Select(p => p.Value).Max();
- }
- catch
- {
- GpuUsage = null;
- }
- _queue.Add(UpdateFlags.CpuAndGpu);
- }
- private void UpdateTimer_Tick(object sender, EventArgs e)
- {
- if (_queue.Count == 0) return;
- UpdateGraphic();
- }
- protected override void RenderGraphic(Graphics g)
- {
- const int margin = 10;
- const int chHeight = 64;
- var chWidth = ViewSize.Width - margin * 2;
- if (_queue.Count == 0) return;
- var flags = _queue.Take();
- if (flags.HasFlag(UpdateFlags.ClearAll)) g.Clear(Color.Transparent);
- g.SetHighQuality();
- if (flags.HasFlag(UpdateFlags.NoteFullyCut) || flags.HasFlag(UpdateFlags.RefreshAll))
- {
- const int chLeft = margin;
- const int chTop = margin;
- var chBottom = chTop + chHeight;
- float RecordPixel = 2.5f;
- var LineWidth = 1.5f;
- var displayCount = (int)(chWidth / RecordPixel);
- using var scorePen = new Pen(Color.FromArgb(200, 255, 0, 0), LineWidth) { LineJoin = LineJoin.Round };
- using var comboPen = new Pen(Color.FromArgb(200, 0, 0, 255), LineWidth) { LineJoin = LineJoin.Round };
- using var percentPen = new Pen(Color.FromArgb(200, Color.Green), LineWidth) { LineJoin = LineJoin.Round };
- using var cpsPen = new Pen(Color.Yellow, LineWidth) { LineJoin = LineJoin.Round };
- using var heartPen = new Pen(Color.FromArgb(200, Color.White), LineWidth) { LineJoin = LineJoin.Round };
- using var calPen = new Pen(Color.FromArgb(200, Color.Lime), LineWidth) { LineJoin = LineJoin.Round };
- //clear region 0,210 Wx200
- g.ClearRect(Color.Transparent, 0, chTop - 3, ViewSize.Width, chHeight + 6);
- //draw chart
- using var bgBrush = new SolidBrush(Color.FromArgb(90, 255, 255, 255));
- g.FillRectangle(bgBrush, chLeft, chTop, chWidth, chHeight);
- g.DrawRectangle(Pens.White, chLeft - 1, chTop - 1, chWidth + 2, chHeight + 2);
- if (CutHistory.Count > 1)
- {
- HistoryModel[] items;
- lock (CutHistory)
- {
- var skip = CutHistory.Count > displayCount ? CutHistory.Count - displayCount : 0;
- items = CutHistory.Skip(skip).ToArray();
- }
- if (items.Length > 1)
- {
- var minScore = items.Select(p => p.CutScore).Min();
- var maxScore = items.Select(p => p.CutScore).Max();
- var rngScore = maxScore - minScore;
- if (rngScore < 1) rngScore = 1;
- var minCombo = items.Select(p => p.CurrentCombo).Min();
- var maxCombo = items.Select(p => p.CurrentCombo).Max();
- var rngCombo = maxCombo - minCombo;
- if (rngCombo < 1) rngCombo = 1;
- var minHea = items.Select(p => p.HeartRate).Min();
- var maxHea = items.Select(p => p.HeartRate).Max();
- var rngHea = maxHea - minHea;
- if (rngHea < 1) rngHea = 1;
- var minCal = items.Select(p => p.KcalPerMin).Min();
- var maxCal = items.Select(p => p.KcalPerMin).Max();
- var rngCal = maxCal - minCal;
- if (rngCal < 1) rngCal = 1;
- var minCps = items.Select(p => p.CutsPerSecond).Min();
- var maxCps = items.Select(p => p.CutsPerSecond).Max();
- var rngCps = maxCps - minCps;
- if (rngCps < 1) rngCps = 1;
- var x = chWidth - items.Length * RecordPixel + chLeft + 1;
- var scorePts = new List<PointF>();
- var comboPts = new List<PointF>();
- var percentPts = new List<PointF>();
- var heaPts = new List<PointF>();
- var calPts = new List<PointF>();
- var cpsPts = new List<PointF>();
- for (var i = 0; i < items.Length; i++)
- {
- var item = items[i];
- scorePts.Add(new PointF(x + RecordPixel * i, (chBottom - 1) - (chHeight - 2) * ((float)(item.CutScore - minScore) / rngScore)));
- comboPts.Add(new PointF(x + RecordPixel * i, (chBottom - 1) - (chHeight - 2) * ((float)(item.CurrentCombo - minCombo) / rngCombo)));
- percentPts.Add(new PointF(x + RecordPixel * i, (chBottom - 1) - (chHeight - 2) * item.Percent));
- cpsPts.Add(new PointF(x + RecordPixel * i, (chBottom - 1) - (chHeight - 2) * ((float)(item.CutsPerSecond - minCps) / rngCps)));
- heaPts.Add(new PointF(x + RecordPixel * i, (chBottom - 1) - (chHeight - 2) * ((item.HeartRate - minHea) / rngHea)));
- calPts.Add(new PointF(x + RecordPixel * i, (chBottom - 1) - (chHeight - 2) * ((item.KcalPerMin - minCal) / rngCal)));
- }
- g.DrawLines(scorePen, scorePts.ToArray());
- g.DrawLines(comboPen, comboPts.ToArray());
- g.DrawLines(percentPen, percentPts.ToArray());
- g.DrawLines(cpsPen, cpsPts.ToArray());
- g.DrawLines(heartPen, heaPts.ToArray());
- g.DrawLines(calPen, calPts.ToArray());
- }
- }
- }
- if (flags.HasFlag(UpdateFlags.Performance) || flags.HasFlag(UpdateFlags.RefreshAll))
- {
- float x = margin;
- float y = chHeight + margin * 2;
- //clear region 0,180 Wx180
- g.ClearRect(Color.Transparent, 0, y, ViewSize.Width, 50);
- //draw text
- var sz = g.DrawStringWithOutline($"{CurrentScore:N0}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithRoundedRect("POINT", _smallFont, x, y + margin * 1.5f, Brushes.White, Brushes.Red,
- 2.5f);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithOutline($"{CurrentCombo}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithRoundedRect("COMBO", _smallFont, x, y + margin * 1.5f, Brushes.White, Brushes.Blue,
- 2.5f);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithOutline($"{CurrentRank}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithRoundedRect("RANK", _smallFont, x, y + margin * 1.5f, Brushes.White,
- Brushes.Green, 2.5f);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithOutline($"{CurrentCutPerSecond}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithRoundedRect("CUT / SEC", _smallFont, x, y + margin * 1.5f, Brushes.Black,
- Brushes.Yellow, 2.5f);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithOutline($"{YurStatus.HeartRate:N1}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithRoundedRect("HEART", _smallFont, x, y + margin * 1.5f, Brushes.Black,
- Brushes.White, 2.5f);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithOutline($"{YurStatus.KcalPerMin:N1}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithRoundedRect("KCAL / MIN", _smallFont, x, y + margin * 1.5f, Brushes.Black,
- Brushes.Lime, 2.5f);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithOutline($"{CurrentMissed}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- // ReSharper disable once RedundantAssignment
- sz = g.DrawStringWithRoundedRect("MISS", _smallFont, x, y + margin * 1.5f, Brushes.White,
- Brushes.Purple, 2.5f);
- }
- if (flags.HasFlag(UpdateFlags.BeatMap) || flags.HasFlag(UpdateFlags.RefreshAll))
- {
- TopMost = false;
- Application.DoEvents();
- TopMost = true;
- Application.DoEvents();
- BringToFront();
- Application.DoEvents();
- const int coverSize = 130;
- g.ClearRect(Color.Transparent, 0, ViewSize.Height - (coverSize + 100), ViewSize.Width, coverSize + 100);
- var coverLeft = margin;
- var coverTop = ViewSize.Height - coverSize - margin;
- g.DrawImage(SongIcon, coverLeft, coverTop, coverSize, coverSize);
- g.DrawRectangle(Pens.White, coverLeft, coverTop, coverSize, coverSize);
- g.DrawStringWithRoundedRect(Difficulty, Font, coverLeft, coverTop - 100, Brushes.Black, Brushes.White);
- {
- float x = margin;
- var y = coverTop - 50;
- var sz = g.DrawStringWithOutline($"{SongBpm}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithRoundedRect("BPM", _smallFont, x, y + margin * 1.5f, Brushes.Black, Brushes.White, 2.5f);
- x += sz.Width + margin * 2;
- sz = g.DrawStringWithOutline($"{SongNjs}", Font, x, y, Color.Black, Color.White);
- x += sz.Width + margin * 2;
- // ReSharper disable once RedundantAssignment
- sz = g.DrawStringWithRoundedRect("NJS", _smallFont, x, y + margin * 1.5f, Brushes.Black, Brushes.White, 2.5f);
- }
- {
- var bootom = ViewSize.Height - margin;
- var left = coverSize + margin * 2;
- var fontDeltaH = 1.3f;
- var sz = g.MeasureString(SongName, _mediumFont);
- g.DrawStringWithOutline(SongName, _mediumFont, left, bootom - sz.Height * fontDeltaH, Color.Black, Color.White);
- sz = g.MeasureString(SongSubName, _mediumFont);
- g.DrawStringWithOutline(SongSubName, _mediumFont, left, bootom - (sz.Height * fontDeltaH) * 2, Color.Black, Color.White);
- sz = g.MeasureString(SongArtist, _mediumFont);
- g.DrawStringWithOutline(SongArtist, _mediumFont, left, bootom - (sz.Height * fontDeltaH) * 3, Color.Black, Color.White);
- sz = g.MeasureString(BeatMapper, _mediumFont);
- g.DrawStringWithOutline(BeatMapper, _mediumFont, left, bootom - (sz.Height * fontDeltaH) * 4, Color.Black, Color.White);
- }
- }
- if (Program.NoPerf == false && (flags.HasFlag(UpdateFlags.CpuAndGpu) || flags.HasFlag(UpdateFlags.RefreshAll)))
- {
- var width = 800;
- var height = 50;
- var field = 400;
- var left = ViewSize.Width - width;
- var top = ViewSize.Height - height;
- g.ClearRect(Color.Transparent, left, top, width, height);
- g.DrawStringWithOutline($"CPU: {CpuGhz:N1}GHz {CpuUsage:N1}%", Font, left, top, Color.Black, Color.White);
- g.DrawStringWithOutline($"GPU: {GpuUsage:N0}%", Font, left + field, top, Color.Black, Color.White);
- }
- }
- }
- }
|