summaryrefslogtreecommitdiff
path: root/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js
diff options
context:
space:
mode:
authornippy <you@example.com>2026-04-18 13:49:56 +0200
committernippy <you@example.com>2026-04-18 13:49:56 +0200
commit5d94c0a7d44a2255b81815a52a7056a94a39842d (patch)
tree759bdea9645c6a62f9e1e4c001f7d81cccd120d2 /raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js
parente79cdf210b267f21a186255ce1a4d50029439d54 (diff)
downloadRaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.tar.gz
RaveOS-PKGBUILD-5d94c0a7d44a2255b81815a52a7056a94a39842d.zip
update Raveos themes
Diffstat (limited to 'raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js')
-rw-r--r--raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js235
1 files changed, 235 insertions, 0 deletions
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
new file mode 100644
index 0000000..28d184a
--- /dev/null
+++ b/raveos-theme/gnome/theme-data/extensions/installed/ShutdownTimer@deminder/dbus-service/control.js
@@ -0,0 +1,235 @@
+// SPDX-FileCopyrightText: 2023 Deminder <tremminder@gmail.com>
+// 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<void>} - 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');
+ }
+}