From fe12f615ba40dc499e2f0ee93063cf95814d907e Mon Sep 17 00:00:00 2001 From: Natsumi Date: Sat, 15 Jun 2024 11:53:58 +1200 Subject: [PATCH] Update Cef 125.0.210, fix PerformanceCounter bugs --- Dotnet/AppApi/AppApi.cs | 5 + Dotnet/AppApi/AppApiVr.cs | 26 ++--- Dotnet/Cef/CefCustomDownloadHandler.cs | 6 +- Dotnet/CpuMonitor.cs | 90 --------------- Dotnet/Discord.cs | 54 ++++----- Dotnet/Program.cs | 5 +- Dotnet/SystemMonitor.cs | 151 +++++++++++++++++++++++++ VRCX.csproj | 4 +- html/src/app.js | 10 +- html/src/localization/en/en.json | 2 +- html/src/mixins/tabs/settings.pug | 4 +- html/src/vr.js | 17 ++- html/src/vr.pug | 2 +- 13 files changed, 223 insertions(+), 153 deletions(-) delete mode 100644 Dotnet/CpuMonitor.cs create mode 100644 Dotnet/SystemMonitor.cs diff --git a/Dotnet/AppApi/AppApi.cs b/Dotnet/AppApi/AppApi.cs index bed870cb..1cc134e2 100644 --- a/Dotnet/AppApi/AppApi.cs +++ b/Dotnet/AppApi/AppApi.cs @@ -40,6 +40,11 @@ namespace VRCX ProcessMonitor.Instance.ProcessExited += Instance.OnProcessStateChanged; } + public void Init() + { + // Create Instance before Cef tries to bind it + } + /// /// Computes the MD5 hash of the file represented by the specified base64-encoded string. /// diff --git a/Dotnet/AppApi/AppApiVr.cs b/Dotnet/AppApi/AppApiVr.cs index b17b88b2..361233be 100644 --- a/Dotnet/AppApi/AppApiVr.cs +++ b/Dotnet/AppApi/AppApiVr.cs @@ -9,20 +9,15 @@ namespace VRCX public class AppApiVr { public static readonly AppApiVr Instance; - private static readonly PerformanceCounter Uptime; static AppApiVr() { Instance = new AppApiVr(); + } - try - { - Uptime = new PerformanceCounter("System", "System Up Time"); - } - catch - { - Uptime = null; - } + public void Init() + { + // Create Instance before Cef tries to bind it } public void VrInit() @@ -31,13 +26,18 @@ namespace VRCX MainForm.Instance.Browser.ExecuteScriptAsync("$app.vrInit", ""); } + public void ToggleSystemMonitor(bool enabled) + { + SystemMonitor.Instance.Start(enabled); + } + /// /// Returns the current CPU usage as a percentage. /// /// The current CPU usage as a percentage. public float CpuUsage() { - return CpuMonitor.Instance.CpuUsage; + return SystemMonitor.Instance.CpuUsage; } /// @@ -56,11 +56,7 @@ namespace VRCX /// The number of milliseconds that the system has been running. public double GetUptime() { - if (Uptime == null) - return 0; - - Uptime.NextValue(); - return TimeSpan.FromSeconds(Uptime.NextValue()).TotalMilliseconds; + return SystemMonitor.Instance.UpTime; } /// diff --git a/Dotnet/Cef/CefCustomDownloadHandler.cs b/Dotnet/Cef/CefCustomDownloadHandler.cs index c58ce9a8..3a43b02c 100644 --- a/Dotnet/Cef/CefCustomDownloadHandler.cs +++ b/Dotnet/Cef/CefCustomDownloadHandler.cs @@ -15,10 +15,10 @@ namespace VRCX return true; } - public void OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) + public bool OnBeforeDownload(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback) { if (callback.IsDisposed) - return; + return true; using (callback) { @@ -27,6 +27,8 @@ namespace VRCX showDialog: true ); } + + return true; } public void OnDownloadUpdated(IWebBrowser chromiumWebBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback) diff --git a/Dotnet/CpuMonitor.cs b/Dotnet/CpuMonitor.cs deleted file mode 100644 index ab4154f2..00000000 --- a/Dotnet/CpuMonitor.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright(c) 2019-2022 pypy, Natsumi and individual contributors. -// All rights reserved. -// -// This work is licensed under the terms of the MIT license. -// For a copy, see . - -using System.Diagnostics; -using System.Threading; - -namespace VRCX -{ - public class CpuMonitor - { - public static readonly CpuMonitor Instance; - public float CpuUsage; - private readonly PerformanceCounter _performanceCounter; - private readonly Timer _timer; - - static CpuMonitor() - { - Instance = new CpuMonitor(); - } - - public CpuMonitor() - { - try - { - _performanceCounter = new PerformanceCounter( - "Processor Information", - "% Processor Utility", - "_Total", - true - ); - } - catch - { - } - - // fallback - if (_performanceCounter == null) - { - try - { - _performanceCounter = new PerformanceCounter( - "Processor", - "% Processor Time", - "_Total", - true - ); - } - catch - { - } - } - - _timer = new Timer(TimerCallback, null, -1, -1); - } - - internal void Init() - { - _timer.Change(1000, 1000); - } - - internal void Exit() - { - lock (this) - { - _timer.Change(-1, -1); - _performanceCounter?.Dispose(); - } - } - - private void TimerCallback(object state) - { - lock (this) - { - try - { - if (_performanceCounter != null) - { - CpuUsage = _performanceCounter.NextValue(); - } - } - catch - { - } - } - } - } -} diff --git a/Dotnet/Discord.cs b/Dotnet/Discord.cs index 9b1f1646..4841d8e6 100644 --- a/Dotnet/Discord.cs +++ b/Dotnet/Discord.cs @@ -65,7 +65,7 @@ namespace VRCX if (m_Client == null && m_Active) { m_Client = new DiscordRpcClient(DiscordAppId); - if (m_Client.Initialize() == false) + if (!m_Client.Initialize()) { m_Client.Dispose(); m_Client = null; @@ -117,18 +117,18 @@ namespace VRCX public void SetText(string details, string state) { - if (m_Client != null && !m_Lock.IsReadLockHeld) + if (m_Client == null || m_Lock.IsReadLockHeld) + return; + + m_Lock.EnterWriteLock(); + try { - m_Lock.EnterWriteLock(); - try - { - m_Presence.Details = LimitByteLength(details, 127); - m_Presence.State = LimitByteLength(state, 127); - } - finally - { - m_Lock.ExitWriteLock(); - } + m_Presence.Details = LimitByteLength(details, 127); + m_Presence.State = LimitByteLength(state, 127); + } + finally + { + m_Lock.ExitWriteLock(); } } @@ -137,17 +137,15 @@ namespace VRCX m_Lock.EnterWriteLock(); try { - if (string.IsNullOrEmpty(largeKey) == true && - string.IsNullOrEmpty(smallKey) == true) + if (string.IsNullOrEmpty(largeKey) && + string.IsNullOrEmpty(smallKey)) { m_Presence.Assets = null; } else { - if (m_Presence.Assets == null) - m_Presence.Assets = new Assets(); - if (m_Presence.Party == null) - m_Presence.Party = new Party(); + m_Presence.Assets ??= new Assets(); + m_Presence.Party ??= new Party(); m_Presence.Assets.LargeImageKey = largeKey; m_Presence.Assets.LargeImageText = largeText; m_Presence.Assets.SmallImageKey = smallKey; @@ -155,15 +153,15 @@ namespace VRCX m_Presence.Party.ID = partyId; m_Presence.Party.Size = partySize; m_Presence.Party.Max = partyMax; - Button[] Buttons = { }; + Button[] buttons = []; if (!string.IsNullOrEmpty(buttonUrl)) { - Buttons = new Button[] - { - new Button() { Label = buttonText, Url = buttonUrl } - }; + buttons = + [ + new Button { Label = buttonText, Url = buttonUrl } + ]; } - m_Presence.Buttons = Buttons; + m_Presence.Buttons = buttons; if (DiscordAppId != appId) { DiscordAppId = appId; @@ -181,7 +179,7 @@ namespace VRCX m_Lock.ExitWriteLock(); } } - + public void SetTimestamps(double startUnixMilliseconds, double endUnixMilliseconds) { var _startUnixMilliseconds = (ulong)startUnixMilliseconds; @@ -196,11 +194,7 @@ namespace VRCX } else { - if (m_Presence.Timestamps == null) - { - m_Presence.Timestamps = new Timestamps(); - } - + m_Presence.Timestamps ??= new Timestamps(); m_Presence.Timestamps.StartUnixMilliseconds = _startUnixMilliseconds; if (_endUnixMilliseconds == 0) diff --git a/Dotnet/Program.cs b/Dotnet/Program.cs index aa53ec07..036b3f6a 100644 --- a/Dotnet/Program.cs +++ b/Dotnet/Program.cs @@ -144,7 +144,8 @@ namespace VRCX ProcessMonitor.Instance.Init(); VRCXStorage.Load(); SQLiteLegacy.Instance.Init(); - CpuMonitor.Instance.Init(); + AppApi.Instance.Init(); + AppApiVr.Instance.Init(); Discord.Instance.Init(); WorldDBManager.Instance.Init(); WebApi.Instance.Init(); @@ -166,7 +167,7 @@ namespace VRCX WorldDBManager.Instance.Stop(); Discord.Instance.Exit(); - CpuMonitor.Instance.Exit(); + SystemMonitor.Instance.Exit(); VRCXStorage.Save(); SQLiteLegacy.Instance.Exit(); ProcessMonitor.Instance.Exit(); diff --git a/Dotnet/SystemMonitor.cs b/Dotnet/SystemMonitor.cs new file mode 100644 index 00000000..b473f340 --- /dev/null +++ b/Dotnet/SystemMonitor.cs @@ -0,0 +1,151 @@ +// Copyright(c) 2019-2022 pypy, Natsumi and individual contributors. +// All rights reserved. +// +// This work is licensed under the terms of the MIT license. +// For a copy, see . + +using System; +using System.Diagnostics; +using System.Threading; +using NLog; + +namespace VRCX +{ + public class SystemMonitor + { + public static readonly SystemMonitor Instance; + public float CpuUsage; + public double UpTime; + private bool _enabled; + private PerformanceCounter _performanceCounterCpuUsage; + private PerformanceCounter _performanceCounterUpTime; + private Thread _thread; + private static readonly NLog.Logger logger = NLog.LogManager.GetLogger("VRCX"); + + static SystemMonitor() + { + Instance = new SystemMonitor(); + } + + public void Start(bool enabled) + { + if (enabled == _enabled) + return; + + _enabled = enabled; + if (enabled) + StartThread(); + else + Exit(); + } + + internal void Exit() + { + CpuUsage = 0; + UpTime = 0; + try + { + if (_thread != null) + { + _thread.Interrupt(); + _thread.Join(); + _thread = null; + } + } + catch (ThreadInterruptedException) + { + } + + _performanceCounterCpuUsage?.Dispose(); + _performanceCounterCpuUsage = null; + _performanceCounterUpTime?.Dispose(); + _performanceCounterUpTime = null; + } + + private void StartThread() + { + Exit(); + + try + { + _performanceCounterCpuUsage = new PerformanceCounter( + "Processor Information", + "% Processor Utility", + "_Total", + true + ); + _performanceCounterCpuUsage?.NextValue(); + } + catch (Exception ex) + { + logger.Warn($"Failed to create \"Processor Utility\" PerformanceCounter ${ex}"); + } + + // fallback + if (_performanceCounterCpuUsage == null) + { + try + { + _performanceCounterCpuUsage = new PerformanceCounter( + "Processor", + "% Processor Time", + "_Total", + true + ); + _performanceCounterCpuUsage?.NextValue(); + } + catch (Exception ex) + { + logger.Warn($"Failed to create \"Processor Time\" PerformanceCounter ${ex}"); + } + } + + try + { + _performanceCounterUpTime = new PerformanceCounter("System", "System Up Time"); + _performanceCounterUpTime?.NextValue(); + } + catch + { + logger.Warn("Failed to create \"System Up Time\" PerformanceCounter"); + } + + if (_performanceCounterCpuUsage == null && + _performanceCounterUpTime == null) + { + logger.Error("Failed to create any PerformanceCounter"); + return; + } + logger.Info("SystemMonitor started"); + + _thread = new Thread(ThreadProc) + { + IsBackground = true + }; + _thread.Start(); + } + + private void ThreadProc() + { + try + { + while (_enabled) + { + if (_performanceCounterCpuUsage != null) + CpuUsage = _performanceCounterCpuUsage.NextValue(); + + if (_performanceCounterUpTime != null) + UpTime = TimeSpan.FromSeconds(_performanceCounterUpTime.NextValue()).TotalMilliseconds; + + Thread.Sleep(1000); + } + } + catch (Exception ex) + { + logger.Warn($"SystemMonitor thread exception: {ex}"); + } + + Exit(); + } + } +} diff --git a/VRCX.csproj b/VRCX.csproj index 0cb5913d..65cf1c41 100644 --- a/VRCX.csproj +++ b/VRCX.csproj @@ -119,8 +119,8 @@ - - + + diff --git a/html/src/app.js b/html/src/app.js index 6fb10797..4fd63ee7 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -15225,8 +15225,8 @@ speechSynthesis.getVoices(); 'VRCX_hideDevicesFromFeed', false ); - $app.data.hideCpuUsageFromFeed = await configRepository.getBool( - 'VRCX_hideCpuUsageFromFeed', + $app.data.vrOverlayCpuUsage = await configRepository.getBool( + 'VRCX_vrOverlayCpuUsage', false ); $app.data.hideUptimeFromFeed = await configRepository.getBool( @@ -15429,8 +15429,8 @@ speechSynthesis.getVoices(); this.hideDevicesFromFeed ); await configRepository.setBool( - 'VRCX_hideCpuUsageFromFeed', - this.hideCpuUsageFromFeed + 'VRCX_vrOverlayCpuUsage', + this.vrOverlayCpuUsage ); await configRepository.setBool( 'VRCX_hideUptimeFromFeed', @@ -16155,7 +16155,7 @@ speechSynthesis.getVoices(); var VRConfigVars = { overlayNotifications: this.overlayNotifications, hideDevicesFromFeed: this.hideDevicesFromFeed, - hideCpuUsageFromFeed: this.hideCpuUsageFromFeed, + vrOverlayCpuUsage: this.vrOverlayCpuUsage, minimalFeed: this.minimalFeed, notificationPosition: this.notificationPosition, notificationTimeout: this.notificationTimeout, diff --git a/html/src/localization/en/en.json b/html/src/localization/en/en.json index 858c8b73..c747e1c3 100644 --- a/html/src/localization/en/en.json +++ b/html/src/localization/en/en.json @@ -374,7 +374,7 @@ "background_color": "Background Color", "minimal_feed_icons": "Minimal Feed Icons", "hide_vr_devices": "Hide VR Devices", - "hide_cpu_usage": "Hide CPU Usage", + "show_cpu_usage": "Show CPU Usage", "hide_game_uptime": "Hide Game Uptime", "show_pc_uptime": "Show PC Uptime", "wrist_feed_filters": "Wrist Feed Filters" diff --git a/html/src/mixins/tabs/settings.pug b/html/src/mixins/tabs/settings.pug index c84bb367..92a4c8b0 100644 --- a/html/src/mixins/tabs/settings.pug +++ b/html/src/mixins/tabs/settings.pug @@ -372,8 +372,8 @@ mixin settingsTab() span.name {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.hide_vr_devices') }} el-switch(v-model="hideDevicesFromFeed" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist") div.options-container-item - span.name {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.hide_cpu_usage') }} - el-switch(v-model="hideCpuUsageFromFeed" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist") + span.name {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.show_cpu_usage') }} + el-switch(v-model="vrOverlayCpuUsage" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist") div.options-container-item span.name {{ $t('view.settings.wrist_overlay.steamvr_wrist_overlay.hide_game_uptime') }} el-switch(v-model="hideUptimeFromFeed" @change="saveOpenVROption" :disabled="!openVR || !overlayWrist") diff --git a/html/src/vr.js b/html/src/vr.js index c862b23d..926332e4 100644 --- a/html/src/vr.js +++ b/html/src/vr.js @@ -194,8 +194,11 @@ Vue.component('marquee-text', MarqueeText); // 2 = 항상 화면에 보이는 거 appType: location.href.substr(-1), appLanguage: 'en', + currentCulture: 'en-nz', currentTime: new Date().toJSON(), + cpuUsageEnabled: false, cpuUsage: 0, + pcUptimeEnabled: false, pcUptime: '', customInfo: '', config: {}, @@ -351,6 +354,16 @@ Vue.component('marquee-text', MarqueeText); this.setDatetimeFormat(); this.setAppLanguage(this.config.appLanguage); this.updateFeedLength(); + if ( + this.config.vrOverlayCpuUsage !== this.cpuUsageEnabled || + this.config.pcUptimeOnFeed !== this.pcUptimeEnabled + ) { + this.cpuUsageEnabled = this.config.vrOverlayCpuUsage; + this.pcUptimeEnabled = this.config.pcUptimeOnFeed; + AppApiVr.ToggleSystemMonitor( + this.cpuUsageEnabled || this.pcUptimeEnabled + ); + } }; $app.methods.updateOnlineFriendCount = function (count) { @@ -435,7 +448,7 @@ Vue.component('marquee-text', MarqueeText); .replace(' PM', ' pm') .replace(',', ''); - if (!this.config.hideCpuUsageFromFeed) { + if (this.cpuUsageEnabled) { var cpuUsage = await AppApiVr.CpuUsage(); this.cpuUsage = cpuUsage.toFixed(0); } @@ -802,8 +815,6 @@ Vue.component('marquee-text', MarqueeText); this.hudTimeout = JSON.parse(json); }; - $app.data.currentCulture = await AppApiVr.CurrentCulture(); - $app.methods.setDatetimeFormat = async function () { this.currentCulture = await AppApiVr.CurrentCulture(); var formatDate = function (date) { diff --git a/html/src/vr.pug b/html/src/vr.pug index 3f53cdca..f57b11ee 100644 --- a/html/src/vr.pug +++ b/html/src/vr.pug @@ -532,7 +532,7 @@ html span(style="display:inline-block;font-weight:bold") {{ lastLocation.friendList.length !== 0 ? `‎‎‎‎‎‎‎‎‏‏‎ ‎(${lastLocation.friendList.length})` : ''}} br span(style="float:right") {{ currentTime }} - span(v-if="config && !config.hideCpuUsageFromFeed" style="display:inline-block;margin-right:5px") {{ $t('vr.status.cpu') }} {{ cpuUsage }}% + span(v-if="config && cpuUsageEnabled" style="display:inline-block;margin-right:5px") {{ $t('vr.status.cpu') }} {{ cpuUsage }}% span(style="display:inline-block") {{ $t('vr.status.online') }} {{ onlineFriendCount }} ‎{{ customInfo }} template(v-else) svg(class="np-progress-circle")