diff --git a/Makefile b/Makefile index 7d1ae3d..794cd9a 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ pot: --package-name="$(NAME)" \ --package-version="$(VERSION)" \ --output=./src/po/$(UUID).pot \ - ./src/**/*.js ./src/**/*.xml + ./src/*.js ./src/**/*.js ./src/**/*.xml sed -i '1,4s/SOME DESCRIPTIVE TITLE./$(NAME)/g' ./src/po/$(UUID).pot sed -i '1,4s/YEAR/$(COPYRIGHT_YEAR)/' ./src/po/$(UUID).pot sed -i "1,4s/THE PACKAGE'S COPYRIGHT HOLDER/$(AUTHOR_NAME)/" ./src/po/$(UUID).pot diff --git a/README.md b/README.md index 82ea905..b89b7d7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ ![](./screenshot.png) -Automatically toggle between your light and dark GTK theme variants when Night Light activates. +Automatically toggle between your light and dark GTK theme variants. + +Supports Night Light, Location Services and manual schedule. _Do you need to change your GNOME shell theme as well? Try [Night Shell Switcher](https://gitlab.com/rmnvgr/nightshellswitcher-gnome-shell-extension/)!_ diff --git a/ego_description.txt b/ego_description.txt index 587a7ee..d864e35 100644 --- a/ego_description.txt +++ b/ego_description.txt @@ -1,4 +1,6 @@ -Night mode for GNOME apps! Automatically toggle between your light and dark GTK theme variants when Night Light activates. +Night mode for GNOME apps! Automatically toggle between your light and dark GTK theme variants. + +Supports Night Light, Location Services and manual schedule. These themes have been tested and work: - Adapta diff --git a/src/config.js b/src/config.js index fb47850..ccf6a17 100644 --- a/src/config.js +++ b/src/config.js @@ -16,13 +16,68 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -var EXT_NAME = 'Night Theme Switcher'; -var EXT_UUID = 'nightthemeswitcher@romainvigier.fr'; - var THEME_GSETTINGS_SCHEMA = 'org.gnome.desktop.interface'; var THEME_GSETTINGS_PROPERTY = 'gtk-theme'; var NIGHTLIGHT_GSETTINGS_SCHEMA = 'org.gnome.settings-daemon.plugins.color'; var NIGHTLIGHT_GSETTINGS_PROPERTY = 'night-light-enabled'; +var LOCATION_GSETTINGS_SCHEMA = 'org.gnome.system.location'; +var LOCATION_GSETTINGS_PROPERTY = 'enabled'; + +// GeoClue2 interfaces from https://gitlab.freedesktop.org/geoclue/geoclue/ +var GEOCLUE_MANAGER_INTERFACE = ` + + + + + + + + + + + + + + + + + +`; +var GEOCLUE_CLIENT_INTERFACE = ` + + + + + + + + + + + + + + + + + + + +`; +var GEOCLUE_LOCATION_INTERFACE = ` + + + + + + + + + + + +`; + var debug = false; diff --git a/src/metadata.json b/src/metadata.json index 39d9335..e725f42 100644 --- a/src/metadata.json +++ b/src/metadata.json @@ -1,6 +1,6 @@ { "name": "Night Theme Switcher", - "description": "Automatically toggle between your light and dark GTK theme variants when Night Light activates.", + "description": "Automatically toggle between your light and dark GTK theme variants.", "uuid": "nightthemeswitcher@romainvigier.fr", "gettext-domain": "nightthemeswitcher@romainvigier.fr", "settings-schema": "org.gnome.shell.extensions.nightthemeswitcher", diff --git a/src/modules/Nightlighter.js b/src/modules/Nightlighter.js deleted file mode 100644 index dce3cb8..0000000 --- a/src/modules/Nightlighter.js +++ /dev/null @@ -1,186 +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 . -*/ - -const { extensionUtils, fileUtils } = imports.misc; -const { Gio } = imports.gi; -const { main } = imports.ui; - -const Me = extensionUtils.getCurrentExtension(); -const config = Me.imports.config; - -const { log_debug } = Me.imports.utils; - -const Gettext = imports.gettext.domain(config.EXT_UUID); -const _ = Gettext.gettext; - - -/* -The Nightlighter establishes a connection with the session bus to get the -current Night Light status. It listens to changes and reports them. - -As Night Light is essential for the extension to work, it continuously checks -if it 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 changes to reactivate itself automatically. -*/ - -var Nightlighter = class { - - constructor() { - log_debug('Initializing Nightlighter...'); - this.first_nightlight_not_enabled_warning = true; - this.nightlight_gsettings = new Gio.Settings({ schema: config.NIGHTLIGHT_GSETTINGS_SCHEMA }); - log_debug('Nightlighter initialized.'); - } - - enable() { - log_debug('Enabling NightLighter...'); - try { - this._listen_to_nightlight_status(); - this._check_nightlight_status(); - this._connect_to_dbus(); - this._listen_to_nightlight_changes(); - this.emit(); - log_debug('Nightlighter enabled.'); - } - catch(e) { - if ( e.message ) { - main.notifyError(config.EXT_NAME, e.message); - } - } - } - - disable() { - log_debug('Disabling Nightlighter...'); - this._stop_listening_to_nightlight_status(); - this._stop_listening_to_nightlight_changes(); - this._disconnect_from_dbus(); - log_debug('Nightlighter disabled.'); - } - - get time() { - return this._get_nightlight_active() ? 'night' : 'day'; - } - - subscribe(callback) { - this.nightlight_change_callback = callback; - } - - emit() { - if ( this.nightlight_change_callback ) { - this.nightlight_change_callback(); - } - } - - _is_nightlight_enabled() { - return this.nightlight_gsettings.get_boolean(config.NIGHTLIGHT_GSETTINGS_PROPERTY); - } - - _check_nightlight_status() { - if ( !this._is_nightlight_enabled() ) { - if ( this.first_nightlight_not_enabled_warning ) { - this.first_nightlight_not_enabled_warning = false; - const message = _('Night Light must be enabled to use this extension. Please enable it in your system settings.'); - throw new Error(message); - } - else { - throw new Error(); - } - } - } - - _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_change.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_change() { - log_debug('Night Light status has changed.'); - this.enable(); - } - - _connect_to_dbus() { - if ( !this.dbus_proxy ) { - log_debug('Connecting to DBus...'); - const color_interface = fileUtils.loadInterfaceXML('org.gnome.SettingsDaemon.Color'); - const ColorProxy = Gio.DBusProxy.makeProxyWrapper(color_interface); - this.dbus_proxy = new ColorProxy( - Gio.DBus.session, - 'org.gnome.SettingsDaemon.Color', - '/org/gnome/SettingsDaemon/Color' - ); - if ( !this.dbus_proxy ) { - const message = _('Unable to create proxy to the session bus.'); - throw new Error(message); - } - } - } - - _disconnect_from_dbus() { - if ( this.dbus_proxy ) { - this.dbus_proxy = null; - log_debug('Disconnected from DBus.'); - } - } - - _listen_to_nightlight_changes() { - if ( !this.nightlight_changes_connect ) { - this.nightlight_changes_connect = this.dbus_proxy.connect( - 'g-properties-changed', - this._on_nightlight_change.bind(this) - ); - log_debug('Listening to Night Light changes...'); - } - } - - _stop_listening_to_nightlight_changes() { - if ( this.dbus_proxy && this.nightlight_changes_connect ) { - this.dbus_proxy.disconnect(this.nightlight_changes_connect); - this.nightlight_changes_connect = null; - log_debug('Stopped listening to Night Light changes.'); - } - } - - _on_nightlight_change(dbus_properties, dbus_signals) { - const signals = dbus_signals.deep_unpack(); - if ( signals.NightLightActive ) { - log_debug('Night Light has become ' + (signals.NightLightActive.unpack() ? '' : 'in') + 'active.'); - this.emit(); - } - } - - _get_nightlight_active() { - if ( this.dbus_proxy ) { - return this.dbus_proxy.NightLightActive; - } - } - -} diff --git a/src/modules/Switcher.js b/src/modules/Switcher.js index b442830..6659854 100644 --- a/src/modules/Switcher.js +++ b/src/modules/Switcher.js @@ -24,31 +24,30 @@ const config = Me.imports.config; const { log_debug } = Me.imports.utils; -const { Nightlighter } = Me.imports.modules.Nightlighter; 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 Nightlighter to listen to changes in the Night Light -status: +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 - Night Light status. - - On Night Light activation or deactivation, it asks the Themer to apply - the relevant variant. + 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 Nightlighter to -disable themselves. +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(config.EXT_UUID); + extensionUtils.initTranslations(Me.metadata.uuid); log_debug('Extension initialized.'); } @@ -57,17 +56,16 @@ var Switcher = class { try { this.theme = new Themer(); this.theme.enable(); - this.theme.subscribe(this._on_theme_change.bind(this)); + this.theme.subscribe(this._on_theme_changed.bind(this)); - this.nightlight = new Nightlighter(); - this.nightlight.enable(); - this.nightlight.subscribe(this._on_nightlight_change.bind(this)); + this.time = new Timer(); + this.time.enable(); + this.time.subscribe(this._on_time_changed.bind(this)); - this.theme.set_variant(this.nightlight.time); log_debug('Extension enabled.'); } catch(e) { - main.notifyError(config.EXT_NAME, e.message); + main.notifyError(Me.metadata.name, e.message); } } @@ -75,23 +73,23 @@ var Switcher = class { log_debug('Disabling extension...'); try { this.theme.disable(); - this.nightlight.disable(); + this.time.disable(); } catch(e) {} // Since we're disabling, we'll just ignore errors. finally { this.theme = null; - this.nightlight = null; + this.time = null; } log_debug('Extension disabled.'); } - _on_theme_change() { + _on_theme_changed() { this.theme.update_variants(); - this.theme.set_variant(this.nightlight.time); + this.theme.set_variant(this.time.current); } - _on_nightlight_change() { - this.theme.set_variant(this.nightlight.time); + _on_time_changed() { + this.theme.set_variant(this.time.current); } } diff --git a/src/modules/Themer.js b/src/modules/Themer.js index ab1621a..0d3d093 100644 --- a/src/modules/Themer.js +++ b/src/modules/Themer.js @@ -120,7 +120,7 @@ var Themer = class { if ( !this.theme_change_connect ) { this.theme_change_connect = this.theme_gsettings.connect( 'changed::' + config.THEME_GSETTINGS_PROPERTY, - this._on_theme_change.bind(this) + this._on_theme_changed.bind(this) ); log_debug('Listening for theme changes...'); } @@ -134,7 +134,7 @@ var Themer = class { } } - _on_theme_change() { + _on_theme_changed() { log_debug(`Theme has changed to "${this.current_theme}".`); this.emit(); } diff --git a/src/modules/Timer.js b/src/modules/Timer.js new file mode 100644 index 0000000..b5ffcd4 --- /dev/null +++ b/src/modules/Timer.js @@ -0,0 +1,545 @@ +/* +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 . +*/ + +const MainLoop = imports.mainloop; +const { Gio, GLib } = imports.gi; +const { extensionUtils, fileUtils } = imports.misc; +const { main } = imports.ui; + +const Me = extensionUtils.getCurrentExtension(); +const config = Me.imports.config; + +const { log_debug } = Me.imports.utils; + +const Gettext = imports.gettext.domain(Me.metadata.uuid); +const _ = Gettext.gettext; + + +/* +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. +*/ + +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('Nightlighter initialized.'); + } + + 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) { + if ( e.message ) { + main.notifyError(Me.metadata.name, e.message); + } + } + } + + 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; + } + 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'; + } + } + else { + return 'day'; + } + } + + subscribe(callback) { + this.time_change_callback = callback; + } + + emit() { + if ( this.time_change_callback ) { + this.time_change_callback(); + } + } + + _get_source() { + if ( this._is_force_manual_enabled() ) { + return 'manual'; + } + else if ( this._is_nightlight_enabled() ) { + return 'nightlight'; + } + else if ( this._is_location_enabled() ) { + return '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); + } + + 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; + } + } + +} diff --git a/src/po/fr.po b/src/po/fr.po index 27c2353..23d66da 100644 --- a/src/po/fr.po +++ b/src/po/fr.po @@ -1,34 +1,70 @@ # Night Theme Switcher # Copyright (C) 2019 Romain Vigier # This file is distributed under the same license as the Night Theme Switcher package. -# Romain Vigier <>, 2019-2020. +# Romain Vigier , 2019-2020. # msgid "" msgstr "" "Project-Id-Version: Night Theme Switcher 7\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-12 14:18+0200\n" -"PO-Revision-Date: 2020-04-12 14:21+0200\n" -"Last-Translator: Romain Vigier <>\n" -"Language-Team: French \n" -"Language: fr\n" +"POT-Creation-Date: 2020-05-06 16:40+0200\n" +"PO-Revision-Date: 2020-05-06 16:40+0200\n" +"Last-Translator: Romain Vigier \n" +"Language-Team: French - France \n" +"Language: fr_FR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" -"X-Generator: Gtranslator 3.34.0\n" +"X-Generator: Gtranslator 3.36.0\n" -#: src/modules/Nightlighter.js:101 +#: src/prefs.js:58 msgid "" -"Night Light must be enabled to use this extension. Please enable it in your " -"system settings." +"The extension will try to use Night Light or Location Services to " +"automatically set your current sunrise and sunset times if they are " +"enabled.\n" +"If you prefer, you can set a manual schedule." msgstr "" -"Le mode nuit doit être activé pour utiliser l’extension. Veuillez l’activer " -"dans les réglages du système." +"S’ils sont activés, l’extension essayera d’utiliser le mode nuit ou les " +"services de localisation pour définir automatiquement les heures de lever et " +"de coucher du soleil.\n" +"Si vous préférez, vous pouvez définir manuellement un horaire." -#: src/modules/Nightlighter.js:144 -msgid "Unable to create proxy to the session bus." -msgstr "Impossible de créer une passerelle vers le bus de la session." +#: src/prefs.js:93 +msgid "Manual schedule:" +msgstr "Horaire manuel :" + +#: src/prefs.js:138 +msgid "Sunrise: " +msgstr "Lever du soleil :" + +#: src/prefs.js:139 +msgid "Sunset: " +msgstr "Coucher du soleil :" + +#: src/modules/Timer.js:268 +msgid "Unable to connect to Color DBus proxy." +msgstr "Impossible de se connecter à la passerelle DBus Color." + +#: src/modules/Timer.js:330 +msgid "Unable to connect to GeoClue Manager DBus proxy." +msgstr "Impossible de se connecter à la passerelle DBus GeoClue Manager." + +#: src/modules/Timer.js:340 +msgid "Unable to get a GeoClue Client." +msgstr "Impossible d’obtenir un client GeoClue." + +#: src/modules/Timer.js:358 +msgid "Unable to connect to GeoClue Client DBus proxy." +msgstr "Impossible de se connecter à la passerelle DBus GeoClue Client." + +#: src/modules/Timer.js:417 +msgid "Unable to connect to GeoClue Location DBus proxy." +msgstr "Impossible de se connecter à la passerelle DBus GeoClue Location." + +#: src/modules/Timer.js:447 +msgid "Unable to get current location." +msgstr "Impossible d’obtenir l’emplacement actuel." #: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:6 msgid "Original theme" @@ -54,6 +90,56 @@ msgstr "Thème nocturne" msgid "The theme to use during nighttime" msgstr "Thème à utiliser pendant la nuit" +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:21 +msgid "Use manual schedule" +msgstr "Utiliser un horaire manuel" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:22 +msgid "Disable automatic times detection" +msgstr "Désactive la détection automatique des horaires" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:31 +msgid "Time source" +msgstr "Source des horaires" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:32 +msgid "The source used to check current time" +msgstr "Source utilisée pour vérifier les horaires" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:36 +msgid "Sunrise time" +msgstr "Heure du lever" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:37 +msgid "When the day starts" +msgstr "Quand le jour se lève" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:41 +msgid "Sunset time" +msgstr "Heure du coucher" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:42 +msgid "When the day ends" +msgstr "Quand le jour se couche" + +#~ msgid "" +#~ "Night Light or Location Services must be enabled to use this extension. " +#~ "Please enable it in your system settings." +#~ msgstr "" +#~ "Le mode nuit ou les services de localisation doivent être activés pour " +#~ "utiliser cette extension. Veuillez les activer dans vos paramètres du " +#~ "système." + +#~ msgid "" +#~ "Night Light must be enabled to use this extension. Please enable it in " +#~ "your system settings." +#~ msgstr "" +#~ "Le mode nuit doit être activé pour utiliser l’extension. Veuillez " +#~ "l’activer dans les réglages du système." + +#~ msgid "Unable to create proxy to the session bus." +#~ msgstr "Impossible de créer une passerelle vers le bus de la session." + #~ msgid "Unable to connect to the session bus." #~ msgstr "Impossible de se connecter au bus de la session." diff --git a/src/po/nightthemeswitcher@romainvigier.fr.pot b/src/po/nightthemeswitcher@romainvigier.fr.pot index b857027..073ddb5 100644 --- a/src/po/nightthemeswitcher@romainvigier.fr.pot +++ b/src/po/nightthemeswitcher@romainvigier.fr.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Night Theme Switcher 22\n" +"Project-Id-Version: Night Theme Switcher 25\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-04-12 14:18+0200\n" +"POT-Creation-Date: 2020-05-06 16:40+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,14 +17,48 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: src/modules/Nightlighter.js:101 +#: src/prefs.js:58 msgid "" -"Night Light must be enabled to use this extension. Please enable it in your " -"system settings." +"The extension will try to use Night Light or Location Services to " +"automatically set your current sunrise and sunset times if they are " +"enabled.\n" +"If you prefer, you can set a manual schedule." msgstr "" -#: src/modules/Nightlighter.js:144 -msgid "Unable to create proxy to the session bus." +#: src/prefs.js:93 +msgid "Manual schedule:" +msgstr "" + +#: src/prefs.js:138 +msgid "Sunrise: " +msgstr "" + +#: src/prefs.js:139 +msgid "Sunset: " +msgstr "" + +#: src/modules/Timer.js:268 +msgid "Unable to connect to Color DBus proxy." +msgstr "" + +#: src/modules/Timer.js:330 +msgid "Unable to connect to GeoClue Manager DBus proxy." +msgstr "" + +#: src/modules/Timer.js:340 +msgid "Unable to get a GeoClue Client." +msgstr "" + +#: src/modules/Timer.js:358 +msgid "Unable to connect to GeoClue Client DBus proxy." +msgstr "" + +#: src/modules/Timer.js:417 +msgid "Unable to connect to GeoClue Location DBus proxy." +msgstr "" + +#: src/modules/Timer.js:447 +msgid "Unable to get current location." msgstr "" #: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:6 @@ -50,3 +84,35 @@ msgstr "" #: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:17 msgid "The theme to use during nighttime" msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:21 +msgid "Use manual schedule" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:22 +msgid "Disable automatic times detection" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:31 +msgid "Time source" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:32 +msgid "The source used to check current time" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:36 +msgid "Sunrise time" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:37 +msgid "When the day starts" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:41 +msgid "Sunset time" +msgstr "" + +#: src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml:42 +msgid "When the day ends" +msgstr "" diff --git a/src/prefs.js b/src/prefs.js new file mode 100644 index 0000000..3018c64 --- /dev/null +++ b/src/prefs.js @@ -0,0 +1,239 @@ +/* +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 . +*/ + +'use strict'; + +const { Gio, GLib, Gtk } = imports.gi; +const { extensionUtils } = imports.misc; + +const Me = extensionUtils.getCurrentExtension(); +const config = Me.imports.config; + +const { log_debug } = Me.imports.utils; + +const Gettext = imports.gettext.domain(Me.metadata.uuid); +const _ = Gettext.gettext; + + +let settings; + + +function init() { + log_debug('Initializing preferences...'); + settings = extensionUtils.getSettings(); + extensionUtils.initTranslations(Me.metadata.uuid); + log_debug('Preferences initialized.'); +} + +function buildPrefsWidget() { + const prefs_widget = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + spacing: 30, + margin_top: 30, + margin_end: 36, + margin_start: 36, + valign: Gtk.Align.START, + halign: Gtk.Align.CENTER, + width_request: 450, + margin_bottom: 36, + visible: true + }) + + const prefs_info = new Gtk.Label({ + label: _('The extension will try to use Night Light or Location Services to automatically set your current sunrise and sunset times if they are enabled.\nIf you prefer, you can set a manual schedule.'), + wrap: true, + max_width_chars: 60, + halign: Gtk.Align.FILL, + opacity: 0.7, + visible: true + }); + prefs_widget.pack_start(prefs_info, true, true, 0); + + const prefs_frame = new Gtk.Frame({ + can_focus: false, + visible: true + }); + prefs_widget.pack_start(prefs_frame, true, true, 0); + + const prefs_list = new Gtk.ListBox({ + border_width: 0, + margin: 0, + can_focus: false, + selection_mode: Gtk.SelectionMode.NONE, + visible: true + }); + prefs_frame.add(prefs_list); + + // Force manual toggle + const force_manual_box = new Gtk.Box({ + margin: 16, + spacing: 12, + orientation: Gtk.Orientation.HORIZONTAL, + can_focus: false, + visible: true + }); + prefs_list.add(force_manual_box); + + const force_manual_label = new Gtk.Label({ + label: _('Manual schedule:'), + halign: Gtk.Align.START, + visible: true + }); + force_manual_box.pack_start(force_manual_label, true, true, 0); + + const force_manual_toggle = new Gtk.Switch({ + active: this.settings.get_boolean('time-force-manual'), + halign: Gtk.Align.END, + visible: true + }); + settings.bind( + 'time-force-manual', + force_manual_toggle, + 'active', + Gio.SettingsBindFlags.DEFAULT + ); + force_manual_box.pack_start(force_manual_toggle, false, false, 0); + + prefs_list.add(new Gtk.Separator({ + orientation: Gtk.Orientation.VERTICAL, + can_focus: false, + visible: true + })); + + // Times schedule + ['sunrise', 'sunset'].forEach((suntime, i, suntimes) => { + const time_box = new Gtk.Box({ + margin: 16, + spacing: 12, + orientation: Gtk.Orientation.HORIZONTAL, + can_focus: false, + visible: true + }); + prefs_list.add(time_box); + + if ( i < suntimes.length - 1 ) { + prefs_list.add(new Gtk.Separator({ + orientation: Gtk.Orientation.VERTICAL, + can_focus: false, + visible: true + })); + } + + const time_labels = new Map([ + ['sunrise', _('Sunrise: ')], + ['sunset', _('Sunset: ')] + ]); + const time_label = new Gtk.Label({ + label: time_labels.get(suntime), + halign: Gtk.Align.START, + visible: true + }) + time_box.pack_start(time_label, true, true, 0); + + const time = settings.get_double(`time-${suntime}`); + const hour = Math.trunc(time); + const minutes = Math.round((time - hour) * 60); + + const time_hour_spin = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + value: hour, + lower: 0, + upper: 23, + step_increment: 1, + page_increment: 0, + page_size: 0 + }), + value: hour, + numeric: true, + wrap: true, + orientation: Gtk.Orientation.VERTICAL, + visible: true + }); + settings.bind( + 'time-force-manual', + time_hour_spin, + 'sensitive', + Gio.SettingsBindFlags.DEFAULT + ); + settings.connect(`changed::time-${suntime}`, () => { + const hour = Math.trunc(settings.get_double(`time-${suntime}`)); + time_hour_spin.value = hour; + }); + time_hour_spin.connect('output', () => { + const text = time_hour_spin.adjustment.value.toString().padStart(2, '0'); + time_hour_spin.set_text(text); + return true; + }); + time_hour_spin.connect('value-changed', () => { + const old_time = settings.get_double(`time-${suntime}`); + const old_hour = Math.trunc(old_time); + const minutes = old_time - old_hour; + const new_time = time_hour_spin.value + minutes; + settings.set_double(`time-${suntime}`, new_time); + }); + time_box.pack_start(time_hour_spin, false, false, 0); + + const time_separator = new Gtk.Label({ + label: ':', + visible: true + }) + time_box.pack_start(time_separator, false, false, 0); + + const time_minutes_spin = new Gtk.SpinButton({ + adjustment: new Gtk.Adjustment({ + value: minutes, + lower: 0, + upper: 59, + step_increment: 1, + page_increment: 0, + page_size: 0 + }), + value: minutes, + numeric: true, + wrap: true, + orientation: Gtk.Orientation.VERTICAL, + visible: true + }); + settings.bind( + 'time-force-manual', + time_minutes_spin, + 'sensitive', + Gio.SettingsBindFlags.DEFAULT + ); + settings.connect(`changed::time-${suntime}`, () => { + const time = settings.get_double(`time-${suntime}`); + const hour = Math.trunc(time); + const minutes = Math.round((time - hour) * 60); + time_minutes_spin.value = minutes; + }); + time_minutes_spin.connect('output', () => { + const text = time_minutes_spin.adjustment.value.toString().padStart(2, '0'); + time_minutes_spin.set_text(text); + return true; + }); + time_minutes_spin.connect('value-changed', () => { + const hour = Math.trunc(settings.get_double(`time-${suntime}`)); + const minutes = time_minutes_spin.value / 60; + const new_time = hour + minutes; + settings.set_double(`time-${suntime}`, new_time); + }); + time_box.pack_start(time_minutes_spin, false, false, 0); + }); + + return prefs_widget; +} diff --git a/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml b/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml index dbf3fab..7597ae4 100644 --- a/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml +++ b/src/schemas/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml @@ -16,5 +16,30 @@ Night theme The theme to use during nighttime + + false + Use manual schedule + Disable automatic times detection + + + + + + + + "manual" + Time source + The source used to check current time + + + 6 + Sunrise time + When the day starts + + + 20 + Sunset time + When the day ends + diff --git a/src/utils.js b/src/utils.js index 61559c3..eb66e5d 100644 --- a/src/utils.js +++ b/src/utils.js @@ -23,6 +23,6 @@ const config = Me.imports.config; var log_debug = function(message) { if ( config.debug ) { - log(`[DEBUG] ${config.EXT_NAME}: ${message}`); + log(`[DEBUG] ${Me.metadata.name}: ${message}`); } }