diff --git a/atom/browser/api/atom_api_power_monitor.cc b/atom/browser/api/atom_api_power_monitor.cc index fce749790b52e..adac90c430032 100644 --- a/atom/browser/api/atom_api_power_monitor.cc +++ b/atom/browser/api/atom_api_power_monitor.cc @@ -5,12 +5,33 @@ #include "atom/browser/api/atom_api_power_monitor.h" #include "atom/browser/browser.h" +#include "atom/common/native_mate_converters/callback.h" #include "base/power_monitor/power_monitor.h" #include "base/power_monitor/power_monitor_device_source.h" #include "native_mate/dictionary.h" #include "atom/common/node_includes.h" +namespace mate { +template<> +struct Converter { + static v8::Local ToV8(v8::Isolate* isolate, + const ui::IdleState& in) { + switch (in) { + case ui::IDLE_STATE_ACTIVE: + return mate::StringToV8(isolate, "active"); + case ui::IDLE_STATE_IDLE: + return mate::StringToV8(isolate, "idle"); + case ui::IDLE_STATE_LOCKED: + return mate::StringToV8(isolate, "locked"); + case ui::IDLE_STATE_UNKNOWN: + default: + return mate::StringToV8(isolate, "unknown"); + } + } +}; +} // namespace mate + namespace atom { namespace api { @@ -60,6 +81,22 @@ void PowerMonitor::OnResume() { Emit("resume"); } +void PowerMonitor::QuerySystemIdleState(v8::Isolate* isolate, + int idle_threshold, + const ui::IdleCallback& callback) { + if (idle_threshold > 0) { + ui::CalculateIdleState(idle_threshold, callback); + } else { + isolate->ThrowException(v8::Exception::TypeError( + mate::StringToV8(isolate, + "Invalid idle threshold, must be greater than 0"))); + } +} + +void PowerMonitor::QuerySystemIdleTime(const ui::IdleTimeCallback& callback) { + ui::CalculateIdleTime(callback); +} + // static v8::Local PowerMonitor::Create(v8::Isolate* isolate) { if (!Browser::Get()->is_ready()) { @@ -76,11 +113,15 @@ v8::Local PowerMonitor::Create(v8::Isolate* isolate) { void PowerMonitor::BuildPrototype( v8::Isolate* isolate, v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "PowerMonitor")); -#if defined(OS_LINUX) + mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) - .SetMethod("blockShutdown", &PowerMonitor::BlockShutdown) - .SetMethod("unblockShutdown", &PowerMonitor::UnblockShutdown); + .MakeDestroyable() +#if defined(OS_LINUX) + .SetMethod("blockShutdown", &PowerMonitor::BlockShutdown) + .SetMethod("unblockShutdown", &PowerMonitor::UnblockShutdown) #endif + .SetMethod("querySystemIdleState", &PowerMonitor::QuerySystemIdleState) + .SetMethod("querySystemIdleTime", &PowerMonitor::QuerySystemIdleTime); } } // namespace api diff --git a/atom/browser/api/atom_api_power_monitor.h b/atom/browser/api/atom_api_power_monitor.h index 90971556de833..b4f43d24470bc 100644 --- a/atom/browser/api/atom_api_power_monitor.h +++ b/atom/browser/api/atom_api_power_monitor.h @@ -9,6 +9,7 @@ #include "atom/browser/lib/power_observer.h" #include "base/compiler_specific.h" #include "native_mate/handle.h" +#include "ui/base/idle/idle.h" namespace atom { @@ -41,6 +42,11 @@ class PowerMonitor : public mate::TrackableObject, void OnResume() override; private: + void QuerySystemIdleState(v8::Isolate* isolate, + int idle_threshold, + const ui::IdleCallback& callback); + void QuerySystemIdleTime(const ui::IdleTimeCallback& callback); + DISALLOW_COPY_AND_ASSIGN(PowerMonitor); }; diff --git a/atom/browser/atom_browser_main_parts.cc b/atom/browser/atom_browser_main_parts.cc index 718d7d4f82bab..e8659bae15803 100644 --- a/atom/browser/atom_browser_main_parts.cc +++ b/atom/browser/atom_browser_main_parts.cc @@ -24,6 +24,7 @@ #include "content/public/browser/child_process_security_policy.h" #include "device/geolocation/geolocation_delegate.h" #include "device/geolocation/geolocation_provider.h" +#include "ui/base/idle/idle.h" #include "ui/base/l10n/l10n_util.h" #include "v8/include/v8-debug.h" @@ -159,6 +160,11 @@ int AtomBrowserMainParts::PreCreateThreads() { fake_browser_process_->SetApplicationLocale( brightray::BrowserClient::Get()->GetApplicationLocale()); } + + #if defined(OS_MACOSX) + ui::InitIdleMonitor(); + #endif + return result; } diff --git a/docs/api/power-monitor.md b/docs/api/power-monitor.md index 5d600da48a946..16927da6623dc 100644 --- a/docs/api/power-monitor.md +++ b/docs/api/power-monitor.md @@ -46,3 +46,25 @@ Emitted when the system is about to reboot or shut down. If the event handler invokes `e.preventDefault()`, Electron will attempt to delay system shutdown in order for the app to exit cleanly. If `e.preventDefault()` is called, the app should exit as soon as possible by calling something like `app.quit()`. + +## Methods + +The `powerMonitor` module has the following methods: + +#### `powerMonitor.querySystemIdleState(idleThreshold, callback)` + +* `idleThreshold` Integer +* `callback` Function + * `idleState` String - Can be `active`, `idle`, `locked` or `unknown` + +Calculate the system idle state. `idleThreshold` is the amount of time (in seconds) +before considered idle. `callback` will be called synchronously on some systems +and with an `idleState` argument that describes the system's state. `locked` is +available on supported systems only. + +#### `powerMonitor.querySystemIdleTime(callback)` + +* `callback` Function + * `idleTime` Integer - Idle time in seconds + +Calculate system idle time in seconds. diff --git a/spec/api-power-monitor-spec.js b/spec/api-power-monitor-spec.js index 3b8d9ab5e4a94..8f7c93eeb5188 100644 --- a/spec/api-power-monitor-spec.js +++ b/spec/api-power-monitor-spec.js @@ -10,26 +10,28 @@ const assert = require('assert') const dbus = require('dbus-native') const Promise = require('bluebird') -const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRESS; - -(skip ? describe.skip : describe)('powerMonitor', () => { - let logindMock, powerMonitor, getCalls, emitSignal, reset - - before(async () => { - const systemBus = dbus.systemBus() - const loginService = systemBus.getService('org.freedesktop.login1') - const getInterface = Promise.promisify(loginService.getInterface, {context: loginService}) - logindMock = await getInterface('/org/freedesktop/login1', 'org.freedesktop.DBus.Mock') - getCalls = Promise.promisify(logindMock.GetCalls, {context: logindMock}) - emitSignal = Promise.promisify(logindMock.EmitSignal, {context: logindMock}) - reset = Promise.promisify(logindMock.Reset, {context: logindMock}) - }) +const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRESS - after(async () => { - await reset() - }) +describe('powerMonitor', () => { + let logindMock, dbusMockPowerMonitor, getCalls, emitSignal, reset - describe('when powerMonitor module is loaded', () => { + if (!skip) { + before(async () => { + const systemBus = dbus.systemBus() + const loginService = systemBus.getService('org.freedesktop.login1') + const getInterface = Promise.promisify(loginService.getInterface, {context: loginService}) + logindMock = await getInterface('/org/freedesktop/login1', 'org.freedesktop.DBus.Mock') + getCalls = Promise.promisify(logindMock.GetCalls, {context: logindMock}) + emitSignal = Promise.promisify(logindMock.EmitSignal, {context: logindMock}) + reset = Promise.promisify(logindMock.Reset, {context: logindMock}) + }) + + after(async () => { + await reset() + }) + } + + (skip ? describe.skip : describe)('when powerMonitor module is loaded with dbus mock', () => { function onceMethodCalled (done) { function cb () { logindMock.removeListener('MethodCalled', cb) @@ -41,7 +43,7 @@ const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRES before((done) => { logindMock.on('MethodCalled', onceMethodCalled(done)) // lazy load powerMonitor after we listen to MethodCalled mock signal - powerMonitor = require('electron').remote.powerMonitor + dbusMockPowerMonitor = require('electron').remote.powerMonitor }) it('should call Inhibit to delay suspend', async () => { @@ -59,14 +61,14 @@ const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRES describe('when PrepareForSleep(true) signal is sent by logind', () => { it('should emit "suspend" event', (done) => { - powerMonitor.once('suspend', () => done()) + dbusMockPowerMonitor.once('suspend', () => done()) emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep', 'b', [['b', true]]) }) describe('when PrepareForSleep(false) signal is sent by logind', () => { it('should emit "resume" event', (done) => { - powerMonitor.once('resume', () => done()) + dbusMockPowerMonitor.once('resume', () => done()) emitSignal('org.freedesktop.login1.Manager', 'PrepareForSleep', 'b', [['b', false]]) }) @@ -90,7 +92,7 @@ const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRES before(async () => { const calls = await getCalls() assert.equal(calls.length, 2) - powerMonitor.once('shutdown', () => { }) + dbusMockPowerMonitor.once('shutdown', () => { }) }) it('should call Inhibit to delay shutdown', async () => { @@ -108,11 +110,53 @@ const skip = process.platform !== 'linux' || !process.env.DBUS_SYSTEM_BUS_ADDRES describe('when PrepareForShutdown(true) signal is sent by logind', () => { it('should emit "shutdown" event', (done) => { - powerMonitor.once('shutdown', () => { done() }) + dbusMockPowerMonitor.once('shutdown', () => { done() }) emitSignal('org.freedesktop.login1.Manager', 'PrepareForShutdown', 'b', [['b', true]]) }) }) }) }) + + describe('when powerMonitor module is loaded', () => { + let powerMonitor + before(() => { + powerMonitor = require('electron').remote.powerMonitor + }) + + describe('powerMonitor.querySystemIdleState', () => { + it('notify current system idle state', (done) => { + powerMonitor.querySystemIdleState(1, (idleState) => { + assert.ok(idleState) + done() + }) + }) + + it('does not accept non positive integer threshold', () => { + assert.throws(() => { + powerMonitor.querySystemIdleState(-1, (idleState) => { + }) + }) + + assert.throws(() => { + powerMonitor.querySystemIdleState(NaN, (idleState) => { + }) + }) + + assert.throws(() => { + powerMonitor.querySystemIdleState('a', (idleState) => { + }) + }) + }) + }) + + describe('powerMonitor.querySystemIdleTime', () => { + it('notify current system idle time', (done) => { + powerMonitor.querySystemIdleTime((idleTime) => { + assert.ok(idleTime >= 0) + done() + }) + }) + }) + }) })