Skip to content

Commit

Permalink
auto-dark-light@gihaume: Version 1.2.0 (#6521)
Browse files Browse the repository at this point in the history
*  Add commands launching
  • Loading branch information
guillaume-mueller authored Oct 22, 2024
1 parent 0e8845f commit 5737a65
Show file tree
Hide file tree
Showing 14 changed files with 1,012 additions and 226 deletions.
6 changes: 4 additions & 2 deletions auto-dark-light@gihaume/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Change log
## 1.2.0 - 22.10.2024

## 1.1.0 - 2.09.2024
- Added commands launching feature

## 1.1.0 - 02.09.2024

- Added desktop background support

Expand Down
35 changes: 17 additions & 18 deletions auto-dark-light@gihaume/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,42 @@ This Cinnamon applet brings the ability to automatically switch between dark and
## Features

- Dump system themes and desktop background settings in one click to take advantage of the Cinnamon settings menu.
- Sync location from the system `Region` and `City` settings using a local database to determine the geographical coordinates.
- Sync location from the system `Region` and `City` settings using a local database to automatically determine the geographical coordinates.
- Enter manually any geographical coordinates if needed.
- Always sync instantaneously with external changes of color scheme change, region/city, time (in e.g. from a sleep wakeup).
- Fully event based, zero polling.
- Always sync instantaneously with external changes of color scheme, region/city and time (useful in e.g. after a sleep wake up).
- Fully event and scheduling based, zero polling.
- Automatic mode switch can be disabled.
- Dark/light mode can be switched manually.
- Dark/light mode can always be switched manually.
- Schedule any amount of commands to launch at twilight times.

## Applet icons legend

<div style="display: flex; align-items: center;">
<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: 10px;">
<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;">
Automatic mode switch enabled.
</div>
<br>
<div style="display: flex; align-items: center;">
<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: 10px;">
<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;">
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>
<br>
<div style="display: flex; align-items: center;">
<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: 10px;">
<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;">
Automatic mode switch disabled and the current mode is light.
</div>
<br>
<div style="display: flex; align-items: center;">
<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: 10px;">
<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;">
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-essentials`.
`make` and `gcc`, which can be installed on Debian-based system with `sudo apt install build-essential`.

## Feedback

- Add a *Like* if you like it.
- Report issues on the [GitHub repository](https://github.com/linuxmint/cinnamon-spices-applets/issues) in mentionning `@guillaume-mueller` to notify me.
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.

## Donate

Expand Down
57 changes: 46 additions & 11 deletions auto-dark-light@gihaume/files/auto-dark-light@gihaume/applet.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const Timezone_change_listener = require('./lib/timezone_change_listener.js'
const Timezone_coordinates_finder = require('./lib/timezones_coordinates/timezone_coordinates_finder.js');
const Sleep_wakeup_listener = require('./lib/sleep_wakeup_listener.js');
const Color_scheme_change_listener = require('./lib/color_scheme_change_listener.js');
const Commands_launcher = require('./lib/commands_launcher.js');
const { _ } = require('./lib/translator.js');

const Applet = imports.ui.applet;
Expand All @@ -22,7 +23,11 @@ class ThisApplet extends Applet.IconApplet {
super(orientation, panel_height, instance_id);

this.metadata = metadata;
this.settings = new Settings.AppletSettings(this, metadata.uuid, instance_id);
this.settings = new Settings.AppletSettings(
this,
metadata.uuid,
instance_id
);

this.set_applet_tooltip(
_("Click: toggle dark/light mode") + "\n"
Expand Down Expand Up @@ -101,6 +106,17 @@ class ThisApplet extends Applet.IconApplet {
}
);

this.commands_light = new Commands_launcher(
this.settings,
'light-mode_commands_list',
this._notify_error.bind(this)
);
this.commands_dark = new Commands_launcher(
this.settings,
'dark-mode_commands_list',
this._notify_error.bind(this)
);

try {
this.time_change_listener = new Time_change_listener(
`${this.metadata.path}/lib/time_change_listener`,
Expand Down Expand Up @@ -144,9 +160,9 @@ class ThisApplet extends Applet.IconApplet {
"is_auto_mode_inverted"
);
this.settings.bind(
'settings_control_switch_auto-mode',
"ui_switch_auto_mode",
() => { this.apply_ui_switch_auto_mode(); this._update(); }
'settings_control_switch_auto-mode',
"ui_switch_auto_mode",
() => { this.apply_ui_switch_auto_mode(); this._update(); }
);
this.settings.bind(
'settings_location_switch_sync-from-timezone',
Expand All @@ -165,8 +181,16 @@ class ThisApplet extends Applet.IconApplet {
);
this.settings.bind(
'both-modes_background_switch_enable',
"ui_switch_enable_background"
)
"ui_switch_enable_background"
);
this.settings.bind(
'light-mode_commands_switch_enable',
"ui_switch_enable_commands_light"
);
this.settings.bind(
'dark-mode_commands_switch_enable',
"ui_switch_enable_commands_dark"
);
}

apply_ui_switch_dark_mode() {
Expand All @@ -179,6 +203,13 @@ class ThisApplet extends Applet.IconApplet {
this.ui_switch_dark_mode ? this.background_dark.apply()
: this.background_light.apply();

if (this.ui_switch_dark_mode) {
if (this.ui_switch_enable_commands_dark)
this.commands_dark.launch_commands();
} else {
if (this.ui_switch_enable_commands_light)
this.commands_light.launch_commands();
}
this._update_applet_icon();
}

Expand Down Expand Up @@ -332,9 +363,9 @@ class ThisApplet extends Applet.IconApplet {
if (!this._update_twilights())
return;
this._notify(
_("Today's automatic mode switch times:") + '\n'
+ `- ${_("Light mode:")} ${this.sunrise.as_string()}` + '\n'
+ `- ${_("Dark mode:") } ${this.sunset .as_string()}`
_("Today's automatic mode switch times") + _(":") + '\n'
+ `- ${_("Light mode")}${_(":")} ${this.sunrise.as_string()}` + '\n'
+ `- ${_("Dark mode") }${_(":")} ${this.sunset .as_string()}`
);
}

Expand Down Expand Up @@ -370,16 +401,20 @@ class ThisApplet extends Applet.IconApplet {

on_button_apply_background_dark() { this.background_dark.apply(); }

on_button_launch_commands_light() { this.commands_light.launch_commands(); }

on_button_launch_commands_dark() { this.commands_dark.launch_commands(); }

_notify(msg) { Main.notify(this.metadata.name, msg); }

_notify_error(msg) {
Main.notifyError(this.metadata.name, _("Error:") + " " + msg);
Main.notifyError(this.metadata.name, `${_("Error")}${_(":")} ${msg}`);
}

_notify_critical(msg) {
Main.criticalNotify(
this.metadata.name,
_("Critical error:") + " " + msg
`${_("Critical error")}${_(":")} ${msg}`
);
}

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const launch_command = require('./lib/launch_command.js');
const { _ } = require('./lib/translator.js');

const {Gio, GLib} = imports.gi;

class Commands_launcher {
#callback_for_errors;

/**
* @param {Settings.XletSettingsBase} settings - The settings of the desk/applet.
* @param {object} key_of_list - The keys of the settings' commands list.
* @param {function(string): void} callback_for_errors - The callback with a message for when an error occurs.
*/
constructor(settings, key_of_list, callback_for_errors) {
settings.bindWithObject(this, key_of_list, "list");
this.#callback_for_errors = callback_for_errors;
}

async launch_commands() {
for (const item of this.list) {
const { name, active, expiry, command } = item;

if (!active)
continue;

try { await launch_command(command, expiry); }
catch (error) {
let msg = `${_("the command")} '${name}' ${_("failed")}`;
if (error instanceof GLib.ShellError)
msg += ` ${_("due to a wrong format")}${_(":")} ${error.message}`;
else
if (error instanceof Gio.IOErrorEnum) {
if (error.code === Gio.IOErrorEnum.TIMED_OUT)
msg += ` ${_("due to time expiration")}`;
else
if (error.code === Gio.IOErrorEnum.FAILED)
msg += ` ${_("with the following errors")}${_(":")} ${error.message}`
} else
msg += `${_(":")} ${error}` // full `error` object so we may see the stack trace

this.#callback_for_errors(msg);
}
}
}
}

module.exports = Commands_launcher;
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
const {Gio, GLib} = imports.gi;

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_seconds=10] - The delay in seconds before cancelling the command. `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.
*/
async function launch_command(command, timeout_seconds = 10) {
const [_ok, argvp] = GLib.shell_parse_argv(command); // can throw GLib.ShellError

const proc = 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_seconds !== 0)
timeout_event_source_id = GLib.timeout_add_seconds(
GLib.PRIORITY_DEFAULT,
timeout_seconds,
() => {
cancellable.cancel();
timeout_event_source_id = undefined;
}
);

try {
const [_stdout, stderr] = await proc.communicate_utf8_async(null, null);

if (cancellable.is_cancelled())
throw new Gio.IOErrorEnum({
code: Gio.IOErrorEnum.TIMED_OUT,
message: "timed out"
});

const exit_status = proc.get_exit_status();
if (exit_status !== 0) {
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);
}
}

module.exports = launch_command;

// // Example usage:
//
// async function main() {
// console.error("start");
// try {
// await launch_command('./test', 5);
// } catch (error) {
// if (error instanceof GLib.ShellError)
// console.error("command format: " + error.message);
// else
// if (error instanceof Gio.IOErrorEnum) {
// if (error.code === Gio.IOErrorEnum.TIMED_OUT)
// console.error("command aborted due to time out");
// else
// if (error.code === Gio.IOErrorEnum.FAILED)
// console.error("command failed: " + error.message);
// } else
// console.error(error); // full object so we can see the stack trace
// }
// }

// main();

// new GLib.MainLoop(null, false).run();
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
"uuid": "auto-dark-light@gihaume",
"name": "Automatic dark/light themes",
"description": "Automatically switch between dark and light themes at twilight times.",
"version": "1.1.0",
"version": "1.2.0",
"max-instances": "1"
}
Loading

0 comments on commit 5737a65

Please sign in to comment.