From 70ca3fef77ee8bdc6e3ac28589a6fa08c024cc69 Mon Sep 17 00:00:00 2001 From: AlexanderCurl Date: Sat, 18 Apr 2026 17:46:06 +0100 Subject: Replaced file structures for themes in the correct format --- .../ShutdownTimer@deminder/dbus-service/action.js | 217 ---------------- .../ShutdownTimer@deminder/dbus-service/control.js | 235 ----------------- .../dbus-service/shutdown-timer-dbus.js | 72 ------ .../ShutdownTimer@deminder/dbus-service/timer.js | 286 --------------------- 4 files changed, 810 deletions(-) delete mode 100644 raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js delete mode 100644 raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js delete mode 100644 raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js delete mode 100644 raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js (limited to 'raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service') diff --git a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js b/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js deleted file mode 100644 index 4b5bf9f..0000000 --- a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/action.js +++ /dev/null @@ -1,217 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Deminder -// SPDX-License-Identifier: GPL-3.0-or-later - -import Gio from 'gi://Gio'; -import * as Control from './control.js'; -import { proxyPromise } from '../modules/util.js'; -import { pgettext as C_, gettext as _ } from '../modules/translation.js'; -import { logDebug } from '../modules/util.js'; - -export const ACTIONS = { - PowerOff: 0, - Reboot: 1, - Suspend: 2, - SuspendThenHibernate: 3, - Hibernate: 4, - HybridSleep: 5, - Halt: 6, -}; - -export const WAKE_ACTIONS = { wake: 100, 'no-wake': 101 }; - -/** - * Get supported actions. - * In order to show an error when shutdown or reboot are not supported - * they are always included here. */ -export async function* supportedActions() { - const actionDbus = new Action(); - for await (const action of Object.keys(ACTIONS).map(async a => - ['PowerOff', 'Reboot'].includes(a) || - (await actionDbus.canShutdownAction(a)) - ? a - : null - )) { - if (action) { - yield action; - } - } -} - -export class UnsupportedActionError extends Error {} - -export class Action { - #cancellable = new Gio.Cancellable(); - #cookie = null; - - #loginProxy = proxyPromise( - 'org.freedesktop.login1.Manager', - Gio.DBus.system, - 'org.freedesktop.login1', - '/org/freedesktop/login1', - this.#cancellable - ); - - #screenSaverProxy = proxyPromise( - 'org.gnome.ScreenSaver', - Gio.DBus.session, - 'org.gnome.ScreenSaver', - '/org/gnome/ScreenSaver', - this.#cancellable - ); - - #sessionProxy = proxyPromise( - 'org.gnome.SessionManager', - Gio.DBus.session, - 'org.gnome.SessionManager', - '/org/gnome/SessionManager', - this.#cancellable - ); - - destroy() { - if (this.#cancellable !== null) { - this.#cancellable.cancel(); - this.#cancellable = null; - } - } - - #poweroffOrReboot(action) { - return [ACTIONS.PowerOff, ACTIONS.Reboot].includes(ACTIONS[action]); - } - - /** - * Perform the shutdown action. - * - * @param {string} action the shutdown action - * @param {boolean} showEndSessionDialog show the end session dialog or directly shutdown - * - * @returns {Promise} resolves on action completion - */ - async shutdownAction(action, showEndSessionDialog) { - if (!(action in ACTIONS)) - throw new Error(`Unknown shutdown action: ${action}`); - logDebug('[shutdownAction]', action); - - await this.uninhibitSuspend(); - - const screenSaverProxy = await this.#screenSaverProxy; - const [screenSaverActive] = await screenSaverProxy.GetActiveAsync(); - if ( - showEndSessionDialog && - !screenSaverActive && - this.#poweroffOrReboot(action) - ) { - const sessionProxy = await this.#sessionProxy; - if (action === 'PowerOff') { - await sessionProxy.ShutdownAsync(); - } else { - await sessionProxy.RebootAsync(); - } - } else { - const loginProxy = await this.#loginProxy; - if (await this.canShutdownAction(action)) { - await loginProxy[`${action}Async`](true); - } else if (this.#poweroffOrReboot(action)) { - await Control.shutdown('now', ACTIONS[action] === ACTIONS.Reboot); - } else { - throw new UnsupportedActionError(); - } - } - } - - /** - * Check if a shutdown action can be performed (without authentication). - * - * @returns {Promise} resolves to `true` if action can be performed, otherwise `false`. - */ - async canShutdownAction(action) { - const loginProxy = await this.#loginProxy; - if (!(action in ACTIONS)) - throw new Error(`Unknown shutdown action: ${action}`); - const [result] = await loginProxy[`Can${action}Async`](); - return result === 'yes'; - } - - /** - * Schedule a wake after some minutes or cancel - * - * @param {boolean} wake - * @param {number} minutes - */ - async wakeAction(wake, minutes) { - if (wake) { - await Control.wake(minutes); - } else { - await Control.wakeCancel(); - } - } - - async inhibitSuspend() { - if (this.#cookie === null) { - const sessionProxy = await this.#sessionProxy; - const [cookie] = await sessionProxy.InhibitAsync( - 'user', - 0, - 'Inhibit by Shutdown Timer (GNOME-Shell extension)', - /* Suspend flag */ 4 - ); - this.#cookie = cookie; - } - } - - async uninhibitSuspend() { - if (this.#cookie !== null) { - const sessionProxy = await this.#sessionProxy; - await sessionProxy.UninhibitAsync(this.#cookie); - this.#cookie = null; - } - } -} - -/** - * Get the translated action label - * - * @param action - */ -export function actionLabel(action) { - return { - SuspendThenHibernate: _('Suspend then Hibernate'), - HybridSleep: _('Hybrid Sleep'), - Hibernate: _('Hibernate'), - Halt: _('Halt'), - Suspend: _('Suspend'), - PowerOff: _('Power Off'), - Reboot: _('Restart'), - wake: _('Wake'), - 'no-wake': _('No Wake'), - }[action]; -} - -export function untilText(action) { - return { - SuspendThenHibernate: C_('untiltext', 'suspend and hibernate'), - HybridSleep: C_('untiltext', 'hybrid sleep'), - Hibernate: C_('untiltext', 'hibernate'), - Halt: C_('untiltext', 'halt'), - Suspend: C_('untiltext', 'suspend'), - PowerOff: C_('untiltext', 'shutdown'), - Reboot: C_('untiltext', 'reboot'), - wake: C_('untiltext', 'wakeup'), - }[action]; -} - -export function mapLegacyAction(action) { - return action in ACTIONS || ['wake', ''].includes(action) - ? action - : { - poweroff: 'PowerOff', - shutdown: 'PowerOff', - reboot: 'Reboot', - suspend: 'Suspend', - }[action] ?? - { - p: 'PowerOff', - r: 'Reboot', - s: 'Suspend', - h: 'SuspendThenHibernate', - }[action[0].toLowerCase()]; -} diff --git a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js b/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js deleted file mode 100644 index 28d184a..0000000 --- a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js +++ /dev/null @@ -1,235 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Deminder -// SPDX-License-Identifier: GPL-3.0-or-later - -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; -import { gettext as _ } from '../modules/translation.js'; - -import { logDebug } from '../modules/util.js'; - -function readLine(stream, cancellable) { - return new Promise((resolve, reject) => { - stream.read_line_async(0, cancellable, (s, res) => { - try { - const line = s.read_line_finish_utf8(res)[0]; - - if (line !== null) { - resolve(line); - } else { - reject(new Error('No line was read!')); - } - } catch (e) { - reject(e); - } - }); - }); -} - -function quoteEscape(str) { - return str.replaceAll('\\', '\\\\').replaceAll('"', '\\"'); -} - -/** - * Execute a command asynchronously and check the exit status. - * - * If given, @cancellable can be used to stop the process before it finishes. - * - * @param {string[] | string} argv - a list of string arguments or command line that will be parsed - * @param {Gio.Cancellable} [cancellable] - optional cancellable object - * @param {boolean} shell - run command as shell command - * @param logFunc - * @returns {Promise} - The process success - */ -export function execCheck( - argv, - cancellable = null, - shell = true, - logFunc = undefined -) { - if (!shell && typeof argv === 'string') { - argv = GLib.shell_parse_argv(argv)[1]; - } - - const isRootProc = argv[0] && argv[0].endsWith('pkexec'); - - if (shell && Array.isArray(argv)) { - argv = argv.map(c => `"${quoteEscape(c)}"`).join(' '); - } - let cancelId = 0; - let proc = new Gio.Subprocess({ - argv: (shell ? ['/bin/sh', '-c'] : []).concat(argv), - flags: - logFunc !== undefined - ? Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE - : Gio.SubprocessFlags.NONE, - }); - proc.init(cancellable); - - if (cancellable instanceof Gio.Cancellable) { - cancelId = cancellable.connect(() => { - if (logFunc !== undefined) { - if (isRootProc) { - logFunc(`# ${_('Can not cancel root process!')}`); - } else { - logFunc(`[${_('CANCEL')}]`); - } - } - proc.force_exit(); - }); - } - let stdoutStream = null; - let stderrStream = null; - let readLineCancellable = null; - - if (logFunc !== undefined) { - readLineCancellable = new Gio.Cancellable(); - stdoutStream = new Gio.DataInputStream({ - base_stream: proc.get_stdout_pipe(), - close_base_stream: true, - }); - - stderrStream = new Gio.DataInputStream({ - base_stream: proc.get_stderr_pipe(), - close_base_stream: true, - }); - const readNextLine = async (stream, prefix) => { - try { - const line = await readLine(stream, readLineCancellable); - logFunc(prefix + line); - logDebug(line); - await readNextLine(stream, prefix); - } catch { - if (!stream.is_closed()) { - stream.close_async(0, null, (s, sRes) => { - try { - s.close_finish(sRes); - } catch (e) { - logDebug(`[StreamCloseError] ${e}`); - } - }); - } - } - }; - // read stdout and stderr asynchronously - readNextLine(stdoutStream, ''); - readNextLine(stderrStream, '# '); - } - - return new Promise((resolve, reject) => { - proc.wait_check_async(null, (p, res) => { - try { - const success = p.wait_check_finish(res); - if (!success) { - let status = p.get_exit_status(); - - throw new Gio.IOErrorEnum({ - code: Gio.io_error_from_errno(status), - message: GLib.strerror(status), - }); - } - - resolve(); - } catch (e) { - reject(e); - } finally { - if (readLineCancellable) readLineCancellable.cancel(); - readLineCancellable = null; - if (cancelId > 0) cancellable.disconnect(cancelId); - } - }); - }); -} - -export function sleepUntilDeadline(deadlineSeconds, cancellable) { - return new Promise((resolve, reject) => { - const secondsLeft = () => - deadlineSeconds - GLib.DateTime.new_now_utc().to_unix(); - - let timeoutId = 0; - let handlerId = cancellable.connect(() => { - handlerId = 0; - if (timeoutId) { - clearTimeout(timeoutId); - } - reject(new Error('Sleep until deadline cancelled!')); - }); - - const continueSleep = () => { - timeoutId = setTimeout(() => { - timeoutId = 0; - if (secondsLeft() > 0) { - continueSleep(); - } else { - if (handlerId) { - cancellable.disconnect(handlerId); - } - resolve(); - } - }, 1000); - }; - continueSleep(); - }); -} - -export function installedScriptPath() { - for (const name of [ - 'shutdowntimerctl', - `shutdowntimerctl-${GLib.get_user_name()}`, - ]) { - const standard = GLib.find_program_in_path(name); - if (standard !== null) { - return standard; - } - for (const bindir of ['/usr/local/bin/', '/usr/bin/']) { - const path = bindir + name; - logDebug(`Looking for: ${path}`); - if (Gio.File.new_for_path(path).query_exists(null)) { - return path; - } - } - } - return null; -} - -function execControlScript(args, noScriptArgs) { - const installedScript = installedScriptPath(); - if (installedScript !== null) { - return execCheck(['pkexec', installedScript].concat(args), null, false); - } - if (noScriptArgs === undefined) { - throw new Error(_('Privileged script installation required!')); - } - return execCheck(noScriptArgs, null, false); -} - -export function shutdown(minutes, reboot = false) { - logDebug(`[root-shutdown] ${minutes} minutes, reboot: ${reboot}`); - return execControlScript( - [reboot ? 'reboot' : 'shutdown', `${minutes}`], - ['shutdown', reboot ? '-r' : '-P', `${minutes}`] - ); -} - -function shutdownCancel() { - logDebug('[root-shutdown] cancel'); - return execControlScript(['shutdown-cancel'], ['shutdown', '-c']); -} - -export function wake(minutes) { - return execControlScript(['wake', `${minutes}`]); -} - -export function wakeCancel() { - return execControlScript(['wake-cancel']); -} - -export async function stopRootModeProtection(info) { - if (['PowerOff', 'Reboot'].includes(info.mode)) { - await shutdownCancel(); - } -} -export async function startRootModeProtection(info) { - if (['PowerOff', 'Reboot'].includes(info.mode)) { - await shutdown(Math.max(0, info.minutes) + 1, info.mode === 'Reboot'); - } -} diff --git a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js b/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js deleted file mode 100644 index 8db37a4..0000000 --- a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/shutdown-timer-dbus.js +++ /dev/null @@ -1,72 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Deminder -// SPDX-License-Identifier: GPL-3.0-or-later - -import Gio from 'gi://Gio'; -import GLib from 'gi://GLib'; - -import { loadInterfaceXML, logDebug } from '../modules/util.js'; -import { Timer } from './timer.js'; - -export const ShutdownTimerName = 'org.gnome.Shell.Extensions.ShutdownTimer'; -export const ShutdownTimerObjectPath = - '/org/gnome/Shell/Extensions/ShutdownTimer'; -export const ShutdownTimerIface = await loadInterfaceXML(ShutdownTimerName); - -export class ShutdownTimerDBus { - constructor({ settings }) { - this._dbusImpl = Gio.DBusExportedObject.wrapJSObject( - ShutdownTimerIface, - this - ); - this._dbusImpl.export(Gio.DBus.session, ShutdownTimerObjectPath); - - this._timer = new Timer({ settings }); - this._timer.connect('message', (_, msg) => { - logDebug('[shutdown-timer-dbus] msg', msg); - this._dbusImpl.emit_signal('OnMessage', new GLib.Variant('(s)', [msg])); - }); - this._timer.connect('change', () => { - logDebug('[shutdown-timer-dbus] state', this._timer.state); - this._dbusImpl.emit_signal( - 'OnStateChange', - new GLib.Variant('(s)', [this._timer.state]) - ); - }); - this._timer.connect('change-external', () => { - this._dbusImpl.emit_signal('OnExternalChange', null); - }); - } - - async ScheduleShutdownAsync(parameters, invocation) { - const [shutdown, action] = parameters; - logDebug( - '[sdt-dbus] [ScheduleShutdownAsync] shutdown', - shutdown, - 'action', - action - ); - await this._timer.toggleShutdown(shutdown, action); - invocation.return_value(null); - } - - async ScheduleWakeAsync(parameters, invocation) { - const [wake] = parameters; - logDebug('[sdt-dbus] [ScheduleWakeAsync] wake', wake); - await this._timer.toggleWake(wake); - invocation.return_value(null); - } - - GetStateAsync(_, invocation) { - logDebug('[sdt-dbus] [GetStateAsync]'); - invocation.return_value(new GLib.Variant('(s)', [this._timer.state])); - return Promise.resolve(); - } - - destroy() { - logDebug('[sdt-dbus] destroy'); - this._dbusImpl.unexport(); - this._dbusImpl = null; - this._timer.destroy(); - this._timer = null; - } -} diff --git a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js b/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js deleted file mode 100644 index 6c439ab..0000000 --- a/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/timer.js +++ /dev/null @@ -1,286 +0,0 @@ -// SPDX-FileCopyrightText: 2023 Deminder -// SPDX-License-Identifier: GPL-3.0-or-later - -import GLib from 'gi://GLib'; -import Gio from 'gi://Gio'; -import { - gettext as _, - ngettext as _n, - pgettext as C_, -} from '../modules/translation.js'; - -import { logDebug, throttleTimeout } from '../modules/util.js'; -import * as Control from './control.js'; -import { - longDurationString, - ScheduleInfo, - getShutdownScheduleFromSettings, - getSliderMinutesFromSettings, -} from '../modules/schedule-info.js'; -import { - actionLabel, - Action, - mapLegacyAction, - UnsupportedActionError, -} from './action.js'; - -const Signals = imports.signals; - -export class Timer { - _timerCancellable = null; - _updateScheduleCancel = null; - _action = new Action(); - info = { - externalShutdown: new ScheduleInfo({ - external: true, - }), - externalWake: new ScheduleInfo({ - external: false, - mode: 'wake', - }), - internalShutdown: new ScheduleInfo({ mode: 'PowerOff' }), - }; - - constructor({ settings }) { - this._settings = settings; - - const [updateScheduleThrottled, updateScheduleCancel] = throttleTimeout( - () => this.updateSchedule(), - 20 - ); - this._updateScheduleCancel = updateScheduleCancel; - this._settingsIds = [ - 'root-mode-value', - 'shutdown-timestamp-value', - 'shutdown-mode-value', - ].map(settingName => - settings.connect(`changed::${settingName}`, updateScheduleThrottled) - ); - - this.updateSchedule(); - } - - destroy() { - if (this._settings === null) { - throw new Error('should not destroy twice'); - } - // Disconnect settings - this._settingsIds.forEach(id => this._settings.disconnect(id)); - this._settingsIds = []; - this._settings = null; - - // Cancel schedule updates - if (this._updateScheduleCancel !== null) { - this._updateScheduleCancel(); - this._updateScheduleCancel = null; - } - - // Cancel internal timer - if (this._timerCancellable !== null) { - this._timerCancellable.cancel(); - this._timerCancellable = null; - } - - // External schedules (for 'shutdown' and 'wake') are not stopped - } - - updateSchedule() { - const oldInternal = this.info.internalShutdown; - const internal = getShutdownScheduleFromSettings(this._settings); - this.info.internalShutdown = internal; - logDebug( - `[updateSchedule] internal schedule: ${internal.label} (old internal schedule: ${oldInternal.label})` - ); - this._updateRootModeProtection(oldInternal); - if ( - internal.mode !== oldInternal.mode || - internal.deadline !== oldInternal.deadline - ) { - this.emit('change'); - if (internal.scheduled) { - if (internal.minutes > 0) { - // Show schedule info - this.emit( - 'message', - C_('StartSchedulePopup', '%s in %s').format( - actionLabel(internal.mode), - longDurationString( - internal.minutes, - h => _n('%s hour', '%s hours', h), - m => _n('%s minute', '%s minutes', m) - ) - ) - ); - } else { - logDebug(`[updateSchedule] hidden message for '< 1 minute' schedule`); - } - if (this._timerCancellable !== null) { - this._timerCancellable.cancel(); - this._timerCancellable = null; - } - this.executeActionDelayed() - .then(() => { - logDebug('[executeActionDelayed] done'); - }) - .catch(err => { - console.error('executeActionDelayed', err); - }); - } else { - if (this._timerCancellable !== null) { - this._timerCancellable.cancel(); - this._timerCancellable = null; - } - if (oldInternal.scheduled) { - this.emit('message', _('Shutdown Timer stopped')); - } - } - } - } - - async executeAction() { - const internal = this.info.internalShutdown; - if (!internal.scheduled) { - logDebug(`Refusing to exectute non scheduled action! '${internal.mode}'`); - return; - } - logDebug(`Running '${internal.mode}' timer action...`); - try { - this.emit('change'); - // Refresh root mode protection - await Promise.all([ - this._updateRootModeProtection(), - // Do shutdown - this._action.shutdownAction( - internal.mode, - this._settings.get_boolean('show-end-session-dialog-value') - ), - ]); - } catch (err) { - if (/* destroyed */ this._settings === null) { - throw err; - } - const newInternal = this.info.internalShutdown; - if (newInternal.scheduled && newInternal.secondsLeft > 0) { - logDebug('[timer] Replaced by new schedule.'); - return; - } - if (err instanceof UnsupportedActionError) { - this.emit( - 'message', - _('%s is not supported!').format(actionLabel(internal.mode)) - ); - } - } - await this.toggleShutdown(false, ''); - } - - async executeActionDelayed() { - const internal = this.info.internalShutdown; - const secs = internal.secondsLeft; - if (secs > 0) { - logDebug(`Started delayed action: ${internal.minutes}min remaining`); - try { - this._timerCancellable = new Gio.Cancellable(); - await Control.sleepUntilDeadline( - internal.deadline, - this._timerCancellable - ); - this._timerCancellable = null; - } catch { - logDebug(`Canceled delayed action: ${internal.minutes}min remaining`); - return; - } - } - await this.executeAction(); - } - - async toggleWake(wake) { - try { - logDebug('[toggleWake] wake', wake); - await this._action.wakeAction( - wake, - getSliderMinutesFromSettings(this._settings, 'wake') - ); - this.emit('change-external'); - } catch (err) { - this.emit( - 'message', - C_('Error', '%s\n%s').format(_('Wake action failed!'), err) - ); - this._settings.set_int('shutdown-timestamp-value', -1); - } - } - - async toggleShutdown(shutdown, legacyAction) { - // Update shutdown action - const action = mapLegacyAction(legacyAction); - if (action === undefined) { - this.emit( - 'message', - _('Unknown shutdown action: "%s"!').format(legacyAction) - ); - return; - } - logDebug('[toggleShutdown] shutdown', shutdown, 'action', action); - if (action !== '') { - this._settings.set_string('shutdown-mode-value', action); - } - - // Update shutdown timestamp - this._settings.set_int( - 'shutdown-timestamp-value', - shutdown - ? GLib.DateTime.new_now_utc().to_unix() + - Math.max( - 1, - getSliderMinutesFromSettings(this._settings, 'shutdown') * 60 - ) - : -1 - ); - if (shutdown) { - await this._action.inhibitSuspend(); - } else { - await this._action.uninhibitSuspend(); - } - if (this._settings.get_boolean('auto-wake-value')) { - await this.toggleWake(shutdown); - } - } - - get state() { - return this.info.internalShutdown.scheduled - ? this.info.internalShutdown.secondsLeft > 0 - ? 'active' - : 'action' - : 'inactive'; - } - - /** - * Ensure that shutdown/reboot is executed even if the Timer fails by running - * the `shutdown` command delayed by 1 minute. - */ - async _updateRootModeProtection(oldInternal) { - if (this._settings.get_boolean('root-mode-value')) { - const internal = this.info.internalShutdown; - logDebug('[updateRootModeProtection] mode ', internal.mode); - try { - if (oldInternal?.scheduled && oldInternal.mode !== internal.mode) { - await Control.stopRootModeProtection(oldInternal); - } - if (internal.scheduled) { - await Control.startRootModeProtection(internal); - } else { - await Control.stopRootModeProtection(internal); - } - } catch (err) { - this.emit( - 'message', - C_('Error', '%s\n%s').format(_('Root mode protection failed!'), err) - ); - console.error('[updateRootModeProtection]', err); - } - this.emit('change-external'); - } - } -} -Signals.addSignalMethods(Timer.prototype); -- cgit v1.3