diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index 1292ea1f05fc..6f53114b12dd 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -24,6 +24,30 @@ Emitted when the system changes to AC power. Emitted when system changes to battery power. +### Event: 'thermal-state-change' _macOS_ + +* `state` string - The system's new thermal state. Can be `unknown`, `nominal`, `fair`, `serious`, `critical`. + +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. + +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_ Emitted when the system is about to reboot or shut down. If the event handler @@ -55,7 +79,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 +90,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 017de65634ab..d664c8ba1a7b 100644 --- a/lib/browser/api/power-monitor.ts +++ b/lib/browser/api/power-monitor.ts @@ -4,6 +4,7 @@ const { createPowerMonitor, getSystemIdleState, getSystemIdleTime, + getCurrentThermalState, isOnBatteryPower } = process._linkedBinding('electron_browser_power_monitor'); @@ -40,6 +41,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 5878d2c7f193..93e63c33a6ed 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) { @@ -137,6 +178,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, @@ -147,6 +192,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 7aab65e7f78e..01f3aced32f4 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 edc9d7d466a8..9fda9528465e 100644 --- a/spec/api-power-monitor-spec.ts +++ b/spec/api-power-monitor-spec.ts @@ -178,6 +178,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');