Architecture rewrite and new features
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { GLib } = imports.gi;
|
||||
const { extensionUtils } = imports.misc;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const e = Me.imports.extension;
|
||||
const { log_debug } = Me.imports.utils;
|
||||
|
||||
/**
|
||||
* The Backgrounder is responsible for changing the desktop background
|
||||
* according to the time.
|
||||
*
|
||||
* When the user changes its desktop background (for example via the system
|
||||
* settings), it will use it as the current time background.
|
||||
*/
|
||||
var Backgrounder = class {
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling Backgrounder...');
|
||||
this._watch_status();
|
||||
if ( e.settingsManager.backgrounds_enabled ) {
|
||||
this._change_background(e.timer.time);
|
||||
this._connect_settings();
|
||||
this._connect_timer();
|
||||
}
|
||||
log_debug('Backgrounder enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Backgrounder...');
|
||||
this._disconnect_timer();
|
||||
this._disconnect_settings();
|
||||
this._unwatch_status();
|
||||
log_debug('Backgrounder disabled.');
|
||||
}
|
||||
|
||||
|
||||
_watch_status() {
|
||||
log_debug('Watching backgrounds status...');
|
||||
this._backgrounds_status_changed_connect = e.settingsManager.connect('backgrounds-status-changed', this._on_backgrounds_status_changed.bind(this));
|
||||
}
|
||||
|
||||
_unwatch_status() {
|
||||
if ( this._backgrounds_status_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._backgrounds_status_changed_connect);
|
||||
this._backgrounds_status_changed_connect = null;
|
||||
}
|
||||
log_debug('Stopped watching backgrounds status.');
|
||||
}
|
||||
|
||||
_connect_settings() {
|
||||
log_debug('Connecting Backgrounder to settings...');
|
||||
this._background_time_changed_connect = e.settingsManager.connect('background-time-changed', this._on_background_time_changed.bind(this));
|
||||
this._background_changed_connect = e.settingsManager.connect('background-changed', this._on_background_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_settings() {
|
||||
if ( this._background_time_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._background_time_changed_connect);
|
||||
this._background_time_changed_connect = null;
|
||||
}
|
||||
if ( this._background_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._background_changed_connect);
|
||||
this._background_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnected Backgrounder from settings.');
|
||||
}
|
||||
|
||||
_connect_timer() {
|
||||
log_debug('Connecting Backgrounder to Timer...');
|
||||
this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_timer() {
|
||||
if ( this._time_changed_connect ) {
|
||||
e.timer.disconnect(this._time_changed_connect);
|
||||
this._time_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnecting Backgrounder from Timer.');
|
||||
}
|
||||
|
||||
|
||||
_on_backgrounds_status_changed(settings, enabled) {
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
_on_background_time_changed(settings, changed_background_time) {
|
||||
if ( changed_background_time === e.timer.time ) {
|
||||
this._change_background(changed_background_time);
|
||||
}
|
||||
}
|
||||
|
||||
_on_background_changed(settings, new_background) {
|
||||
switch (e.timer.time) {
|
||||
case 'day':
|
||||
e.settingsManager.background_day = new_background;
|
||||
break;
|
||||
case 'night':
|
||||
e.settingsManager.background_night = new_background;
|
||||
}
|
||||
}
|
||||
|
||||
_on_time_changed(timer, new_time) {
|
||||
this._change_background(new_time);
|
||||
}
|
||||
|
||||
|
||||
_change_background(time) {
|
||||
e.settingsManager.background = time === 'day' ? e.settingsManager.background_day : e.settingsManager.background_night;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { GLib } = imports.gi;
|
||||
const { extensionUtils } = imports.misc;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const e = Me.imports.extension;
|
||||
const { log_debug } = Me.imports.utils;
|
||||
|
||||
|
||||
/**
|
||||
* The Commander is responsible for spawning commands according to the time.
|
||||
*/
|
||||
var Commander = class {
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling Commander...');
|
||||
this._watch_status();
|
||||
if ( e.settingsManager.commands_enabled ) {
|
||||
this._connect_timer();
|
||||
}
|
||||
log_debug('Commander enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Commander...');
|
||||
this._disconnect_timer();
|
||||
this._unwatch_status();
|
||||
log_debug('Commander disabled.');
|
||||
}
|
||||
|
||||
|
||||
_watch_status() {
|
||||
log_debug('Watching commands status...');
|
||||
this._commands_status_changed_connect = e.settingsManager.connect('commands-status-changed', this._on_commands_status_changed.bind(this));
|
||||
}
|
||||
|
||||
_unwatch_status() {
|
||||
if ( this._commands_status_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._commands_status_changed_connect);
|
||||
this._commands_status_changed_connect = null;
|
||||
}
|
||||
log_debug('Stopped watching commands status.');
|
||||
}
|
||||
|
||||
_connect_timer() {
|
||||
log_debug('Connecting Commander to Timer...');
|
||||
this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_timer() {
|
||||
if ( this._time_changed_connect ) {
|
||||
e.timer.disconnect(this._time_changed_connect);
|
||||
this._time_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnecting Commander from Timer.');
|
||||
}
|
||||
|
||||
|
||||
_on_commands_status_changed(settings, enabled) {
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
_on_time_changed(timer, new_time) {
|
||||
this._spawn_command(new_time);
|
||||
}
|
||||
|
||||
|
||||
_spawn_command(time) {
|
||||
const command = time === 'day' ? e.settingsManager.command_sunrise : e.settingsManager.command_sunset;
|
||||
GLib.spawn_async(null, ['sh', '-c', command], null, GLib.SpawnFlags.SEARCH_PATH, null);
|
||||
log_debug(`Spawned ${time} command.`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { Gio, GLib, Gtk } = imports.gi;
|
||||
const { extensionUtils } = imports.misc;
|
||||
const { main } = imports.ui;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const e = Me.imports.extension;
|
||||
const { log_debug, log_error, get_installed_gtk_themes } = Me.imports.utils;
|
||||
const { GtkVariants } = Me.imports.modules.GtkVariants;
|
||||
|
||||
const Gettext = imports.gettext.domain(Me.metadata.uuid);
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
|
||||
/**
|
||||
* The GTK Themer is responsible for changing the GTK theme according to the
|
||||
* time.
|
||||
*
|
||||
* When the user changes its GTK theme (for example via GNOME Tweaks), it will
|
||||
* try to automatically guess the day and night variants for this theme. It
|
||||
* will warn the user if it is unable to guess.
|
||||
*
|
||||
* In manual mode, it will not attempt to update the variants. The user can
|
||||
* change the GTK variants in the extension's preferences.
|
||||
*/
|
||||
var GtkThemer = class {
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling GTK Themer...');
|
||||
try {
|
||||
this._update_variants();
|
||||
this._set_variant(e.timer.time);
|
||||
this._connect_settings();
|
||||
this._connect_timer();
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
log_debug('GTK Themer enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling GTK Themer...');
|
||||
this._disconnect_timer();
|
||||
this._disconnect_settings();
|
||||
this._reset_theme();
|
||||
log_debug('GTK Themer disabled.');
|
||||
}
|
||||
|
||||
|
||||
_connect_settings() {
|
||||
log_debug('Connecting GTK Themer to settings...');
|
||||
this._gtk_variant_changed_connect = e.settingsManager.connect('gtk-variant-changed', this._on_gtk_variant_changed.bind(this));
|
||||
this._gtk_theme_changed_connect = e.settingsManager.connect('gtk-theme-changed', this._on_gtk_theme_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_settings() {
|
||||
if ( this._gtk_variant_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._gtk_variant_changed_connect);
|
||||
this._gtk_variant_changed_connect = null;
|
||||
}
|
||||
if ( this._gtk_theme_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._gtk_theme_changed_connect);
|
||||
this._gtk_theme_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnected GTK Themer from settings.');
|
||||
}
|
||||
|
||||
_connect_timer() {
|
||||
log_debug('Connecting GTK Themer to Timer...');
|
||||
this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_timer() {
|
||||
if ( this._time_changed_connect ) {
|
||||
e.timer.disconnect(this._time_changed_connect);
|
||||
this._time_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnected GTK Themer from Timer.');
|
||||
}
|
||||
|
||||
|
||||
_on_gtk_variant_changed(settings, changed_variant_time) {
|
||||
if ( changed_variant_time === e.timer.time ) {
|
||||
this._set_variant(changed_variant_time);
|
||||
}
|
||||
}
|
||||
|
||||
_on_gtk_theme_changed(settings, new_theme) {
|
||||
try {
|
||||
this._update_variants();
|
||||
this._set_variant(e.timer.time);
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
_on_time_changed(timer, new_time) {
|
||||
this._set_variant(new_time);
|
||||
}
|
||||
|
||||
|
||||
_are_variants_up_to_date() {
|
||||
return ( e.settingsManager.gtk_theme === e.settingsManager.gtk_variant_day || e.settingsManager.gtk_theme === e.settingsManager.gtk_variant_night );
|
||||
}
|
||||
|
||||
_set_variant(time) {
|
||||
log_debug(`Setting the GTK ${time} variant...`);
|
||||
switch (time) {
|
||||
case 'day':
|
||||
e.settingsManager.gtk_theme = e.settingsManager.gtk_variant_day;
|
||||
break;
|
||||
case 'night':
|
||||
e.settingsManager.gtk_theme = e.settingsManager.gtk_variant_night;
|
||||
break;
|
||||
case 'original':
|
||||
e.settingsManager.gtk_theme = e.settingsManager.gtk_variant_original;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_update_variants() {
|
||||
if ( e.settingsManager.manual_gtk_variants || this._are_variants_up_to_date() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug('Updating GTK variants...');
|
||||
const variants = GtkVariants.guess_from(e.settingsManager.gtk_theme);
|
||||
const installed_themes = get_installed_gtk_themes();
|
||||
|
||||
if ( !installed_themes.has(variants.get('day')) || !installed_themes.has(variants.get('night')) ) {
|
||||
e.settingsManager.gtk_variant_original = variants.get('original');
|
||||
const message = _('Unable to automatically detect the day and night variants for the "%s" GTK theme. Please manually choose them in the extension\'s preferences.').format(variants.get('original'));
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
e.settingsManager.gtk_variant_day = variants.get('day');
|
||||
e.settingsManager.gtk_variant_night = variants.get('night');
|
||||
e.settingsManager.gtk_variant_original = variants.get('original');
|
||||
log_debug(`New GTK variants. { day: '${variants.get('day')}'; night: '${variants.get('night')}' }`);
|
||||
}
|
||||
|
||||
_reset_theme() {
|
||||
// We don't reset the theme when locking the session to prevent
|
||||
// flicker on unlocking
|
||||
if ( !main.screenShield.locked ) {
|
||||
log_debug('Resetting to the user\'s original GTK theme...');
|
||||
this._set_variant('original');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,32 +17,30 @@ You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
The magic of guessing theme variants happens here.
|
||||
|
||||
If the theme doesn't fit a particular case, we'll do the following:
|
||||
- Remove any signs of a dark variant to the theme name to get the day
|
||||
variant
|
||||
- Remove any signs of a light variant to the day variant and add '-dark' to
|
||||
get the night variant
|
||||
|
||||
For themes that don't work with the general rule, a particular case must be
|
||||
written. Day and night variants should be guessed with the most generic light
|
||||
and dark variants the theme offer, except if the user explicitly chose a
|
||||
specific variant.
|
||||
|
||||
Light variants, from the most to the least generic:
|
||||
- ''
|
||||
- '-light'
|
||||
- '-darker'
|
||||
|
||||
Dark variants, from the most the least generic:
|
||||
- '-dark'
|
||||
- '-darkest'
|
||||
*/
|
||||
|
||||
var Variants = class {
|
||||
/**
|
||||
* The magic of guessing theme variants happens here.
|
||||
*
|
||||
* If the theme doesn't fit a particular case, we'll do the following:
|
||||
* - Remove any signs of a dark variant to the theme name to get the day
|
||||
* variant
|
||||
* - Remove any signs of a light variant to the day variant and add '-dark' to
|
||||
* get the night variant
|
||||
*
|
||||
* For themes that don't work with the general rule, a particular case must be
|
||||
* written. Day and night variants should be guessed with the most generic light
|
||||
* and dark variants the theme offer, except if the user explicitly chose a
|
||||
* specific variant.
|
||||
*
|
||||
* Light variants, from the most to the least generic:
|
||||
* - ''
|
||||
* - '-light'
|
||||
* - '-darker'
|
||||
*
|
||||
* Dark variants, from the most the least generic:
|
||||
* - '-dark'
|
||||
* - '-darkest'
|
||||
*/
|
||||
var GtkVariants = class {
|
||||
|
||||
static guess_from(name) {
|
||||
const variants = new Map();
|
||||
@@ -0,0 +1,427 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { Gio } = imports.gi;
|
||||
const { extensionUtils } = imports.misc;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const { log_debug, get_userthemes_extension, get_userthemes_settings } = Me.imports.utils;
|
||||
|
||||
|
||||
const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
|
||||
if ( shell_minor_version <= 30 ) {
|
||||
extensionUtils.getSettings = Me.imports.convenience.getSettings;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The Settings Manager centralizes all the different settings the extension
|
||||
* needs. It handles getting and settings values as well as signaling any
|
||||
* changes.
|
||||
*/
|
||||
var SettingsManager = class {
|
||||
|
||||
constructor() {
|
||||
log_debug('Initializing settings...');
|
||||
this._extensionsSettings = extensionUtils.getSettings();
|
||||
this._colorSettings = new Gio.Settings({ schema: 'org.gnome.settings-daemon.plugins.color' });
|
||||
this._locationSettings = new Gio.Settings({ schema: 'org.gnome.system.location' });
|
||||
this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
|
||||
this._backgroundSettings = new Gio.Settings({ schema: 'org.gnome.desktop.background' });
|
||||
this._userthemesSettings = get_userthemes_settings();
|
||||
log_debug('Settings initialized.');
|
||||
}
|
||||
|
||||
enable() {
|
||||
log_debug('Connecting settings signals...');
|
||||
this._gtk_variant_day_changed_connect = this._extensionsSettings.connect('changed::gtk-variant-day', this._on_gtk_variant_day_changed.bind(this));
|
||||
this._gtk_variant_night_changed_connect = this._extensionsSettings.connect('changed::gtk-variant-night', this._on_gtk_variant_night_changed.bind(this));
|
||||
this._gtk_variant_original_changed_connect = this._extensionsSettings.connect('changed::gtk-variant-original', this._on_gtk_variant_original_changed.bind(this));
|
||||
this._shell_variant_day_changed_connect = this._extensionsSettings.connect('changed::shell-variant-day', this._on_shell_variant_day_changed.bind(this));
|
||||
this._shell_variant_night_changed_connect = this._extensionsSettings.connect('changed::shell-variant-night', this._on_shell_variant_night_changed.bind(this));
|
||||
this._shell_variant_original_changed_connect = this._extensionsSettings.connect('changed::shell-variant-original', this._on_shell_variant_original_changed.bind(this));
|
||||
this._time_source_changed_connect = this._extensionsSettings.connect('changed::time-source', this._on_time_source_changed.bind(this));
|
||||
this._manual_time_source_changed_connect = this._extensionsSettings.connect('changed::manual-time-source', this._on_manual_time_source_changed.bind(this));
|
||||
this._commands_status_connect = this._extensionsSettings.connect('changed::commands-enabled', this._on_commands_status_changed.bind(this));
|
||||
this._backgrounds_status_connect = this._extensionsSettings.connect('changed::backgrounds-enabled', this._on_backgrounds_status_changed.bind(this));
|
||||
this._background_day_changed_connect = this._extensionsSettings.connect('changed::background-day', this._on_background_day_changed.bind(this));
|
||||
this._background_night_changed_connect = this._extensionsSettings.connect('changed::background-night', this._on_background_night_changed.bind(this));
|
||||
this._nightlight_status_connect = this._colorSettings.connect('changed::night-light-enabled', this._on_nightlight_status_changed.bind(this));
|
||||
this._location_status_connect = this._locationSettings.connect('changed::enabled', this._on_location_status_changed.bind(this));
|
||||
this._gtk_theme_changed_connect = this._interfaceSettings.connect('changed::gtk-theme', this._on_gtk_theme_changed.bind(this));
|
||||
this._background_changed_connect = this._backgroundSettings.connect('changed::picture-uri', this._on_background_changed.bind(this));
|
||||
if ( this._userthemesSettings ) {
|
||||
this._shell_theme_changed_connect = this._userthemesSettings.connect('changed::name', this._on_shell_theme_changed.bind(this));
|
||||
}
|
||||
log_debug('Settings signals connected.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disconnecting settings signals...');
|
||||
this._extensionsSettings.disconnect(this._gtk_variant_day_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._gtk_variant_night_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._gtk_variant_original_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._shell_variant_day_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._shell_variant_night_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._shell_variant_original_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._time_source_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._manual_time_source_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._commands_status_connect);
|
||||
this._extensionsSettings.disconnect(this._backgrounds_status_connect);
|
||||
this._extensionsSettings.disconnect(this._background_day_changed_connect);
|
||||
this._extensionsSettings.disconnect(this._background_night_changed_connect);
|
||||
this._colorSettings.disconnect(this._nightlight_status_connect);
|
||||
this._locationSettings.disconnect(this._location_status_connect);
|
||||
this._interfaceSettings.disconnect(this._gtk_theme_changed_connect);
|
||||
this._backgroundSettings.disconnect(this._background_changed_connect);
|
||||
if ( this._userthemesSettings ) {
|
||||
this._userthemesSettings.disconnect(this._shell_theme_changed_connect);
|
||||
}
|
||||
log_debug('Settings signals disconnected.');
|
||||
}
|
||||
|
||||
/**
|
||||
* SETTERS AND GETTERS
|
||||
*/
|
||||
|
||||
/* GTK variants settings */
|
||||
|
||||
get gtk_variant_day() {
|
||||
return this._extensionsSettings.get_string('gtk-variant-day');
|
||||
}
|
||||
|
||||
set gtk_variant_day(value) {
|
||||
if ( value !== this.gtk_variant_day ) {
|
||||
this._extensionsSettings.set_string('gtk-variant-day', value);
|
||||
log_debug(`The GTK day variant has been set to '${value}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
get gtk_variant_night() {
|
||||
return this._extensionsSettings.get_string('gtk-variant-night');
|
||||
}
|
||||
|
||||
set gtk_variant_night(value) {
|
||||
if ( value !== this.gtk_variant_night ) {
|
||||
this._extensionsSettings.set_string('gtk-variant-night', value);
|
||||
log_debug(`The GTK night variant has been set to '${value}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
get gtk_variant_original() {
|
||||
return this._extensionsSettings.get_string('gtk-variant-original');
|
||||
}
|
||||
|
||||
set gtk_variant_original(value) {
|
||||
if ( value !== this.gtk_variant_original ) {
|
||||
this._extensionsSettings.set_string('gtk-variant-original', value);
|
||||
log_debug(`The GTK original variant has been set to '${value}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
get manual_gtk_variants() {
|
||||
return this._extensionsSettings.get_boolean('manual-gtk-variants');
|
||||
}
|
||||
|
||||
|
||||
/* Shell variants settings */
|
||||
|
||||
get shell_variant_day() {
|
||||
return this._extensionsSettings.get_string('shell-variant-day');
|
||||
}
|
||||
|
||||
set shell_variant_day(value) {
|
||||
if ( value !== this.shell_variant_day ) {
|
||||
this._extensionsSettings.set_string('shell-variant-day', value);
|
||||
log_debug(`The shell day variant has been set to '${value}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
get shell_variant_night() {
|
||||
return this._extensionsSettings.get_string('shell-variant-night');
|
||||
}
|
||||
|
||||
set shell_variant_night(value) {
|
||||
if ( value !== this.shell_variant_night ) {
|
||||
this._extensionsSettings.set_string('shell-variant-night', value);
|
||||
log_debug(`The shell night variant has been set to '${value}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
get shell_variant_original() {
|
||||
return this._extensionsSettings.get_string('shell-variant-original');
|
||||
}
|
||||
|
||||
set shell_variant_original(value) {
|
||||
if ( value !== this.shell_variant_original ) {
|
||||
this._extensionsSettings.set_string('shell-variant-original', value);
|
||||
log_debug(`The shell original variant has been set to '${value}'.`);
|
||||
}
|
||||
}
|
||||
|
||||
get manual_shell_variants() {
|
||||
return this._extensionsSettings.get_boolean('manual-shell-variants');
|
||||
}
|
||||
|
||||
|
||||
/* Time source settings */
|
||||
|
||||
get time_source() {
|
||||
return this._extensionsSettings.get_string('time-source');
|
||||
}
|
||||
|
||||
get manual_time_source() {
|
||||
return this._extensionsSettings.get_boolean('manual-time-source');
|
||||
}
|
||||
|
||||
set time_source(value) {
|
||||
if ( value !== this.time_source ) {
|
||||
this._extensionsSettings.set_string('time-source', value);
|
||||
log_debug(`The time source has been set to ${value}.`);
|
||||
}
|
||||
}
|
||||
|
||||
get schedule_sunrise() {
|
||||
return this._extensionsSettings.get_double('schedule-sunrise');
|
||||
}
|
||||
|
||||
get schedule_sunset() {
|
||||
return this._extensionsSettings.get_double('schedule-sunset');
|
||||
}
|
||||
|
||||
|
||||
/* Commands settings */
|
||||
|
||||
get commands_enabled() {
|
||||
return this._extensionsSettings.get_boolean('commands-enabled');
|
||||
}
|
||||
|
||||
get command_sunrise() {
|
||||
return this._extensionsSettings.get_string('command-sunrise');
|
||||
}
|
||||
|
||||
get command_sunset() {
|
||||
return this._extensionsSettings.get_string('command-sunset');
|
||||
}
|
||||
|
||||
|
||||
/* Background settings */
|
||||
|
||||
get backgrounds_enabled() {
|
||||
return this._extensionsSettings.get_boolean('backgrounds-enabled');
|
||||
}
|
||||
|
||||
get background_day() {
|
||||
return this._extensionsSettings.get_string('background-day') || this.background;
|
||||
}
|
||||
|
||||
set background_day(value) {
|
||||
this._extensionsSettings.set_string('background-day', value);
|
||||
}
|
||||
|
||||
get background_night() {
|
||||
return this._extensionsSettings.get_string('background-night') || this.background;
|
||||
}
|
||||
|
||||
set background_night(value) {
|
||||
this._extensionsSettings.set_string('background-night', value);
|
||||
}
|
||||
|
||||
|
||||
/* Night Light settings */
|
||||
|
||||
get nightlight_enabled() {
|
||||
return this._colorSettings.get_boolean('night-light-enabled');
|
||||
}
|
||||
|
||||
|
||||
/* Location settings */
|
||||
|
||||
get location_enabled() {
|
||||
return this._locationSettings.get_boolean('enabled');
|
||||
}
|
||||
|
||||
|
||||
/* GTK theme settings */
|
||||
|
||||
get gtk_theme() {
|
||||
return this._interfaceSettings.get_string('gtk-theme');
|
||||
}
|
||||
|
||||
set gtk_theme(value) {
|
||||
if ( value !== this.gtk_theme ) {
|
||||
this._interfaceSettings.set_string('gtk-theme', value);
|
||||
log_debug(`GTK theme has been set to '${value}.'`);
|
||||
}
|
||||
}
|
||||
|
||||
/* Shell theme settings */
|
||||
|
||||
get shell_theme() {
|
||||
if ( this._userthemesSettings ) {
|
||||
return this._userthemesSettings.get_string('name');
|
||||
}
|
||||
}
|
||||
|
||||
set shell_theme(value) {
|
||||
if ( this._userthemesSettings && value !== this.shell_theme ) {
|
||||
this._userthemesSettings.set_string('name', value);
|
||||
}
|
||||
}
|
||||
|
||||
get use_userthemes() {
|
||||
const extension = get_userthemes_extension();
|
||||
return (extension && extension.state === 1);
|
||||
}
|
||||
|
||||
|
||||
/* Background settings */
|
||||
|
||||
get background() {
|
||||
return this._backgroundSettings.get_string('picture-uri');
|
||||
}
|
||||
|
||||
set background(value) {
|
||||
if ( value !== this.background ) {
|
||||
this._backgroundSettings.set_string('picture-uri', value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* SIGNALS
|
||||
*/
|
||||
|
||||
/* GTK variants */
|
||||
|
||||
_on_gtk_variant_day_changed(settings, changed_key) {
|
||||
log_debug(`GTK day variant has changed to '${this.gtk_variant_day}'.`);
|
||||
this.emit('gtk-variant-changed', 'day');
|
||||
}
|
||||
|
||||
_on_gtk_variant_night_changed(settings, changed_key) {
|
||||
log_debug(`GTK night variant has changed to '${this.gtk_variant_night}'.`);
|
||||
this.emit('gtk-variant-changed', 'night');
|
||||
}
|
||||
|
||||
_on_gtk_variant_original_changed(settings, changed_key) {
|
||||
log_debug(`GTK original variant has changed to '${this.gtk_variant_original}'.`);
|
||||
this.emit('gtk-variant-changed', 'original');
|
||||
}
|
||||
|
||||
|
||||
/* Shell variants */
|
||||
|
||||
_on_shell_variant_day_changed(settings, changed_key) {
|
||||
log_debug(`Shell day variant has changed to '${this.shell_variant_day}'.`);
|
||||
this.emit('shell-variant-changed', 'day');
|
||||
}
|
||||
|
||||
_on_shell_variant_night_changed(settings, changed_key) {
|
||||
log_debug(`Shell night variant has changed to '${this.shell_variant_night}'.`);
|
||||
this.emit('shell-variant-changed', 'night');
|
||||
}
|
||||
|
||||
_on_shell_variant_original_changed(settings, changed_key) {
|
||||
log_debug(`Shell original variant has changed to '${this.shell_variant_original}'.`);
|
||||
this.emit('shell-variant-changed', 'original');
|
||||
}
|
||||
|
||||
|
||||
/* Time source */
|
||||
|
||||
_on_time_source_changed(settings, changed_key) {
|
||||
log_debug(`Time source has changed to ${this.time_source}.`);
|
||||
this.emit('time-source-changed', this.time_source);
|
||||
}
|
||||
|
||||
_on_manual_time_source_changed(settings, changed_key) {
|
||||
log_debug('Manual time source has been ' + (this.manual_time_source ? 'ena' : 'disa') + 'bled.');
|
||||
this.emit('manual-time-source-changed', this.manual_time_source);
|
||||
}
|
||||
|
||||
|
||||
/* Commands */
|
||||
|
||||
_on_commands_status_changed(settings, changed_key) {
|
||||
log_debug('Commands have been ' + (this.commands_enabled ? 'ena' : 'disa') + 'bled.');
|
||||
this.emit('commands-status-changed', this.commands_enabled);
|
||||
}
|
||||
|
||||
|
||||
/* Backgrounds */
|
||||
|
||||
_on_backgrounds_status_changed(settings, changed_key) {
|
||||
log_debug('Backgrounds have been ' + (this.backgrounds_enabled ? 'ena' : 'disa') + 'bled.');
|
||||
this.emit('backgrounds-status-changed', this.backgrounds_enabled);
|
||||
}
|
||||
|
||||
_on_background_day_changed(settings, changed_key) {
|
||||
log_debug(`Day background has changed to '${this.background_day}'.`);
|
||||
this.emit('background-time-changed', 'day');
|
||||
}
|
||||
|
||||
_on_background_night_changed(settings, changed_key) {
|
||||
log_debug(`Night background has changed to '${this.background_night}'.`);
|
||||
this.emit('background-time-changed', 'night');
|
||||
}
|
||||
|
||||
|
||||
/* Night Light */
|
||||
|
||||
_on_nightlight_status_changed(settings, changed_key) {
|
||||
log_debug('Night Light has been ' + (this.nightlight_enabled ? 'ena' : 'disa') + 'bled.');
|
||||
this.emit('nightlight-status-changed', this.nightlight_enabled);
|
||||
}
|
||||
|
||||
|
||||
/* Location */
|
||||
|
||||
_on_location_status_changed(settings, changed_key) {
|
||||
log_debug('Location has been ' + (this.location_enabled ? 'ena' : 'disa') + 'bled.');
|
||||
this.emit('location-status-changed', this.location_enabled);
|
||||
}
|
||||
|
||||
|
||||
/* GTK theme */
|
||||
|
||||
_on_gtk_theme_changed(settings, changed_key) {
|
||||
log_debug(`GTK theme has changed to '${this.gtk_theme}'.`);
|
||||
this.emit('gtk-theme-changed', this.gtk_theme);
|
||||
}
|
||||
|
||||
|
||||
/* Background */
|
||||
|
||||
_on_background_changed(settings, changed_key) {
|
||||
log_debug(`Background has changed to '${this.background}'.`);
|
||||
this.emit('background-changed', this.background);
|
||||
}
|
||||
|
||||
|
||||
/* Shell theme */
|
||||
|
||||
_on_shell_theme_changed(settings, changed_key) {
|
||||
log_debug(`Shell theme has changed to '${this.shell_theme}'.`);
|
||||
this.emit('shell-theme-changed', this.shell_theme);
|
||||
}
|
||||
|
||||
}
|
||||
Signals.addSignalMethods(SettingsManager.prototype);
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { extensionUtils } = imports.misc;
|
||||
const Signals = imports.signals;
|
||||
const { main } = imports.ui;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const e = Me.imports.extension;
|
||||
const { log_debug, log_error, get_theme_dirs_paths, get_installed_shell_themes, get_shell_theme_stylesheet, apply_shell_stylesheet } = Me.imports.utils;
|
||||
const { ShellVariants } = Me.imports.modules.ShellVariants;
|
||||
|
||||
const Gettext = imports.gettext.domain(Me.metadata.uuid);
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
/**
|
||||
* The Shell Themer is responsible for changing the GTK theme according to the
|
||||
* time. It will use the User Themes extension to do so if it is enabled.
|
||||
*
|
||||
* When the user changes its shell theme (for example via GNOME Tweaks), it will
|
||||
* try to automatically guess the day and night variants for this theme. It
|
||||
* will warn the user if it is unable to guess.
|
||||
*
|
||||
* In manual mode, it will not attempt to update the variants. The user can
|
||||
* change the shell variants in the extension's preferences.
|
||||
*/
|
||||
var ShellThemer = class {
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling Shell Themer...');
|
||||
try {
|
||||
this._update_variants();
|
||||
this._set_variant(e.timer.time);
|
||||
this._connect_settings();
|
||||
this._connect_timer();
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
log_debug('Shell Themer enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Shell Themer...');
|
||||
this._disconnect_timer();
|
||||
this._disconnect_settings();
|
||||
this._reset_theme();
|
||||
log_debug('Shell Themer disabled.');
|
||||
}
|
||||
|
||||
|
||||
_connect_settings() {
|
||||
log_debug('Connecting Shell Themer to settings...');
|
||||
this._shell_variant_changed_connect = e.settingsManager.connect('shell-variant-changed', this._on_shell_variant_changed.bind(this));
|
||||
this._shell_theme_changed_connect = e.settingsManager.connect('shell-theme-changed', this._on_shell_theme_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_settings() {
|
||||
if ( this._shell_variant_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._shell_variant_changed_connect);
|
||||
this._shell_variant_changed_connect = null;
|
||||
}
|
||||
if ( this._shell_theme_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._shell_theme_changed_connect);
|
||||
this._shell_theme_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnected Shell Themer from settings.');
|
||||
}
|
||||
|
||||
_connect_timer() {
|
||||
log_debug('Connecting Shell Themer to Timer...');
|
||||
this._time_changed_connect = e.timer.connect('time-changed', this._on_time_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_timer() {
|
||||
if ( this._time_changed_connect ) {
|
||||
e.timer.disconnect(this._time_changed_connect);
|
||||
this._time_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnected Shell Themer from Timer.');
|
||||
}
|
||||
|
||||
|
||||
_on_shell_variant_changed(settings, changed_variant_time) {
|
||||
if ( changed_variant_time === e.timer.time ) {
|
||||
this._set_variant(e.timer.time);
|
||||
}
|
||||
}
|
||||
|
||||
_on_shell_theme_changed(settings, new_theme) {
|
||||
try {
|
||||
this._update_variants();
|
||||
this._set_variant(e.timer.time);
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
_on_time_changed(timer, new_time) {
|
||||
this._set_variant(new_time);
|
||||
}
|
||||
|
||||
|
||||
_are_variants_up_to_date() {
|
||||
return ( e.settingsManager.shell_theme === e.settingsManager.shell_variant_day || e.settingsManager.shell_theme === e.settingsManager.shell_variant_night );
|
||||
}
|
||||
|
||||
_set_variant(time) {
|
||||
log_debug(`Setting the shell ${time} variant...`);
|
||||
let shell_theme;
|
||||
switch (time) {
|
||||
case 'day':
|
||||
shell_theme = e.settingsManager.shell_variant_day;
|
||||
break;
|
||||
case 'night':
|
||||
shell_theme = e.settingsManager.shell_variant_night;
|
||||
break;
|
||||
case 'original':
|
||||
shell_theme = e.settingsManager.shell_variant_original;
|
||||
break;
|
||||
}
|
||||
if ( e.settingsManager.use_userthemes ) {
|
||||
e.settingsManager.shell_theme = shell_theme;
|
||||
}
|
||||
else {
|
||||
const stylesheet = get_shell_theme_stylesheet(shell_theme);
|
||||
apply_shell_stylesheet(stylesheet);
|
||||
}
|
||||
}
|
||||
|
||||
_update_variants() {
|
||||
if ( !e.settingsManager.use_userthemes || e.settingsManager.manual_shell_variants || this._are_variants_up_to_date() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug('Updating Shell variants...');
|
||||
const variants = ShellVariants.guess_from(e.settingsManager.shell_theme);
|
||||
const installed_themes = get_installed_shell_themes();
|
||||
|
||||
if ( !installed_themes.has(variants.get('day')) || !installed_themes.has(variants.get('night')) ) {
|
||||
e.settingsManager.shell_variant_original = variants.get('original');
|
||||
const message = _('Unable to automatically detect the day and night variants for the "%s" GNOME Shell theme. Please manually choose them in the extension\'s preferences.').format(variants.get('original'));
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
e.settingsManager.shell_variant_day = variants.get('day');
|
||||
e.settingsManager.shell_variant_night = variants.get('night');
|
||||
e.settingsManager.shell_variant_original = variants.get('original');
|
||||
log_debug(`New Shell variants. { day: '${variants.get('day')}'; night: '${variants.get('night')}' }`);
|
||||
}
|
||||
|
||||
_reset_theme() {
|
||||
// We don't reset the theme when locking the session to prevent
|
||||
// flicker on unlocking
|
||||
if ( !main.screenShield.locked ) {
|
||||
log_debug('Resetting to the user\'s original Shell theme...');
|
||||
this._set_variant('original');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Signals.addSignalMethods(ShellThemer.prototype);
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2019, 2020 Romain Vigier
|
||||
Copyright (C) 2020 Matti Hyttinen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* The magic of guessing theme variants happens here.
|
||||
*
|
||||
* If the theme doesn't fit a particular case, we'll do the following:
|
||||
* - Remove any signs of a dark variant to the theme name to get the day
|
||||
* variant
|
||||
* - Remove any signs of a light variant to the day variant and add '-dark' to
|
||||
* get the night variant
|
||||
*
|
||||
* For themes that don't work with the general rule, a particular case must be
|
||||
* written. Day and night variants should be guessed with the most generic light
|
||||
* and dark variants the theme offer, except if the user explicitly chose a
|
||||
* specific variant.
|
||||
*
|
||||
* Light variants, from the most to the least generic:
|
||||
* - ''
|
||||
* - '-light'
|
||||
* - '-darker'
|
||||
*
|
||||
* Dark variants, from the most the least generic:
|
||||
* - '-dark'
|
||||
* - '-darkest'
|
||||
*/
|
||||
var ShellVariants = class {
|
||||
|
||||
static guess_from(name) {
|
||||
const variants = new Map();
|
||||
variants.set('original', name);
|
||||
|
||||
if ( name.includes('Adapta') ) {
|
||||
variants.set('day', name.replace('-Nokto', ''));
|
||||
variants.set('night', variants.get('day').replace('Adapta', 'Adapta-Nokto'));
|
||||
}
|
||||
else if ( name.includes('Arc') ) {
|
||||
variants.set('day', name.replace('-Dark', ''));
|
||||
variants.set('night', variants.get('day').replace('Arc', 'Arc-Dark'));
|
||||
}
|
||||
else if ( name.match(/^(Canta|ChromeOS|Materia|Orchis).*-compact/) ) {
|
||||
variants.set('day', name.replace('-dark', ''));
|
||||
variants.set('night', variants.get('day').replace(/(-light)?-compact/, '-dark-compact'));
|
||||
}
|
||||
else if ( name.includes('Flat-Remix') ) {
|
||||
const color = ( name.split('-')[2] && !['Dark', 'Darkest', 'fullPanel'].includes(name.split('-')[2]) ) ? '-' + name.split('-')[2] : '';
|
||||
const dark_variant = name.includes('Darkest') ? '-Darkest' : '-Dark';
|
||||
const size = name.includes('fullPanel')? '-fullPanel' : '';
|
||||
variants.set('day', name.replace(/-Dark(est)?/, ''));
|
||||
variants.set('night', `Flat-Remix${color}${dark_variant}${size}`);
|
||||
}
|
||||
else if ( name.match(/^(Layan|Matcha)/) ) {
|
||||
const basename = name.split('-')[0];
|
||||
variants.set('day', name.replace('-dark', ''));
|
||||
variants.set('night', variants.get('day').replace(new RegExp(`${basename}(-light)?`), `${basename}-dark`));
|
||||
}
|
||||
else if ( name.includes('Mojave') ) {
|
||||
variants.set('day', name.replace('-dark', '-light'));
|
||||
variants.set('night', variants.get('day').replace('-light', '-dark'));
|
||||
}
|
||||
else if ( name.includes('Plata') ) {
|
||||
variants.set('day', name.replace('-Noir', ''));
|
||||
variants.set('night', variants.get('day').replace(/Plata(-Lumine)?/, 'Plata-Noir'));
|
||||
}
|
||||
else if ( name.includes('Simply_Circles') ) {
|
||||
variants.set('day', name.replace('_Dark', '_Light'));
|
||||
variants.set('night', name.replace('_Light', '_Dark'));
|
||||
}
|
||||
else if ( name.includes('Teja') ) {
|
||||
const dark_variant = '_' + (name.replace('_Light').split('_')[1] || 'Dark');
|
||||
variants.set('day', name.replace(/(_Dark(est)?|_Black)/, ''));
|
||||
variants.set('night', variants.get('day').replace('_Light', '') + dark_variant);
|
||||
}
|
||||
else if ( name.includes('vimix') ) {
|
||||
variants.set('day', name.replace('-dark', ''));
|
||||
variants.set('night', variants.get('day').replace(/vimix(-light)?/, 'vimix-dark'));
|
||||
}
|
||||
else {
|
||||
variants.set('day', name.replace(/-dark(?!er)(est)?/, ''));
|
||||
variants.set('night', variants.get('day').replace(/(-light|-darker)/, '') + (name.includes('-darkest') ? '-darkest' : '-dark'));
|
||||
}
|
||||
|
||||
return variants;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { extensionUtils } = imports.misc;
|
||||
const { main } = imports.ui;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
const config = Me.imports.config;
|
||||
|
||||
const { log_debug, log_error } = Me.imports.utils;
|
||||
|
||||
const { Themer } = Me.imports.modules.Themer;
|
||||
const { Timer } = Me.imports.modules.Timer;
|
||||
|
||||
|
||||
/*
|
||||
The Switcher is the orchestrator of the extension.
|
||||
|
||||
When the extension is enabled, it asks the Themer to listen to theme changes
|
||||
from the user and the Timer to listen to changes in the current time:
|
||||
- On theme change, it asks the Themer to guess the new day and night
|
||||
variants for that theme, and to apply the relevant variant depending on the
|
||||
time of the day.
|
||||
- On time of the day change, it asks the Themer to apply the relevant
|
||||
variant.
|
||||
|
||||
When the extension is disabled, it asks the Themer and the Timer to disable
|
||||
themselves.
|
||||
*/
|
||||
|
||||
var Switcher = class {
|
||||
|
||||
constructor() {
|
||||
log_debug('Initializing extension...');
|
||||
extensionUtils.initTranslations(Me.metadata.uuid);
|
||||
log_debug('Extension initialized.');
|
||||
}
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling extension...');
|
||||
try {
|
||||
this.theme = new Themer();
|
||||
this.theme.enable();
|
||||
this.theme.subscribe(this._on_theme_changed.bind(this));
|
||||
|
||||
this.time = new Timer();
|
||||
this.time.enable();
|
||||
this.time.subscribe(this._on_time_changed.bind(this));
|
||||
|
||||
this.theme.set_variant(this.time.current);
|
||||
|
||||
log_debug('Extension enabled.');
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling extension...');
|
||||
try {
|
||||
this.theme.disable();
|
||||
this.time.disable();
|
||||
}
|
||||
catch(e) {} // Since we're disabling, we'll just ignore errors.
|
||||
finally {
|
||||
this.theme = null;
|
||||
this.time = null;
|
||||
}
|
||||
log_debug('Extension disabled.');
|
||||
}
|
||||
|
||||
_on_theme_changed() {
|
||||
if ( !this.theme || !this.time ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.theme.update_variants();
|
||||
this.theme.set_variant(this.time.current);
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
_on_time_changed() {
|
||||
if ( !this.theme || !this.time ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.theme.set_variant(this.time.current);
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { extensionUtils } = imports.misc;
|
||||
const { Gio } = imports.gi;
|
||||
const { main } = imports.ui;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
const config = Me.imports.config;
|
||||
const utils = Me.imports.utils;
|
||||
|
||||
const { log_debug, log_error } = Me.imports.utils;
|
||||
|
||||
const { Variants } = Me.imports.modules.Variants;
|
||||
|
||||
const Gettext = imports.gettext.domain(Me.metadata.uuid);
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
|
||||
/*
|
||||
The Themer communicates with the system to get the current theme or set a new
|
||||
one, and get the day and night variants of a theme. It listens to theme changes
|
||||
and reports them.
|
||||
*/
|
||||
|
||||
var Themer = class {
|
||||
|
||||
constructor() {
|
||||
log_debug('Initializing Themer...');
|
||||
this.settings = extensionUtils.getSettings();
|
||||
this.theme_gsettings = new Gio.Settings({ schema: config.THEME_GSETTINGS_SCHEMA });
|
||||
log_debug('Themer initialized.');
|
||||
}
|
||||
|
||||
enable() {
|
||||
try {
|
||||
log_debug('Enabling Themer...');
|
||||
this.ready = false;
|
||||
this._listen_to_force_manual_status();
|
||||
if ( this._is_force_manual_enabled() ) {
|
||||
log_debug('Using manually set variants.');
|
||||
this._listen_to_prefs_theme_change();
|
||||
}
|
||||
else {
|
||||
log_debug('Automatically detecting variants.');
|
||||
this._listen_to_theme_changes();
|
||||
this.update_variants();
|
||||
}
|
||||
this.ready = true;
|
||||
this.emit();
|
||||
log_debug('Themer enabled.');
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Themer...');
|
||||
this._stop_listening_to_force_manual_status();
|
||||
if ( this._is_force_manual_enabled() ) {
|
||||
this._stop_listening_to_prefs_theme_change();
|
||||
}
|
||||
else {
|
||||
this._stop_listening_to_theme_changes();
|
||||
// GNOME Shell disables extensions when locking the screen. We'll
|
||||
// only reset the theme if the user disables the extension to
|
||||
// prevent flickering when unlocking.
|
||||
if ( !main.screenShield.locked ) {
|
||||
this.reset_theme();
|
||||
}
|
||||
}
|
||||
log_debug('Themer disabled.');
|
||||
}
|
||||
|
||||
get current_theme() {
|
||||
return this.theme_gsettings.get_string(config.THEME_GSETTINGS_PROPERTY);
|
||||
}
|
||||
|
||||
set current_theme(theme) {
|
||||
if ( theme !== this.current_theme ) {
|
||||
this.theme_gsettings.set_string(config.THEME_GSETTINGS_PROPERTY, theme);
|
||||
log_debug(`Theme has been set to "${theme}".`);
|
||||
}
|
||||
}
|
||||
|
||||
subscribe(callback) {
|
||||
this.theme_change_callback = callback;
|
||||
}
|
||||
|
||||
emit() {
|
||||
if ( this.theme_change_callback ) {
|
||||
this.theme_change_callback();
|
||||
}
|
||||
}
|
||||
|
||||
set_variant(variant) {
|
||||
if ( this.ready ) {
|
||||
log_debug(`Setting theme to the "${variant}" variant...`);
|
||||
this.current_theme = this.settings.get_string(`theme-${variant}`);
|
||||
}
|
||||
}
|
||||
|
||||
reset_theme() {
|
||||
this.set_variant('original');
|
||||
log_debug('Theme has been reset to the user\'s original variant.')
|
||||
}
|
||||
|
||||
update_variants() {
|
||||
if ( !this._are_variants_up_to_date() && !this._is_force_manual_enabled() ) {
|
||||
this._update_variants();
|
||||
}
|
||||
}
|
||||
|
||||
_update_variants() {
|
||||
if ( this.current_theme ) {
|
||||
const variants = Variants.guess_from(this.current_theme);
|
||||
if ( utils.is_theme_installed(variants.get('day')) && utils.is_theme_installed(variants.get('night')) ) {
|
||||
variants.forEach( (theme, variant) => this.settings.set_string(`theme-${variant}`, theme) );
|
||||
log_debug(`Variants updated: {day: "${variants.get('day')}", night: "${variants.get('night')}"}`);
|
||||
}
|
||||
else {
|
||||
const message = _('The extension cannot detect the day and night variants for the "%s" theme. Please choose another theme or manually set variants in the extension preferences.').format(variants.get('original'));
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_are_variants_up_to_date() {
|
||||
return ( this.current_theme === this.settings.get_string('theme-day') || this.current_theme === this.settings.get_string('theme-night') );
|
||||
}
|
||||
|
||||
_is_force_manual_enabled() {
|
||||
return this.settings.get_boolean('theme-force-manual');
|
||||
}
|
||||
|
||||
_listen_to_force_manual_status() {
|
||||
if ( !this.force_manual_status_connect ) {
|
||||
this.force_manual_status_connect = this.settings.connect(
|
||||
'changed::theme-force-manual',
|
||||
this._on_force_manual_status_changed.bind(this)
|
||||
);
|
||||
log_debug('Listening to manual variants status changes...');
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_force_manual_status() {
|
||||
if ( this.settings && this.force_manual_status_connect ) {
|
||||
this.settings.disconnect(this.force_manual_status_connect);
|
||||
this.force_manual_status_connect = null;
|
||||
log_debug('Stopped listening to manual variants status changes.');
|
||||
}
|
||||
}
|
||||
|
||||
_on_force_manual_status_changed() {
|
||||
log_debug('Manual variants status has changed.');
|
||||
this.enable();
|
||||
}
|
||||
|
||||
_listen_to_theme_changes() {
|
||||
if ( !this.theme_change_connect ) {
|
||||
this.theme_change_connect = this.theme_gsettings.connect(
|
||||
'changed::' + config.THEME_GSETTINGS_PROPERTY,
|
||||
this._on_theme_changed.bind(this)
|
||||
);
|
||||
log_debug('Listening for theme changes...');
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_theme_changes() {
|
||||
if ( this.theme_gsettings && this.theme_change_connect ){
|
||||
this.theme_gsettings.disconnect(this.theme_change_connect);
|
||||
this.theme_change_connect = null;
|
||||
log_debug('Stopped listening for theme changes.');
|
||||
}
|
||||
}
|
||||
|
||||
_on_theme_changed() {
|
||||
log_debug(`Theme has changed to "${this.current_theme}".`);
|
||||
this.ready ? this.emit() : this.enable();
|
||||
}
|
||||
|
||||
_listen_to_prefs_theme_change() {
|
||||
if ( !this.prefs_theme_change_connect ) {
|
||||
this.prefs_theme_change_connect = new Map();
|
||||
['day', 'night'].forEach(time => {
|
||||
this.prefs_theme_change_connect.set(time, this.settings.connect(
|
||||
`changed::theme-${time}`,
|
||||
this._on_theme_changed.bind(this)
|
||||
));
|
||||
log_debug(`Listening for ${time} theme preference changes...`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_prefs_theme_change() {
|
||||
if ( this.settings && this.prefs_theme_change_connect ) {
|
||||
this.prefs_theme_change_connect.forEach(connect => this.settings.disconnect(connect));
|
||||
this.prefs_theme_change_connect = null;
|
||||
log_debug('Stopped listening to theme preferences changes.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+133
-486
@@ -16,527 +16,174 @@ You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const MainLoop = imports.mainloop;
|
||||
const { Gio, GLib } = imports.gi;
|
||||
const { extensionUtils, fileUtils } = imports.misc;
|
||||
const { extensionUtils } = imports.misc;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
const config = Me.imports.config;
|
||||
|
||||
const { log_debug, log_error } = Me.imports.utils;
|
||||
|
||||
const Gettext = imports.gettext.domain(Me.metadata.uuid);
|
||||
const _ = Gettext.gettext;
|
||||
const e = Me.imports.extension;
|
||||
const { log_debug } = Me.imports.utils;
|
||||
const { TimerNightlight } = Me.imports.modules.TimerNightlight;
|
||||
const { TimerLocation } = Me.imports.modules.TimerLocation;
|
||||
const { TimerSchedule } = Me.imports.modules.TimerSchedule;
|
||||
|
||||
|
||||
/*
|
||||
The Timer checks for changes in the time of day and reports them.
|
||||
|
||||
I can either use Night Light or Location Services as a source.
|
||||
|
||||
As Night Light or Location Services are essential for the extension to work,
|
||||
it continuously checks if one of them is enabled and warns the user if that's
|
||||
not the case. To not overwhelm the user with notifications, it only warns once
|
||||
and then only listens to Night Light or Location Services changes to reactivate
|
||||
itself automatically.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Timer is responsible for signaling any time change to the other modules.
|
||||
*
|
||||
* They can connect to its 'time-changed' signal and ask its 'time' property
|
||||
* for the current time.
|
||||
*
|
||||
* It will try to use one of this three different time sources, in this order of
|
||||
* preference:
|
||||
* - Night Light
|
||||
* - Location Services
|
||||
* - Manual schedule
|
||||
*
|
||||
* The user can manually force a specific time source and set the manual
|
||||
* schedule in the extensions's preferences.
|
||||
*/
|
||||
var Timer = class {
|
||||
|
||||
constructor() {
|
||||
log_debug('Initializing Timer...');
|
||||
this.settings = extensionUtils.getSettings();
|
||||
this.nightlight_gsettings = new Gio.Settings({ schema: config.NIGHTLIGHT_GSETTINGS_SCHEMA });
|
||||
this.location_gsettings = new Gio.Settings({ schema: config.LOCATION_GSETTINGS_SCHEMA });
|
||||
log_debug('Timer initialized.');
|
||||
this._source = null;
|
||||
this._previous_time = null;
|
||||
}
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling Timer...');
|
||||
try {
|
||||
this.ready = false;
|
||||
this.settings.set_string('time-source', this._get_source());
|
||||
this._listen_to_nightlight_status();
|
||||
this._listen_to_location_status();
|
||||
this._listen_to_force_manual_status();
|
||||
switch (this.settings.get_string('time-source')) {
|
||||
case 'nightlight':
|
||||
log_debug('Using Night Light as a source.');
|
||||
this._connect_to_color_dbus_proxy();
|
||||
this._listen_to_nightlight_changes();
|
||||
break;
|
||||
case 'location':
|
||||
log_debug('Using Location Services as a source.');
|
||||
this._connect_to_geoclue_dbus_proxy();
|
||||
this._listen_to_location_changes();
|
||||
this._connect_to_geoclue_location_dbus_proxy();
|
||||
this._update_location();
|
||||
this._update_location_suntimes();
|
||||
this._watch_for_location_time_change();
|
||||
this._regularly_update_location_suntimes();
|
||||
break;
|
||||
case 'manual':
|
||||
log_debug('Using manual schedule as a source.');
|
||||
this._watch_for_location_time_change();
|
||||
}
|
||||
this.ready = true;
|
||||
this.emit();
|
||||
log_debug('Timer enabled.');
|
||||
}
|
||||
catch(e) {
|
||||
log_error(e);
|
||||
}
|
||||
this._connect_settings();
|
||||
this._create_source();
|
||||
this._connect_source();
|
||||
this._enable_source();
|
||||
log_debug('Timer enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Timer...');
|
||||
this._stop_listening_to_nightlight_status();
|
||||
this._stop_listening_to_location_status();
|
||||
this._stop_listening_to_force_manual_status();
|
||||
switch (this.settings.get_string('time-source')) {
|
||||
case 'nightlight':
|
||||
this._stop_listening_to_nightlight_changes();
|
||||
this._disconnect_from_color_dbus_proxy();
|
||||
break;
|
||||
case 'location':
|
||||
this._stop_regularly_updating_location_suntimes();
|
||||
this._stop_watching_for_location_time_change();
|
||||
this._stop_listening_to_location_changes();
|
||||
this._disconnect_from_geoclue_location_dbus_proxy();
|
||||
this._disconnect_from_geoclue_dbus_proxy();
|
||||
break;
|
||||
}
|
||||
this._disconnect_source();
|
||||
this._disable_source();
|
||||
this._disconnect_settings();
|
||||
log_debug('Timer disabled.');
|
||||
}
|
||||
|
||||
get current() {
|
||||
if ( this.ready ) {
|
||||
switch (this.settings.get_string('time-source')) {
|
||||
case 'nightlight':
|
||||
return this._is_nightlight_active() ? 'night' : 'day';
|
||||
case 'location':
|
||||
case 'manual':
|
||||
return this._is_location_daytime() ? 'day' : 'night';
|
||||
}
|
||||
|
||||
get time() {
|
||||
return this._source.time;
|
||||
}
|
||||
|
||||
|
||||
_connect_settings() {
|
||||
log_debug('Connecting Timer to settings...');
|
||||
this._nightlight_status_changed_connect = e.settingsManager.connect('nightlight-status-changed', this._on_source_changed.bind(this));
|
||||
this._location_status_changed_connect = e.settingsManager.connect('location-status-changed', this._on_source_changed.bind(this));
|
||||
this._manual_time_source_changed_connect = e.settingsManager.connect('manual-time-source-changed', this._on_source_changed.bind(this));
|
||||
this._time_source_changed_connect = e.settingsManager.connect('time-source-changed', this._on_time_source_changed.bind(this));
|
||||
}
|
||||
|
||||
_disconnect_settings() {
|
||||
if ( this._nightlight_status_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._nightlight_status_changed_connect);
|
||||
this._nightlight_status_changed_connect = null;
|
||||
}
|
||||
else {
|
||||
return 'day';
|
||||
if ( this._location_status_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._location_status_changed_connect);
|
||||
this._location_status_changed_connect = null;
|
||||
}
|
||||
if ( this._manual_time_source_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._manual_time_source_changed_connect);
|
||||
this._manual_time_source_changed_connect = null;
|
||||
}
|
||||
if ( this._time_source_changed_connect ) {
|
||||
e.settingsManager.disconnect(this._time_source_changed_connect);
|
||||
this._time_source_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnected Timer from settings.');
|
||||
}
|
||||
|
||||
_create_source() {
|
||||
switch ( this._get_source() ) {
|
||||
case 'nightlight':
|
||||
this._source = new TimerNightlight();
|
||||
break;
|
||||
case 'location':
|
||||
this._source = new TimerLocation();
|
||||
break;
|
||||
case 'schedule':
|
||||
this._source = new TimerSchedule();
|
||||
break;
|
||||
}
|
||||
this._source.enable();
|
||||
}
|
||||
|
||||
_enable_source() {
|
||||
this._source.enable();
|
||||
}
|
||||
|
||||
_disable_source() {
|
||||
if ( this._source ) {
|
||||
this._source.disable();
|
||||
this._source = null;
|
||||
}
|
||||
}
|
||||
|
||||
subscribe(callback) {
|
||||
this.time_change_callback = callback;
|
||||
_connect_source() {
|
||||
log_debug('Connecting to time source...');
|
||||
this._time_changed_connect = this._source.connect('time-changed', this._on_time_changed.bind(this));
|
||||
}
|
||||
|
||||
emit() {
|
||||
if ( this.time_change_callback ) {
|
||||
this.time_change_callback();
|
||||
_disconnect_source() {
|
||||
if ( this._time_changed_connect ) {
|
||||
this._source.disconnect(this._time_changed_connect);
|
||||
this._time_changed_connect = null;
|
||||
}
|
||||
log_debug('Disconnected from time source.');
|
||||
}
|
||||
|
||||
|
||||
_on_source_changed() {
|
||||
this.disable();
|
||||
this.enable();
|
||||
}
|
||||
|
||||
_on_time_source_changed(settings, new_source) {
|
||||
if ( e.settingsManager.manual_time_source ) {
|
||||
this._on_source_changed();
|
||||
}
|
||||
}
|
||||
|
||||
_on_time_changed(source, new_time) {
|
||||
if ( new_time !== this._previous_time) {
|
||||
log_debug(`Time has changed to ${new_time}.`);
|
||||
this.emit('time-changed', new_time);
|
||||
this._previous_time = new_time;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_get_source() {
|
||||
if ( this._is_force_manual_enabled() ) {
|
||||
return 'manual';
|
||||
log_debug('Getting time source...');
|
||||
|
||||
if ( e.settingsManager.manual_time_source ) {
|
||||
log_debug(`Time source is forced to ${e.settingsManager.time_source}.`);
|
||||
return e.settingsManager.time_source;
|
||||
}
|
||||
else if ( this._is_nightlight_enabled() ) {
|
||||
return 'nightlight';
|
||||
|
||||
let source;
|
||||
if ( e.settingsManager.nightlight_enabled ) {
|
||||
source = 'nightlight';
|
||||
}
|
||||
else if ( this._is_location_enabled() ) {
|
||||
return 'location';
|
||||
else if ( e.settingsManager.location_enabled ) {
|
||||
source = 'location';
|
||||
}
|
||||
else {
|
||||
return 'manual';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if Night Light is enabled and restart the Timer on changes.
|
||||
*/
|
||||
|
||||
_is_nightlight_enabled() {
|
||||
return this.nightlight_gsettings.get_boolean(config.NIGHTLIGHT_GSETTINGS_PROPERTY);
|
||||
}
|
||||
|
||||
_listen_to_nightlight_status() {
|
||||
if ( !this.nightlight_status_connect ) {
|
||||
this.nightlight_status_connect = this.nightlight_gsettings.connect(
|
||||
'changed::' + config.NIGHTLIGHT_GSETTINGS_PROPERTY,
|
||||
this._on_nightlight_status_changed.bind(this)
|
||||
);
|
||||
log_debug('Listening to Night Light status changes...');
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_nightlight_status() {
|
||||
if ( this.nightlight_gsettings && this.nightlight_status_connect ) {
|
||||
this.nightlight_gsettings.disconnect(this.nightlight_status_connect);
|
||||
this.nightlight_status_connect = null;
|
||||
log_debug('Stopped listening to Night Light status changes.');
|
||||
}
|
||||
}
|
||||
|
||||
_on_nightlight_status_changed() {
|
||||
log_debug('Night Light status has changed.');
|
||||
this.enable();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check if Location Services are enabled and restart the Timer on changes.
|
||||
*/
|
||||
|
||||
_is_location_enabled() {
|
||||
return this.location_gsettings.get_boolean(config.LOCATION_GSETTINGS_PROPERTY);
|
||||
}
|
||||
|
||||
_listen_to_location_status() {
|
||||
if ( !this.location_status_connect ) {
|
||||
this.location_status_connect = this.location_gsettings.connect(
|
||||
'changed::' + config.LOCATION_GSETTINGS_PROPERTY,
|
||||
this._on_location_status_changed.bind(this)
|
||||
);
|
||||
log_debug('Listening to Location Services status changes...');
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_location_status() {
|
||||
if ( this.location_gsettings && this.location_status_connect ) {
|
||||
this.location_gsettings.disconnect(this.location_status_connect);
|
||||
this.location_status_connect = null;
|
||||
log_debug('Stopped listening to Location Services status changes.');
|
||||
}
|
||||
}
|
||||
|
||||
_on_location_status_changed() {
|
||||
log_debug('Location Services status has changed.');
|
||||
this.enable();
|
||||
}
|
||||
|
||||
/*
|
||||
Check if the user wants to force a manual schedule and restart the Timer on
|
||||
changes.
|
||||
*/
|
||||
|
||||
_is_force_manual_enabled() {
|
||||
return this.settings.get_boolean('time-force-manual');
|
||||
}
|
||||
|
||||
_listen_to_force_manual_status() {
|
||||
if ( !this.force_manual_status_connect ) {
|
||||
this.force_manual_status_connect = this.settings.connect(
|
||||
'changed::time-force-manual',
|
||||
this._on_force_manual_status_changed.bind(this)
|
||||
);
|
||||
log_debug('Listening to manual schedule status changes...');
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_force_manual_status() {
|
||||
if ( this.settings && this.force_manual_status_connect ) {
|
||||
this.settings.disconnect(this.force_manual_status_connect);
|
||||
this.force_manual_status_connect = null;
|
||||
log_debug('Stopped listening to manual schedule status changes.');
|
||||
}
|
||||
}
|
||||
|
||||
_on_force_manual_status_changed() {
|
||||
log_debug('Manual schedule status has changed.');
|
||||
this.enable();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Use Night Light as a time source.
|
||||
*/
|
||||
|
||||
_connect_to_color_dbus_proxy() {
|
||||
try {
|
||||
log_debug('Connecting to Color DBus proxy...');
|
||||
const color_interface = fileUtils.loadInterfaceXML('org.gnome.SettingsDaemon.Color');
|
||||
const ColorProxy = Gio.DBusProxy.makeProxyWrapper(color_interface);
|
||||
this.color_dbus_proxy = new ColorProxy(
|
||||
Gio.DBus.session,
|
||||
'org.gnome.SettingsDaemon.Color',
|
||||
'/org/gnome/SettingsDaemon/Color'
|
||||
);
|
||||
log_debug('Connected to Color DBus proxy.');
|
||||
}
|
||||
catch(e) {
|
||||
const message = _('Unable to connect to Color DBus proxy.');
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
_disconnect_from_color_dbus_proxy() {
|
||||
if ( this.color_dbus_proxy ) {
|
||||
log_debug('Disconnecting from Color DBus Proxy...');
|
||||
this.color_dbus_proxy = null;
|
||||
log_debug('Disconnected from Color DBus Proxy.');
|
||||
}
|
||||
}
|
||||
|
||||
_listen_to_nightlight_changes() {
|
||||
if ( !this.nightlight_changes_connect ) {
|
||||
this.nightlight_changes_connect = this.color_dbus_proxy.connect(
|
||||
'g-properties-changed',
|
||||
this._on_nightlight_changed.bind(this)
|
||||
);
|
||||
log_debug('Listening to Night Light changes...');
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_nightlight_changes() {
|
||||
if ( this.color_dbus_proxy && this.nightlight_changes_connect ) {
|
||||
this.color_dbus_proxy.disconnect(this.nightlight_changes_connect);
|
||||
this.nightlight_changes_connect = null;
|
||||
log_debug('Stopped listening to Night Light changes.');
|
||||
}
|
||||
}
|
||||
|
||||
_on_nightlight_changed(sender, dbus_properties) {
|
||||
const properties = dbus_properties.deep_unpack();
|
||||
if ( properties.NightLightActive ) {
|
||||
log_debug('Night Light has become ' + (properties.NightLightActive.unpack() ? '' : 'in') + 'active.');
|
||||
this.emit();
|
||||
}
|
||||
}
|
||||
|
||||
_is_nightlight_active() {
|
||||
if ( this.color_dbus_proxy ) {
|
||||
return this.color_dbus_proxy.NightLightActive;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Use location as a time source.
|
||||
*/
|
||||
|
||||
_connect_to_geoclue_dbus_proxy() {
|
||||
try {
|
||||
log_debug('Connecting to GeoClue Manager DBus proxy...');
|
||||
const GeoClueManagerProxy = Gio.DBusProxy.makeProxyWrapper(config.GEOCLUE_MANAGER_INTERFACE);
|
||||
this.geoclue_manager_dbus_proxy = new GeoClueManagerProxy(
|
||||
Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
'/org/freedesktop/GeoClue2/Manager'
|
||||
);
|
||||
log_debug('Connected to GeoClue Manager DBus proxy.');
|
||||
}
|
||||
catch(e) {
|
||||
const message = _('Unable to connect to GeoClue Manager DBus proxy.');
|
||||
throw new Error(message);
|
||||
source = 'schedule';
|
||||
}
|
||||
|
||||
try {
|
||||
log_debug('Requesting new GeoClue Client...');
|
||||
this.geoclue_client = this.geoclue_manager_dbus_proxy.GetClientSync()[0];
|
||||
log_debug(`Got a GeoClue Client at ${this.geoclue_client}`);
|
||||
}
|
||||
catch(e) {
|
||||
const message = _('Unable to get a GeoClue Client.');
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
try {
|
||||
log_debug('Connecting to GeoClue Client DBus proxy...');
|
||||
const GeoClueClientProxy = Gio.DBusProxy.makeProxyWrapper(config.GEOCLUE_CLIENT_INTERFACE);
|
||||
this.geoclue_client_dbus_proxy = new GeoClueClientProxy(
|
||||
Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
this.geoclue_client
|
||||
);
|
||||
this.geoclue_client_dbus_proxy.DesktopId = Me.metadata.uuid;
|
||||
this.geoclue_client_dbus_proxy.DistanceThreshold = 10000;
|
||||
this.geoclue_client_dbus_proxy.RequestedAccuracyLevel = 4;
|
||||
log_debug('Connected to GeoClue Client DBus proxy.');
|
||||
}
|
||||
catch(e) {
|
||||
const message = _('Unable to connect to GeoClue Client DBus proxy.');
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
_disconnect_from_geoclue_dbus_proxy() {
|
||||
if ( this.geoclue_client_dbus_proxy ) {
|
||||
log_debug('Disconnecting from GeoClue Client DBus proxy...');
|
||||
if ( this.geoclue_manager_dbus_proxy && this.geoclue_client ) {
|
||||
this.geoclue_manager_dbus_proxy.DeleteClientSync(this.geoclue_client);
|
||||
this.geoclue_client = null;
|
||||
}
|
||||
this.geoclue_client_dbus_proxy = null;
|
||||
log_debug('Disconnected from GeoClue Client DBus Proxy.');
|
||||
}
|
||||
|
||||
if ( this.geoclue_manager_dbus_proxy ) {
|
||||
log_debug('Disconnecting from GeoClue Manager DBus proxy...');
|
||||
this.geoclue_manager_dbus_proxy = null;
|
||||
log_debug('Disconnected from GeoClue Manager DBus proxy.');
|
||||
}
|
||||
}
|
||||
|
||||
_listen_to_location_changes() {
|
||||
if ( !this.location_changes_connect ) {
|
||||
this.location_changes_connect = this.geoclue_client_dbus_proxy.connectSignal('LocationUpdated', this._on_location_changed.bind(this));
|
||||
this.geoclue_client_dbus_proxy.StartSync();
|
||||
log_debug('Listening to location changes...');
|
||||
}
|
||||
}
|
||||
|
||||
_stop_listening_to_location_changes() {
|
||||
if ( this.geoclue_client_dbus_proxy && this.location_changes_connect ) {
|
||||
this.geoclue_client_dbus_proxy.disconnectSignal(this.location_changes_connect);
|
||||
this.location_changes_connect = null;
|
||||
this.geoclue_client_dbus_proxy.StopSync();
|
||||
log_debug('Stopped listening to location changes.');
|
||||
}
|
||||
}
|
||||
|
||||
_on_location_changed(proxy, sender, [old_location_path, new_location_path]) {
|
||||
log_debug('Location has changed.');
|
||||
this._connect_to_geoclue_location_dbus_proxy(new_location_path);
|
||||
this._update_location();
|
||||
this._update_location_suntimes();
|
||||
}
|
||||
|
||||
_connect_to_geoclue_location_dbus_proxy(path) {
|
||||
if ( !path && this.geoclue_client_dbus_proxy ) {
|
||||
path = this.geoclue_client_dbus_proxy.Location;
|
||||
}
|
||||
if ( path !== '/' ) {
|
||||
try {
|
||||
log_debug('Connecting to GeoClue Location DBus proxy...');
|
||||
const GeoClueLocationProxy = Gio.DBusProxy.makeProxyWrapper(config.GEOCLUE_LOCATION_INTERFACE);
|
||||
this.geoclue_location_dbus_proxy = new GeoClueLocationProxy(
|
||||
Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
path
|
||||
);
|
||||
log_debug('Connected to GeoClue Location DBus proxy.');
|
||||
}
|
||||
catch(e) {
|
||||
const message = _('Unable to connect to GeoClue Location DBus proxy.');
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_disconnect_from_geoclue_location_dbus_proxy() {
|
||||
if ( this.geoclue_location_dbus_proxy ) {
|
||||
log_debug('Disconnecting from GeoClue Location DBus proxy...');
|
||||
this.geoclue_location_dbus_proxy = null;
|
||||
log_debug('Disconnected from GeoClue Location DBus proxy.')
|
||||
}
|
||||
}
|
||||
|
||||
_update_location() {
|
||||
if ( this.geoclue_location_dbus_proxy ) {
|
||||
try {
|
||||
log_debug('Updating location...');
|
||||
const latitude = this.geoclue_location_dbus_proxy.Latitude;
|
||||
const longitude = this.geoclue_location_dbus_proxy.Longitude;
|
||||
|
||||
this.location = new Map([
|
||||
['latitude', latitude],
|
||||
['longitude', longitude]
|
||||
]);
|
||||
log_debug(`Current location: (${latitude};${longitude})`);
|
||||
}
|
||||
catch(e) {
|
||||
const message = _('Unable to get current location.');
|
||||
throw new Error(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_update_location_suntimes() {
|
||||
if ( !this.location ) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug('Updating sun times...');
|
||||
|
||||
Math.rad = degrees => degrees * Math.PI / 180;
|
||||
Math.deg = radians => radians * 180 / Math.PI;
|
||||
|
||||
// Calculations from https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
|
||||
const latitude = this.location.get('latitude');
|
||||
const longitude = this.location.get('longitude');
|
||||
|
||||
const dt_now = GLib.DateTime.new_now_local();
|
||||
const dt_zero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
|
||||
|
||||
const time_span = dt_now.difference(dt_zero);
|
||||
|
||||
const date = time_span / 1000 / 1000 / 60 / 60 / 24 + 2;
|
||||
const tz_offset = dt_now.get_utc_offset() / 1000 / 1000 / 60 / 60;
|
||||
const time_past_local_midnight = 0;
|
||||
|
||||
const julian_day = date + 2415018.5 + time_past_local_midnight - tz_offset / 24;
|
||||
const julian_century = (julian_day - 2451545) / 36525;
|
||||
const geom_mean_long_sun = (280.46646 + julian_century * (36000.76983 + julian_century * 0.0003032)) % 360;
|
||||
const geom_mean_anom_sun = 357.52911 + julian_century * (35999.05029 - 0.0001537 * julian_century);
|
||||
const eccent_earth_orbit = 0.016708634 - julian_century * (0.000042037 + 0.0000001267 * julian_century);
|
||||
const sun_eq_of_ctr = Math.sin(Math.rad(geom_mean_anom_sun)) * (1.914602 - julian_century * (0.004817 + 0.000014 * julian_century)) + Math.sin(Math.rad(2 * geom_mean_anom_sun)) * (0.019993 - 0.000101 * julian_century) + Math.sin(Math.rad(3 * geom_mean_anom_sun)) * 0.000289;
|
||||
const sun_true_long = geom_mean_long_sun + sun_eq_of_ctr;
|
||||
const sun_app_long = sun_true_long - 0.00569 - 0.00478 * Math.sin(Math.rad(125.04 - 1934.136 * julian_century));
|
||||
const mean_obliq_ecliptic = 23 + (26 + ((21.448 - julian_century * (46.815 + julian_century * (0.00059 - julian_century * 0.001813)))) / 60) / 60;
|
||||
const obliq_corr = mean_obliq_ecliptic + 0.00256 * Math.cos(Math.rad(125.04 - 1934.136 * julian_century));
|
||||
const sun_declin = Math.deg(Math.asin(Math.sin(Math.rad(obliq_corr)) * Math.sin(Math.rad(sun_app_long))));
|
||||
const var_y = Math.tan(Math.rad(obliq_corr / 2)) * Math.tan(Math.rad(obliq_corr / 2));
|
||||
const eq_of_time = 4 * Math.deg(var_y * Math.sin(2 * Math.rad(geom_mean_long_sun)) - 2 * eccent_earth_orbit * Math.sin(Math.rad(geom_mean_anom_sun)) + 4 * eccent_earth_orbit * var_y * Math.sin(Math.rad(geom_mean_anom_sun)) * Math.cos(2 * Math.rad(geom_mean_long_sun)) - 0.5 * var_y * var_y * Math.sin(4 * Math.rad(geom_mean_long_sun)) - 1.25 * eccent_earth_orbit * eccent_earth_orbit * Math.sin(2 * Math.rad(geom_mean_anom_sun)));
|
||||
const ha_sunrise = Math.deg(Math.acos(Math.cos(Math.rad(90.833)) / (Math.cos(Math.rad(latitude)) * Math.cos(Math.rad(sun_declin))) - Math.tan(Math.rad(latitude)) * Math.tan(Math.rad(sun_declin))));
|
||||
const solar_noon = (720 - 4 * longitude - eq_of_time + tz_offset * 60) / 1440;
|
||||
|
||||
const sunrise_time = solar_noon - ha_sunrise * 4 / 1440;
|
||||
const sunset_time = solar_noon + ha_sunrise * 4 / 1440;
|
||||
|
||||
const sunrise = sunrise_time * 24;
|
||||
const sunset = sunset_time * 24;
|
||||
|
||||
this.settings.set_double('time-sunrise', sunrise);
|
||||
this.settings.set_double('time-sunset', sunset);
|
||||
log_debug(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
|
||||
}
|
||||
|
||||
_regularly_update_location_suntimes() {
|
||||
this.regularly_update_suntimes_timer = MainLoop.timeout_add_seconds(3600, () => {
|
||||
this._update_location_suntimes();
|
||||
return true; // Repeat the loop
|
||||
});
|
||||
}
|
||||
|
||||
_stop_regularly_updating_location_suntimes() {
|
||||
if ( this.regularly_update_suntimes_timer ) {
|
||||
MainLoop.source_remove(this.regularly_update_suntimes_timer);
|
||||
this.regularly_update_suntimes_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
_is_location_daytime() {
|
||||
const time = GLib.DateTime.new_now_local();
|
||||
const hour = time.get_hour() + time.get_minute() / 60 + time.get_second() / 3600;
|
||||
return ( hour >= this.settings.get_double('time-sunrise') && hour <= this.settings.get_double('time-sunset') );
|
||||
}
|
||||
|
||||
_watch_for_location_time_change() {
|
||||
this.location_time_change_timer = MainLoop.timeout_add_seconds(1, () => {
|
||||
const previous_time = this.location_daytime;
|
||||
if ( previous_time !== this._is_location_daytime() ) {
|
||||
log_debug('Time of the day has changed.');
|
||||
this.location_daytime = this._is_location_daytime();
|
||||
this.emit();
|
||||
}
|
||||
return true; // Repeat the loop
|
||||
}, null);
|
||||
}
|
||||
|
||||
_stop_watching_for_location_time_change() {
|
||||
if ( this.location_time_change_timer ) {
|
||||
MainLoop.source_remove(this.location_time_change_timer);
|
||||
this.location_time_change_timer = null;
|
||||
}
|
||||
log_debug(`Time source is ${source}.`);
|
||||
e.settingsManager.time_source = source;
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
Signals.addSignalMethods(Timer.prototype);
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { Gio, GLib } = imports.gi;
|
||||
const { extensionUtils } = imports.misc;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const e = Me.imports.extension;
|
||||
const { log_debug } = Me.imports.utils;
|
||||
|
||||
|
||||
// GeoClue2 interfaces from https://gitlab.freedesktop.org/geoclue/geoclue/
|
||||
const GEOCLUE_MANAGER_INTERFACE = `
|
||||
<node>
|
||||
<interface name="org.freedesktop.GeoClue2.Manager">
|
||||
<property name="InUse" type="b" access="read"/>
|
||||
<property name="AvailableAccuracyLevel" type="u" access="read"/>
|
||||
<method name="GetClient">
|
||||
<arg name="client" type="o" direction="out"/>
|
||||
</method>
|
||||
<method name="CreateClient">
|
||||
<arg name="client" type="o" direction="out"/>
|
||||
</method>
|
||||
<method name="DeleteClient">
|
||||
<arg name="client" type="o" direction="in"/>
|
||||
</method>
|
||||
<method name="AddAgent">
|
||||
<arg name="id" type="s" direction="in"/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>`;
|
||||
|
||||
const GEOCLUE_CLIENT_INTERFACE = `
|
||||
<node>
|
||||
<interface name="org.freedesktop.GeoClue2.Client">
|
||||
<property name="Location" type="o" access="read"/>
|
||||
<property name="DistanceThreshold" type="u" access="readwrite">
|
||||
<annotation name="org.freedesktop.Accounts.DefaultValue" value="0"/>
|
||||
</property>
|
||||
<property name="TimeThreshold" type="u" access="readwrite">
|
||||
<annotation name="org.freedesktop.Accounts.DefaultValue" value="0"/>
|
||||
</property>
|
||||
<property name="DesktopId" type="s" access="readwrite"/>
|
||||
<property name="RequestedAccuracyLevel" type="u" access="readwrite"/>
|
||||
<property name="Active" type="b" access="read"/>
|
||||
<method name="Start"/>
|
||||
<method name="Stop"/>
|
||||
<signal name="LocationUpdated">
|
||||
<arg name="old" type="o"/>
|
||||
<arg name="new" type="o"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>`;
|
||||
|
||||
const GEOCLUE_LOCATION_INTERFACE = `
|
||||
<node>
|
||||
<interface name="org.freedesktop.GeoClue2.Location">
|
||||
<property name="Latitude" type="d" access="read"/>
|
||||
<property name="Longitude" type="d" access="read"/>
|
||||
<property name="Accuracy" type="d" access="read"/>
|
||||
<property name="Altitude" type="d" access="read"/>
|
||||
<property name="Speed" type="d" access="read"/>
|
||||
<property name="Heading" type="d" access="read"/>
|
||||
<property name="Description" type="s" access="read"/>
|
||||
<property name="Timestamp" type="(tt)" access="read"/>
|
||||
</interface>
|
||||
</node>`;
|
||||
|
||||
|
||||
/**
|
||||
* The Location Timer uses Location Services to get the current sunrise and
|
||||
* sunset times.
|
||||
*
|
||||
* It gets the current user's location with the GeoClue2 DBus proxy and
|
||||
* calculate the times.
|
||||
*
|
||||
* It will recalculate every hour and when the user's location changed to stay
|
||||
* up to date.
|
||||
*
|
||||
* Every second, it will check if the time has changed and signal if that's the
|
||||
* case.
|
||||
*/
|
||||
var TimerLocation = class {
|
||||
|
||||
constructor() {
|
||||
this._previously_daytime = null;
|
||||
// Before we have the location suntimes, we'll use the manual schedule
|
||||
// times
|
||||
this._suntimes = new Map([
|
||||
['sunrise', e.settingsManager.schedule_sunrise],
|
||||
['sunset', e.settingsManager.schedule_sunrise]
|
||||
]);
|
||||
}
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling Location Timer...');
|
||||
this._connect_to_geoclue_dbus_proxy();
|
||||
this._listen_to_location_updates();
|
||||
this._connect_to_geoclue_location_dbus_proxy();
|
||||
this._update_location();
|
||||
this._update_suntimes();
|
||||
this._watch_for_time_change();
|
||||
this._regularly_update_suntimes();
|
||||
log_debug('Location Timer enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Location Timer...');
|
||||
this._stop_regularly_updating_suntimes();
|
||||
this._stop_watching_for_time_change();
|
||||
this._stop_listening_to_location_updates();
|
||||
this._disconnect_from_geoclue_location_dbus_proxy();
|
||||
this._disconnect_from_geoclue_dbus_proxy();
|
||||
log_debug('Location Timer disabled.');
|
||||
}
|
||||
|
||||
|
||||
get time() {
|
||||
return this._is_daytime() ? 'day' : 'night';
|
||||
}
|
||||
|
||||
_connect_to_geoclue_dbus_proxy() {
|
||||
log_debug('Connecting to GeoClue manager DBus proxy...');
|
||||
const GeoClueManagerProxy = Gio.DBusProxy.makeProxyWrapper(GEOCLUE_MANAGER_INTERFACE);
|
||||
this._geoclue_manager_dbus_proxy = new GeoClueManagerProxy(
|
||||
Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
'/org/freedesktop/GeoClue2/Manager'
|
||||
);
|
||||
log_debug('Connected to GeoClue manager DBus proxy.');
|
||||
|
||||
log_debug('Getting a GeoClue client...');
|
||||
this._geoclue_client = this._geoclue_manager_dbus_proxy.GetClientSync()[0];
|
||||
log_debug(`Got a GeoClue client at ${this._geoclue_client}`);
|
||||
|
||||
log_debug('Connecting to GeoClue client DBus proxy...');
|
||||
const GeoClueClientProxy = Gio.DBusProxy.makeProxyWrapper(GEOCLUE_CLIENT_INTERFACE);
|
||||
this._geoclue_client_dbus_proxy = new GeoClueClientProxy(
|
||||
Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
this._geoclue_client
|
||||
);
|
||||
this._geoclue_client_dbus_proxy.DesktopId = Me.metadata.uuid;
|
||||
this._geoclue_client_dbus_proxy.DistanceThreshold = 10000;
|
||||
this._geoclue_client_dbus_proxy.RequestedAccuracyLevel = 4;
|
||||
log_debug('Connected to GeoClue client DBus proxy.');
|
||||
}
|
||||
|
||||
_disconnect_from_geoclue_dbus_proxy() {
|
||||
log_debug('Disconnecting from GeoClue DBus proxy...')
|
||||
this._geoclue_manager_dbus_proxy.DeleteClientSync(this._geoclue_client);
|
||||
this._geoclue_client = null;
|
||||
this._geoclue_client_dbus_proxy = null;
|
||||
this._geoclue_manager_dbus_proxy = null;
|
||||
log_debug('Disconnected from GeoClue DBus proxy.')
|
||||
}
|
||||
|
||||
_listen_to_location_updates() {
|
||||
log_debug('Listening to location updates...');
|
||||
this._location_updates_connect = this._geoclue_client_dbus_proxy.connectSignal('LocationUpdated', this._on_location_updated.bind(this));
|
||||
this._geoclue_client_dbus_proxy.StartSync();
|
||||
}
|
||||
|
||||
_stop_listening_to_location_updates() {
|
||||
this._geoclue_client_dbus_proxy.disconnectSignal(this._location_updates_connect);
|
||||
this._location_updates_connect = null;
|
||||
this._geoclue_client_dbus_proxy.StopSync();
|
||||
log_debug('Stopped listening to location updates.');
|
||||
}
|
||||
|
||||
_on_location_updated(proxy, sender, [old_location_path, new_location_path]) {
|
||||
log_debug('Location has changed.');
|
||||
this._connect_to_geoclue_location_dbus_proxy(new_location_path);
|
||||
this._update_location();
|
||||
this._update_suntimes();
|
||||
}
|
||||
|
||||
_connect_to_geoclue_location_dbus_proxy(path) {
|
||||
if ( !path ) {
|
||||
path = this._geoclue_client_dbus_proxy.Location;
|
||||
}
|
||||
if ( path !== '/' ) {
|
||||
log_debug('Connecting to GeoClue location DBus proxy...');
|
||||
const GeoClueLocationProxy = Gio.DBusProxy.makeProxyWrapper(GEOCLUE_LOCATION_INTERFACE);
|
||||
this._geoclue_location_dbus_proxy = new GeoClueLocationProxy(
|
||||
Gio.DBus.system,
|
||||
'org.freedesktop.GeoClue2',
|
||||
path
|
||||
);
|
||||
log_debug('Connected to GeoClue location DBus proxy.');
|
||||
}
|
||||
}
|
||||
|
||||
_disconnect_from_geoclue_location_dbus_proxy() {
|
||||
log_debug('Disconnecting from GeoClue location DBus proxy...');
|
||||
this._geoclue_location_dbus_proxy = null;
|
||||
log_debug('Disconnected from GeoClue location DBus proxy.');
|
||||
}
|
||||
|
||||
_update_location() {
|
||||
if ( this._geoclue_location_dbus_proxy ) {
|
||||
log_debug('Updating location...');
|
||||
const latitude = this._geoclue_location_dbus_proxy.Latitude;
|
||||
const longitude = this._geoclue_location_dbus_proxy.Longitude;
|
||||
|
||||
this.location = new Map([
|
||||
['latitude', latitude],
|
||||
['longitude', longitude]
|
||||
]);
|
||||
log_debug(`Current location: (${latitude};${longitude})`);
|
||||
}
|
||||
}
|
||||
|
||||
_update_suntimes() {
|
||||
if ( !this.location ) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug('Updating sun times...');
|
||||
|
||||
Math.rad = degrees => degrees * Math.PI / 180;
|
||||
Math.deg = radians => radians * 180 / Math.PI;
|
||||
|
||||
// Calculations from https://www.esrl.noaa.gov/gmd/grad/solcalc/calcdetails.html
|
||||
const latitude = this.location.get('latitude');
|
||||
const longitude = this.location.get('longitude');
|
||||
|
||||
const dt_now = GLib.DateTime.new_now_local();
|
||||
const dt_zero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
|
||||
|
||||
const time_span = dt_now.difference(dt_zero);
|
||||
|
||||
const date = time_span / 1000 / 1000 / 60 / 60 / 24 + 2;
|
||||
const tz_offset = dt_now.get_utc_offset() / 1000 / 1000 / 60 / 60;
|
||||
const time_past_local_midnight = 0;
|
||||
|
||||
const julian_day = date + 2415018.5 + time_past_local_midnight - tz_offset / 24;
|
||||
const julian_century = (julian_day - 2451545) / 36525;
|
||||
const geom_mean_long_sun = (280.46646 + julian_century * (36000.76983 + julian_century * 0.0003032)) % 360;
|
||||
const geom_mean_anom_sun = 357.52911 + julian_century * (35999.05029 - 0.0001537 * julian_century);
|
||||
const eccent_earth_orbit = 0.016708634 - julian_century * (0.000042037 + 0.0000001267 * julian_century);
|
||||
const sun_eq_of_ctr = Math.sin(Math.rad(geom_mean_anom_sun)) * (1.914602 - julian_century * (0.004817 + 0.000014 * julian_century)) + Math.sin(Math.rad(2 * geom_mean_anom_sun)) * (0.019993 - 0.000101 * julian_century) + Math.sin(Math.rad(3 * geom_mean_anom_sun)) * 0.000289;
|
||||
const sun_true_long = geom_mean_long_sun + sun_eq_of_ctr;
|
||||
const sun_app_long = sun_true_long - 0.00569 - 0.00478 * Math.sin(Math.rad(125.04 - 1934.136 * julian_century));
|
||||
const mean_obliq_ecliptic = 23 + (26 + ((21.448 - julian_century * (46.815 + julian_century * (0.00059 - julian_century * 0.001813)))) / 60) / 60;
|
||||
const obliq_corr = mean_obliq_ecliptic + 0.00256 * Math.cos(Math.rad(125.04 - 1934.136 * julian_century));
|
||||
const sun_declin = Math.deg(Math.asin(Math.sin(Math.rad(obliq_corr)) * Math.sin(Math.rad(sun_app_long))));
|
||||
const var_y = Math.tan(Math.rad(obliq_corr / 2)) * Math.tan(Math.rad(obliq_corr / 2));
|
||||
const eq_of_time = 4 * Math.deg(var_y * Math.sin(2 * Math.rad(geom_mean_long_sun)) - 2 * eccent_earth_orbit * Math.sin(Math.rad(geom_mean_anom_sun)) + 4 * eccent_earth_orbit * var_y * Math.sin(Math.rad(geom_mean_anom_sun)) * Math.cos(2 * Math.rad(geom_mean_long_sun)) - 0.5 * var_y * var_y * Math.sin(4 * Math.rad(geom_mean_long_sun)) - 1.25 * eccent_earth_orbit * eccent_earth_orbit * Math.sin(2 * Math.rad(geom_mean_anom_sun)));
|
||||
const ha_sunrise = Math.deg(Math.acos(Math.cos(Math.rad(90.833)) / (Math.cos(Math.rad(latitude)) * Math.cos(Math.rad(sun_declin))) - Math.tan(Math.rad(latitude)) * Math.tan(Math.rad(sun_declin))));
|
||||
const solar_noon = (720 - 4 * longitude - eq_of_time + tz_offset * 60) / 1440;
|
||||
|
||||
const sunrise_time = solar_noon - ha_sunrise * 4 / 1440;
|
||||
const sunset_time = solar_noon + ha_sunrise * 4 / 1440;
|
||||
|
||||
const sunrise = sunrise_time * 24;
|
||||
const sunset = sunset_time * 24;
|
||||
|
||||
this._suntimes.set('sunrise', sunrise);
|
||||
this._suntimes.set('sunset', sunset);
|
||||
log_debug(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
|
||||
}
|
||||
|
||||
_regularly_update_suntimes() {
|
||||
log_debug('Regularly updating sun times...');
|
||||
this._regularly_update_suntimes_timer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3600, () => {
|
||||
this._update_suntimes();
|
||||
return true; // Repeat the loop
|
||||
});
|
||||
}
|
||||
|
||||
_stop_regularly_updating_suntimes() {
|
||||
GLib.Source.remove(this._regularly_update_suntimes_timer);
|
||||
this._regularly_update_suntimes_timer = null;
|
||||
log_debug('Stopped regularly updating sun times.');
|
||||
}
|
||||
|
||||
_is_daytime() {
|
||||
const time = GLib.DateTime.new_now_local();
|
||||
const hour = time.get_hour() + time.get_minute() / 60 + time.get_second() / 3600;
|
||||
return ( hour >= this._suntimes.get('sunrise') && hour <= this._suntimes.get('sunset') );
|
||||
}
|
||||
|
||||
_watch_for_time_change() {
|
||||
log_debug('Watching for time change...');
|
||||
this._time_change_timer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
|
||||
if ( !Me.imports.extension.enabled ) {
|
||||
// The extension doesn't exist anymore, quit the loop
|
||||
return false;
|
||||
}
|
||||
if ( this._previously_daytime !== this._is_daytime() ) {
|
||||
this._previously_daytime = this._is_daytime();
|
||||
this.emit('time-changed', this.time);
|
||||
}
|
||||
return true; // Repeat the loop
|
||||
});
|
||||
}
|
||||
|
||||
_stop_watching_for_time_change() {
|
||||
GLib.Source.remove(this._time_change_timer);
|
||||
log_debug('Stopped watching for time change.');
|
||||
}
|
||||
|
||||
}
|
||||
Signals.addSignalMethods(TimerLocation.prototype);
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { Gio, GLib } = imports.gi;
|
||||
const { extensionUtils, fileUtils } = imports.misc;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const { log_debug } = Me.imports.utils;
|
||||
|
||||
|
||||
const shell_minor_version = parseInt(imports.misc.config.PACKAGE_VERSION.split('.')[1]);
|
||||
if ( shell_minor_version <= 30 ) {
|
||||
fileUtils.loadInterfaceXML = Me.imports.convenience.loadInterfaceXML;
|
||||
}
|
||||
|
||||
|
||||
const COLOR_INTERFACE = `
|
||||
<node>
|
||||
<interface name="org.gnome.SettingsDaemon.Color">
|
||||
<property name="NightLightActive" type="b" access="read"/>
|
||||
</interface>
|
||||
</node>`;
|
||||
|
||||
|
||||
/**
|
||||
* The Night Light Timer uses Night Light as a time source.
|
||||
*
|
||||
* It connects to the Color SettingsDaemon DBus proxy to listen to the
|
||||
* 'NightLightActive' property and will signal any change.
|
||||
*/
|
||||
var TimerNightlight = class {
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling Night Light Timer...');
|
||||
this._connect_to_color_dbus_proxy();
|
||||
this._listen_to_nightlight_state();
|
||||
this.emit('time-changed', this.time);
|
||||
log_debug('Night Light Timer enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Night Light Timer...');
|
||||
this._stop_listening_to_nightlight_state();
|
||||
this._disconnect_from_color_dbus_proxy();
|
||||
log_debug('Night Light Timer disabled.');
|
||||
}
|
||||
|
||||
|
||||
get time() {
|
||||
return this._is_nightlight_active() ? 'night' : 'day';
|
||||
}
|
||||
|
||||
|
||||
_connect_to_color_dbus_proxy() {
|
||||
log_debug('Connecting to Color DBus proxy...');
|
||||
const ColorProxy = Gio.DBusProxy.makeProxyWrapper(COLOR_INTERFACE);
|
||||
this._color_dbus_proxy = new ColorProxy(
|
||||
Gio.DBus.session,
|
||||
'org.gnome.SettingsDaemon.Color',
|
||||
'/org/gnome/SettingsDaemon/Color'
|
||||
);
|
||||
log_debug('Connected to Color DBus proxy.');
|
||||
}
|
||||
|
||||
_disconnect_from_color_dbus_proxy() {
|
||||
log_debug('Disconnecting from Color DBus proxy...');
|
||||
this._color_dbus_proxy = null;
|
||||
log_debug('Disconnected from Color DBus proxy.');
|
||||
}
|
||||
|
||||
_listen_to_nightlight_state() {
|
||||
log_debug('Listening to Night Light state...');
|
||||
this._nightlight_state_connect = this._color_dbus_proxy.connect(
|
||||
'g-properties-changed',
|
||||
this._on_nightlight_state_changed.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
_stop_listening_to_nightlight_state() {
|
||||
this._color_dbus_proxy.disconnect(this._nightlight_state_connect);
|
||||
log_debug('Stopped listening to Night Light state.');
|
||||
}
|
||||
|
||||
_on_nightlight_state_changed(sender, dbus_properties) {
|
||||
const properties = dbus_properties.deep_unpack();
|
||||
if ( properties.NightLightActive ) {
|
||||
log_debug('Night Light has become ' + (properties.NightLightActive.unpack() ? '' : 'in') + 'active.');
|
||||
this.emit('time-changed', this.time);
|
||||
}
|
||||
}
|
||||
|
||||
_is_nightlight_active() {
|
||||
return this._color_dbus_proxy.NightLightActive;
|
||||
}
|
||||
|
||||
}
|
||||
Signals.addSignalMethods(TimerNightlight.prototype);
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
Night Theme Switcher Gnome Shell extension
|
||||
|
||||
Copyright (C) 2020 Romain Vigier
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free Software
|
||||
Foundation, either version 3 of the License, or (at your option) any later
|
||||
version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program. If not, see <http s ://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
const { GLib } = imports.gi;
|
||||
const { extensionUtils } = imports.misc;
|
||||
const Signals = imports.signals;
|
||||
|
||||
const Me = extensionUtils.getCurrentExtension();
|
||||
|
||||
const e = Me.imports.extension;
|
||||
const { log_debug } = Me.imports.utils;
|
||||
|
||||
|
||||
/**
|
||||
* The Schedule Timer uses a manual schedule to get the current time.
|
||||
*
|
||||
* Every second, it will check if the time has changed and signal if that's the
|
||||
* case.
|
||||
*
|
||||
* The user can change the schedule in the extension's preferences.
|
||||
*/
|
||||
var TimerSchedule = class {
|
||||
|
||||
constructor() {
|
||||
this._previously_daytime = null;
|
||||
}
|
||||
|
||||
enable() {
|
||||
log_debug('Enabling Schedule Timer...');
|
||||
this._watch_for_time_change();
|
||||
log_debug('Schedule Timer enabled.');
|
||||
}
|
||||
|
||||
disable() {
|
||||
log_debug('Disabling Schedule Timer...');
|
||||
this._stop_watching_for_time_change();
|
||||
log_debug('Schedule Timer disabled.');
|
||||
}
|
||||
|
||||
|
||||
get time() {
|
||||
return this._is_daytime() ? 'day' : 'night';
|
||||
}
|
||||
|
||||
|
||||
_is_daytime() {
|
||||
const time = GLib.DateTime.new_now_local();
|
||||
const hour = time.get_hour() + time.get_minute() / 60 + time.get_second() / 3600;
|
||||
return ( hour >= e.settingsManager.schedule_sunrise && hour <= e.settingsManager.schedule_sunset );
|
||||
}
|
||||
|
||||
_watch_for_time_change() {
|
||||
log_debug('Watching for time change...');
|
||||
this._time_change_timer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
|
||||
if ( !Me.imports.extension.enabled ) {
|
||||
// The extension doesn't exist anymore, quit the loop
|
||||
return false;
|
||||
}
|
||||
if ( this._previously_daytime !== this._is_daytime() ) {
|
||||
this._previously_daytime = this._is_daytime();
|
||||
this.emit('time-changed', this.time);
|
||||
}
|
||||
return true; // Repeat the loop
|
||||
});
|
||||
}
|
||||
|
||||
_stop_watching_for_time_change() {
|
||||
GLib.Source.remove(this._time_change_timer);
|
||||
log_debug('Stopped watching for time change.');
|
||||
}
|
||||
|
||||
}
|
||||
Signals.addSignalMethods(TimerSchedule.prototype);
|
||||
Reference in New Issue
Block a user