Skip to content

auto-dark-light@gihaume: Correct README.md + improve old Cinnamon versions unsupport + fixes #6549 #7257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions auto-dark-light@gihaume/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 1.2.6 - 18.05.2025

- Corrected commands launching not supporting `;` in the command

## 1.2.5 - 18.05.2025

- Corrected `g++` dependancy wrongly verified as `gcc`
Expand Down
14 changes: 7 additions & 7 deletions auto-dark-light@gihaume/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,31 @@ This Cinnamon applet brings the ability to automatically switch between dark and
## Applet icons legend

<div style="display: flex; align-items: center; margin-top: 20px; margin-bottom: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/icons/auto-symbolic.svg" alt="Auto" width="75" height="75" style="margin-right: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/5.8/icons/auto-symbolic.svg" alt="Auto" width="75" height="75" style="margin-right: 20px;">
Automatic mode switch enabled.
</div>
<div style="display: flex; align-items: center; margin-top: 20px; margin-bottom: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/icons/auto-inverted-symbolic.svg" alt="Auto inverted" width="75" height="75" style="margin-right: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/5.8/icons/auto-inverted-symbolic.svg" alt="Auto inverted" width="75" height="75" style="margin-right: 20px;">
Automatic mode switch enabled but the current mode has been set while not in sync with the actual daytime, so any external changes won't update it until the next scheduled mode change or if entering auto mode switch again.
</div>
<div style="display: flex; align-items: center; margin-top: 20px; margin-bottom: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/icons/light-symbolic.svg" alt="Light" width="75" height="75" style="margin-right: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/5.8/icons/light-symbolic.svg" alt="Light" width="75" height="75" style="margin-right: 20px;">
Automatic mode switch disabled and the current mode is light.
</div>
<div style="display: flex; align-items: center; margin-top: 20px; margin-bottom: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/icons/dark-symbolic.svg" alt="Dark" width="75" height="75" style="margin-right: 20px;">
<img src="https://raw.githubusercontent.com/linuxmint/cinnamon-spices-applets/master/auto-dark-light@gihaume/files/auto-dark-light@gihaume/5.8/icons/dark-symbolic.svg" alt="Dark" width="75" height="75" style="margin-right: 20px;">
Automatic mode switch disabled and the current mode is dark.
</div>

## Dependencies

`make` and `gcc`, which can be installed on Debian-based system with `sudo apt install build-essential`.
`make` and `g++`, which can be installed on Debian-based system with `sudo apt install make g++`.

## Feedback

Add a `⭐ Score` on the [CINNAMON spices](https://cinnamon-spices.linuxmint.com/applets/view/397) page if you like this applet.
Add a `⭐ Score` on the [Cinnamon spices](https://cinnamon-spices.linuxmint.com/applets/view/397) page if you like this applet.

Report issues on the [GitHub repository](https://github.com/linuxmint/cinnamon-spices-applets/issues) in mentionning `@guillaume-mueller` to notify me.
Report issues on the [GitHub repository](https://github.com/linuxmint/cinnamon-spices-applets/issues) in mentioning `@guillaume-mueller`.

## Donate

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,33 @@
const Applet = imports.ui.applet;
const Main = imports.ui.main;
const Gettext = imports.gettext;
const {GLib} = imports.gi;
const Main = imports.ui.main;

class ThisApplet extends Applet.IconApplet {
constructor(metadata, orientation, panel_height, instance_id) {
super(orientation, panel_height, instance_id);

const applet_name = metadata.name;

Gettext.bindtextdomain(
applet_name,
GLib.get_home_dir() + "/.local/share/locale"
);
const _ = text => Gettext.dgettext(applet_name, text);

const critical_message =
`${_("Critical error")}${_(":")}`
+ `${_("this applet is only supported by")} Cinnamon >= 5.8.`;
Main.criticalNotify(applet_name, critical_message);
this.set_applet_tooltip(critical_message);

this.set_applet_icon_symbolic_name('on-error-symbolic');

Gettext.bindtextdomain(metadata.name, GLib.get_home_dir() + "/.local/share/locale");
function _(string) {
return Gettext.dgettext(metadata.name, string);
}
Object.assign(this, {applet_name, critical_message});
}

Main.criticalNotify(
metadata.name,
`${_("Critical error")}${_(":")} ${_("this applet is only supported for")} Cinnamon >= 5.8.`
);
on_applet_clicked() {
Main.criticalNotify(this.applet_name, this.critical_message);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,25 @@ module.exports = class Commands_launcher {
const name_for_error = name !== '' ? name : command;
let msg = `${_("the command")} '${name_for_error}' ${_("failed")}`;
if (error instanceof GLib.ShellError)
msg += ` ${_("due to a wrong format")}${_(":")} ${error.message}`;
msg += ` ${_("due to a wrong format")}.\n`
+ "\n"
+ `${_("Detail")}${_(":")}\n`
+ error.message;
else
if (error instanceof Gio.IOErrorEnum) {
if (error.code === Gio.IOErrorEnum.TIMED_OUT)
msg += ` ${_("due to time expiration")}`;
msg += ` ${_("due to time expiration")}.\n`
+ "\n"
+ `${_("Detail")}${_(":")}\n`
+ error.message;
else
if (error.code === Gio.IOErrorEnum.FAILED)
msg += ` ${_("with the following errors")}${_(":")} ${error.message}`
msg += ` ${_("due to an error")}.\n`
+ "\n"
+ `${_("Detail")}${_(":")}\n`
+ error.message;
} else
msg += `${_(":")} ${error}` // full `error` object so we may see the stack trace
msg += `${_(":")} ${error}` // last resort: full `error` object so we may see the stack trace

this.#callback_for_errors(msg);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,63 +1,61 @@
const {Gio, GLib} = imports.gi;

const TIMEOUT_EXIT_STATUS_SIGTERM = 124;
const TIMEOUT_EXIT_STATUS_SIGKILL = 137;
const SIGKILL_TIMEOUT = 10; // [s] the time to wait after 'timeout' sends SIGTERM so it sends SIGKILL

Gio._promisify(Gio.Subprocess.prototype, 'communicate_utf8_async');

/**
* Executes a command with a timeout and transmits any error on failure.
* @async
* @param {string} command - The shell command to execute.
* @param {number} [timeout=10] - The delay in seconds before cancelling the command. `0` means infinity/never.
* @param {number} [timeout=10] - The delay in seconds before cancelling the command with a SIGTERM, then a few seconds later with a SIGKILL. `0` means infinity/never.
* @returns {Promise<void>}
* @throws {GLib.ShellError} - If the command format is invalid.
* @throws {Gio.IOErrorEnum.TIMED_OUT} - If the command is cancelled due to a timeout.
* @throws {Gio.IOErrorEnum.FAILED} - If the command fails with a non-zero exit code. The error message is the `stderr` output if any, otherwise the exit status.
*/
module.exports = async function launch_command(command, timeout = 10) {
command = `sh -c ${GLib.shell_quote(`exec ${command}`)}`; // brings shell features
const [_ok, argvp] = GLib.shell_parse_argv(command); // can throw GLib.ShellError
const wrapped_command =
`timeout --kill-after=${SIGKILL_TIMEOUT} ${timeout}s sh -c ${GLib.shell_quote(command)}`;
const [_ok, argvp] = GLib.shell_parse_argv(wrapped_command); // can throw GLib.ShellError

const proc = new Gio.Subprocess({
const process = new Gio.Subprocess({
argv: argvp,
flags: Gio.SubprocessFlags.STDERR_PIPE
});

const cancellable = Gio.Cancellable.new();
const cancellable_signal_handler_id = cancellable.connect(
() => proc.force_exit()
);

proc.init(cancellable);

let timeout_event_source_id;
if (timeout !== 0)
timeout_event_source_id = GLib.timeout_add_seconds(
GLib.PRIORITY_DEFAULT,
timeout,
() => {
cancellable.cancel();
timeout_event_source_id = undefined;
}
);

try {
const [_stdout, stderr] = await proc.communicate_utf8_async(null, null);
const start_time = Date.now(); // [ms]
process.init(null);
const [_stdout, stderr] = await process.communicate_utf8_async(null, null);
const elapsed_time = (Date.now() - start_time) / 1000; // [s]

if (cancellable.is_cancelled())
const exit_status = process.get_exit_status();
switch (exit_status) {
case 0:
break;
case TIMEOUT_EXIT_STATUS_SIGTERM:
throw new Gio.IOErrorEnum({
code: Gio.IOErrorEnum.TIMED_OUT,
message: "timed out"
message: `may have been timed out by SIGTERM (GNU 'timeout' exit status ${TIMEOUT_EXIT_STATUS_SIGTERM})`
});

const exit_status = proc.get_exit_status();
if (exit_status !== 0) {
case TIMEOUT_EXIT_STATUS_SIGKILL:
throw new Gio.IOErrorEnum({
code: Gio.IOErrorEnum.TIMED_OUT,
message: `probably killed by an external SIGKILL (GNU 'timeout' exit status ${TIMEOUT_EXIT_STATUS_SIGKILL})`
});
case 1: // workaround for 'timeout' sending SIGKILL not reported with the intended 137 exit status
if (timeout > 0 && elapsed_time >= timeout + SIGKILL_TIMEOUT)
throw new Gio.IOErrorEnum({
code: Gio.IOErrorEnum.TIMED_OUT,
message: `probably timed out by SIGKILL`
});
// no break, needs to stay directly above the default case
default:
throw new Gio.IOErrorEnum({
code: Gio.IOErrorEnum.FAILED,
message: stderr ? stderr.trim() : "exit status: " + exit_status
});
}
} finally {
cancellable.disconnect(cancellable_signal_handler_id);
if (timeout_event_source_id)
GLib.source_remove(timeout_event_source_id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ module.exports = class Time_change_listener {
this.#callback_for_errors = callback_for_errors;

if (!(GLib.find_program_in_path('make') && GLib.find_program_in_path('g++')))
throw new Error(_("Missing dependencies `make` and `g++`. Install them, in e.g. on Debian-based system with `sudo apt install build-essential`, then reload the applet (in e.g. in restarting Cinnamon)."));
throw new Error(_("Missing dependencies `make` and/or `g++`. Install them, in e.g. on Debian-based system with `sudo apt install make g++`, then reload the applet in restarting Cinnamon."));

const compilation = new Gio.Subprocess({
argv: ['make', '-C', path],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
"settings_location_button_show-twilight-times": {
"type": "button",
"callback": "on_button_show_start_times",
"description": "Check today's switch times in a notification",
"description": "Show switch times in a notification",
"tooltip": "Show today's switch times for light and dark modes based on the current location via a system notification message"
},
"light-mode_themes_button_open-os-settings": {
Expand Down Expand Up @@ -230,7 +230,7 @@
"light-mode_themes_button_apply": {
"type": "button",
"callback": "on_button_apply_themes_light",
"description": "Apply to the system",
"description": "Test applying to the system",
"tooltip": "Apply the light mode themes to the system manually"
},
"both-modes_background_switch_enable": {
Expand Down Expand Up @@ -281,7 +281,7 @@
"light-mode_background_button_apply": {
"type": "button",
"callback": "on_button_apply_background_light",
"description": "Apply to the system",
"description": "Test applying to the system",
"tooltip": "Apply the light mode background settings to the system manually"
},
"light-mode_commands_list": {
Expand All @@ -293,13 +293,13 @@
{"id": "command", "title": "Command", "type": "string"}
],
"default" : [],
"tooltip" : "List of commands to be executed when switching to this mode\n\nNotes:\n- the name's purpose is for error messages\n- the expiry time kills the command's process\n- an expiry of 0 seconds means never\n- any child process won't be able to expire\n- the path is in the ~ directory\n- the error messages reports stderr when the return status is not 0 and will appear in a notification"
"tooltip" : "List of commands to be executed when switching to this mode\n\nNotes:\n- the name's purpose is for error messages\n- the expiry time terminates the command's process early\n- an expiry of 0 second means never\n- a process refusing to terminate 10 seconds after the expiry is killed\n- the path is in the user's home directory (~/)\n- the error messages are displayed in a notification and reports stderr"
},
"light-mode_commands_button_launch": {
"type": "button",
"callback": "on_button_launch_commands_light",
"description": "Launch commands",
"tooltip": "Launch the commands set for the light mode"
"description": "Test commands launch",
"tooltip": "Test launching the commands set for the light mode"
},
"dark-mode_themes_button_open-os-settings": {
"type": "button",
Expand Down Expand Up @@ -340,7 +340,7 @@
"dark-mode_themes_button_apply": {
"type": "button",
"callback": "on_button_apply_themes_dark",
"description": "Apply to the system",
"description": "Test applying to the system",
"tooltip": "Apply the dark mode themes to the system manually"
},
"dark-mode_commands_switch_enable": {
Expand Down Expand Up @@ -385,7 +385,7 @@
"dark-mode_background_button_apply": {
"type": "button",
"callback": "on_button_apply_background_dark",
"description": "Apply to the system",
"description": "Test applying to the system",
"tooltip": "Apply the dark mode background settings to the system manually"
},
"dark-mode_commands_list": {
Expand All @@ -402,8 +402,8 @@
"dark-mode_commands_button_launch": {
"type": "button",
"callback": "on_button_launch_commands_dark",
"description": "Launch commands",
"tooltip": "Launch the commands set for the dark mode"
"description": "Test commands launch",
"tooltip": "Test launching the commands set for the dark mode"
},

"has-detected-themes-light": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"uuid": "auto-dark-light@gihaume",
"name": "Automatic dark/light themes",
"description": "Automatically switch between dark and light themes at twilight times.",
"version": "1.2.5",
"version": "1.2.6",
"max-instances": "1",
"multiversion": true
}
Loading