From 226854f6c8165ee8fd08dde38a7c679c8e3568b8 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Tue, 11 Apr 2023 12:17:47 -0700 Subject: [PATCH 1/2] feat: add thermal states to powerMonitor --- docs/api/power-monitor.md | 14 +++++- lib/browser/api/power-monitor.ts | 5 ++ .../browser/api/electron_api_power_monitor.cc | 47 +++++++++++++++++++ .../browser/api/electron_api_power_monitor.h | 7 ++- spec/api-power-monitor-spec.ts | 6 +++ 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index 1292ea1f05fce..f87c2fb9a8122 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -24,6 +24,14 @@ Emitted when the system changes to AC power. Emitted when system changes to battery power. +### Event: 'thermal-state-change' _macOS_ + +* `state` string - one of `unknown`, `nominal`, `fair`, `serious`, `critical`. + +### Event: 'speed-limit-change' _macOS_ + +* `limit` number + ### Event: 'shutdown' _Linux_ _macOS_ Emitted when the system is about to reboot or shut down. If the event handler @@ -55,7 +63,7 @@ The `powerMonitor` module has the following methods: * `idleThreshold` Integer -Returns `string` - The system's current state. Can be `active`, `idle`, `locked` or `unknown`. +Returns `string` - The system's current idle state. Can be `active`, `idle`, `locked` or `unknown`. Calculate the system idle state. `idleThreshold` is the amount of time (in seconds) before considered idle. `locked` is available on supported systems only. @@ -66,6 +74,10 @@ Returns `Integer` - Idle time in seconds Calculate system idle time in seconds. +### `powerMonitor.getCurrentThermalState()` _macOS_ + +Returns `string` - The system's current thermal state. Can be `unknown`, `nominal`, `fair`, `serious`, or `critical`. + ### `powerMonitor.isOnBatteryPower()` Returns `boolean` - Whether the system is on battery power. diff --git a/lib/browser/api/power-monitor.ts b/lib/browser/api/power-monitor.ts index 02fdde2ed561f..c8ba4996bb169 100644 --- a/lib/browser/api/power-monitor.ts +++ b/lib/browser/api/power-monitor.ts @@ -5,6 +5,7 @@ const { createPowerMonitor, getSystemIdleState, getSystemIdleTime, + getCurrentThermalState, isOnBatteryPower } = process._linkedBinding('electron_browser_power_monitor'); @@ -43,6 +44,10 @@ class PowerMonitor extends EventEmitter { return getSystemIdleState(idleThreshold); } + getCurrentThermalState () { + return getCurrentThermalState(); + } + getSystemIdleTime () { return getSystemIdleTime(); } diff --git a/shell/browser/api/electron_api_power_monitor.cc b/shell/browser/api/electron_api_power_monitor.cc index c7ba897ab9aae..3ca813644a393 100644 --- a/shell/browser/api/electron_api_power_monitor.cc +++ b/shell/browser/api/electron_api_power_monitor.cc @@ -6,8 +6,11 @@ #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_device_source.h" +#include "base/power_monitor/power_observer.h" +#include "gin/data_object_builder.h" #include "gin/handle.h" #include "shell/browser/browser.h" +#include "shell/browser/javascript_environment.h" #include "shell/common/gin_converters/callback_converter.h" #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" @@ -33,6 +36,26 @@ struct Converter { } }; +template <> +struct Converter { + static v8::Local ToV8( + v8::Isolate* isolate, + const base::PowerThermalObserver::DeviceThermalState& in) { + switch (in) { + case base::PowerThermalObserver::DeviceThermalState::kUnknown: + return StringToV8(isolate, "unknown"); + case base::PowerThermalObserver::DeviceThermalState::kNominal: + return StringToV8(isolate, "nominal"); + case base::PowerThermalObserver::DeviceThermalState::kFair: + return StringToV8(isolate, "fair"); + case base::PowerThermalObserver::DeviceThermalState::kSerious: + return StringToV8(isolate, "serious"); + case base::PowerThermalObserver::DeviceThermalState::kCritical: + return StringToV8(isolate, "critical"); + } + } +}; + } // namespace gin namespace electron::api { @@ -47,6 +70,7 @@ PowerMonitor::PowerMonitor(v8::Isolate* isolate) { base::PowerMonitor::AddPowerStateObserver(this); base::PowerMonitor::AddPowerSuspendObserver(this); + base::PowerMonitor::AddPowerThermalObserver(this); #if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) InitPlatformSpecificMonitors(); @@ -56,6 +80,7 @@ PowerMonitor::PowerMonitor(v8::Isolate* isolate) { PowerMonitor::~PowerMonitor() { base::PowerMonitor::RemovePowerStateObserver(this); base::PowerMonitor::RemovePowerSuspendObserver(this); + base::PowerMonitor::RemovePowerThermalObserver(this); } bool PowerMonitor::ShouldShutdown() { @@ -77,6 +102,22 @@ void PowerMonitor::OnResume() { Emit("resume"); } +void PowerMonitor::OnThermalStateChange(DeviceThermalState new_state) { + v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); + v8::HandleScope scope(isolate); + EmitWithoutEvent( + "thermal-state-change", + gin::DataObjectBuilder(isolate).Set("state", new_state).Build()); +} + +void PowerMonitor::OnSpeedLimitChange(int speed_limit) { + v8::Isolate* isolate = JavascriptEnvironment::GetIsolate(); + v8::HandleScope scope(isolate); + EmitWithoutEvent( + "speed-limit-change", + gin::DataObjectBuilder(isolate).Set("limit", speed_limit).Build()); +} + #if BUILDFLAG(IS_LINUX) void PowerMonitor::SetListeningForShutdown(bool is_listening) { if (is_listening) { @@ -138,6 +179,10 @@ bool IsOnBatteryPower() { return base::PowerMonitor::IsOnBatteryPower(); } +base::PowerThermalObserver::DeviceThermalState GetCurrentThermalState() { + return base::PowerMonitor::GetCurrentThermalState(); +} + void Initialize(v8::Local exports, v8::Local unused, v8::Local context, @@ -148,6 +193,8 @@ void Initialize(v8::Local exports, base::BindRepeating(&PowerMonitor::Create)); dict.SetMethod("getSystemIdleState", base::BindRepeating(&GetSystemIdleState)); + dict.SetMethod("getCurrentThermalState", + base::BindRepeating(&GetCurrentThermalState)); dict.SetMethod("getSystemIdleTime", base::BindRepeating(&GetSystemIdleTime)); dict.SetMethod("isOnBatteryPower", base::BindRepeating(&IsOnBatteryPower)); } diff --git a/shell/browser/api/electron_api_power_monitor.h b/shell/browser/api/electron_api_power_monitor.h index 7aab65e7f78e9..01f3aced32f48 100644 --- a/shell/browser/api/electron_api_power_monitor.h +++ b/shell/browser/api/electron_api_power_monitor.h @@ -21,7 +21,8 @@ class PowerMonitor : public gin::Wrappable, public gin_helper::EventEmitterMixin, public gin_helper::Pinnable, public base::PowerStateObserver, - public base::PowerSuspendObserver { + public base::PowerSuspendObserver, + public base::PowerThermalObserver { public: static v8::Local Create(v8::Isolate* isolate); @@ -57,6 +58,10 @@ class PowerMonitor : public gin::Wrappable, void OnSuspend() override; void OnResume() override; + // base::PowerThermalObserver + void OnThermalStateChange(DeviceThermalState new_state) override; + void OnSpeedLimitChange(int speed_limit) override; + #if BUILDFLAG(IS_WIN) // Static callback invoked when a message comes in to our messaging window. static LRESULT CALLBACK WndProcStatic(HWND hwnd, diff --git a/spec/api-power-monitor-spec.ts b/spec/api-power-monitor-spec.ts index 470096d339fd6..c45ef14b887da 100644 --- a/spec/api-power-monitor-spec.ts +++ b/spec/api-power-monitor-spec.ts @@ -173,6 +173,12 @@ describe('powerMonitor', () => { }); }); + describe('powerMonitor.getCurrentThermalState', () => { + it('returns a valid state', () => { + expect(powerMonitor.getCurrentThermalState()).to.be.oneOf(['unknown', 'nominal', 'fair', 'serious', 'critical']); + }); + }); + describe('powerMonitor.onBatteryPower', () => { it('returns a boolean', () => { expect(powerMonitor.onBatteryPower).to.be.a('boolean'); From a66edd043b2cd0cd0c47059eddf03db4f8e212f6 Mon Sep 17 00:00:00 2001 From: Jeremy Rose Date: Tue, 11 Apr 2023 12:27:08 -0700 Subject: [PATCH 2/2] update docs --- docs/api/power-monitor.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index f87c2fb9a8122..6f53114b12dd6 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -26,11 +26,27 @@ Emitted when system changes to battery power. ### Event: 'thermal-state-change' _macOS_ -* `state` string - one of `unknown`, `nominal`, `fair`, `serious`, `critical`. +* `state` string - The system's new thermal state. Can be `unknown`, `nominal`, `fair`, `serious`, `critical`. -### Event: 'speed-limit-change' _macOS_ +Emitted when the thermal state of the system changes. Notification of a change +in the thermal status of the system, such as entering a critical temperature +range. Depending on the severity, the system might take steps to reduce said +temperature, for example, throttling the CPU or switching on the fans if +available. -* `limit` number +Apps may react to the new state by reducing expensive computing tasks (e.g. +video encoding), or notifying the user. The same state might be received +repeatedly. + +See https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/RespondToThermalStateChanges.html + +### Event: 'speed-limit-change' _macOS_ _Windows_ + +* `limit` number - The operating system's advertised speed limit for CPUs, in percent. + +Notification of a change in the operating system's advertised speed limit for +CPUs, in percent. Values below 100 indicate that the system is impairing +processing power due to thermal management. ### Event: 'shutdown' _Linux_ _macOS_