diff --git a/README.md b/README.md
index 1ea2448..cfcd31d 100644
--- a/README.md
+++ b/README.md
@@ -81,3 +81,11 @@ There's a hidden setting to configure the offset (in hours) applied to the calcu
```
gsettings --schemadir ~/.local/share/gnome-shell/extensions/nightthemeswitcher@romainvigier.fr/schemas/ set org.gnome.shell.extensions.nightthemeswitcher.time offset $DESIRED_OFFSET
```
+
+### I have disabled Location services but want to use sunrise and sunset times from my location
+
+If you know your coordinates, you can enter them in a hidden setting, and the extension will use them to calculate the sunrise and sunset times. You can set it with the `gsettings` command:
+
+```
+gsettings --schemadir ~/.local/share/gnome-shell/extensions/nightthemeswitcher@romainvigier.fr/schemas/ set org.gnome.shell.extensions.nightthemeswitcher.time location '($LATITUDE,$LONGITUDE)'
+```
diff --git a/src/data/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml b/src/data/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml
index f43d8f6..9797c21 100644
--- a/src/data/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml
+++ b/src/data/org.gnome.shell.extensions.nightthemeswitcher.gschema.xml
@@ -71,17 +71,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
false
-
+
6
-
+
20
-
- 6
-
-
- 20
+
+ (91,181)
0.4
diff --git a/src/enums/SourceType.js b/src/enums/SourceType.js
deleted file mode 100644
index f711727..0000000
--- a/src/enums/SourceType.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// SPDX-FileCopyrightText: 2022 Romain Vigier
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-/**
- * Source types.
- *
- * @readonly
- * @enum {string}
- */
-var SourceType = {
- LOCATION: 'location',
- SCHEDULE: 'schedule',
-};
diff --git a/src/meson.build b/src/meson.build
index 22a60f5..b954ad7 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -20,13 +20,9 @@ main = [
'utils.js',
]
enums = [
- 'enums/SourceType.js',
'enums/Time.js',
]
modules = [
- 'modules/Source.js',
- 'modules/SourceLocation.js',
- 'modules/SourceSchedule.js',
'modules/Switcher.js',
'modules/SwitcherCommands.js',
'modules/SwitcherTheme.js',
diff --git a/src/modules/Source.js b/src/modules/Source.js
deleted file mode 100644
index e097946..0000000
--- a/src/modules/Source.js
+++ /dev/null
@@ -1,31 +0,0 @@
-// SPDX-FileCopyrightText: 2022 Romain Vigier
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-const { extensionUtils } = imports.misc;
-
-const Me = extensionUtils.getCurrentExtension();
-
-const { Time } = Me.imports.enums.Time;
-
-
-/**
- * Time source base class.
- *
- * It needs to be enabled before being able to retrieve the time and disabled
- * before being disposed.
- *
- * It emits the `time-changed` signal containing the new time when the time
- * changes.
- */
-var Source = class {
- enable() {}
-
- disable() {}
-
- /**
- * @type {Time}
- */
- get time() {
- return Time.UNKNOWN;
- }
-};
diff --git a/src/modules/SourceLocation.js b/src/modules/SourceLocation.js
deleted file mode 100644
index b0f1444..0000000
--- a/src/modules/SourceLocation.js
+++ /dev/null
@@ -1,229 +0,0 @@
-// SPDX-FileCopyrightText: 2020-2022 Romain Vigier
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-const { Geoclue, Gio, GLib } = imports.gi;
-const { extensionUtils } = imports.misc;
-const Signals = imports.signals;
-
-const Me = extensionUtils.getCurrentExtension();
-
-const debug = Me.imports.debug;
-
-const { Source } = Me.imports.modules.Source;
-
-const { Time } = Me.imports.enums.Time;
-
-
-/**
- * The Location source uses Location Services to get the current sunrise and
- * sunset times.
- *
- * It gets the current user's location with the GeoClue2 DBus proxy and
- * calculates the times.
- *
- * It will recalculate every hour and when the user's location changes to stay
- * up to date.
- *
- * Every second, it will check if the time has changed and signal if that's the
- * case.
- */
-var SourceLocation = class extends Source {
- #settings;
-
- #cancellable = null;
- #previouslyDaytime = null;
- #geoclue = null;
- #geoclueConnection = null;
- #timeChangeTimer = null;
- #regularlyUpdateSuntimesTimer = null;
- #settingsConnections = [];
-
- constructor() {
- super();
- this.#settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.time`);
- }
-
- enable() {
- super.enable();
- debug.message('Enabling Location source...');
- this.#cancellable = new Gio.Cancellable();
- this.#connectSettings();
- this.#connectToGeoclue();
- this.#watchForTimeChange();
- this.#regularlyUpdateSuntimes();
- this.emit('time-changed', this.time);
- debug.message('Location source enabled.');
- }
-
- disable() {
- debug.message('Disabling Location source...');
- this.#stopRegularlyUpdatingSuntimes();
- this.#stopWatchingForTimeChange();
- this.#disconnectFromGeoclue();
- this.#disconnectSettings();
- this.#cancellable.cancel();
- this.#cancellable = null;
- debug.message('Location source disabled.');
- super.disable();
- }
-
-
- get time() {
- return this.#isDaytime() ? Time.DAY : Time.NIGHT;
- }
-
-
- #connectSettings() {
- debug.message('Connecting Location source to settings...');
- this.#settingsConnections.push({
- settings: this.#settings,
- id: this.#settings.connect('changed::offset', this.#updateSuntimes.bind(this)),
- });
- }
-
- #disconnectSettings() {
- this.#settingsConnections.forEach(({ settings, id }) => settings.disconnect(id));
- this.#settingsConnections = [];
- debug.message('Disconnected Location source from settings.');
- }
-
-
- #connectToGeoclue() {
- debug.message('Connecting to GeoClue...');
- Geoclue.Simple.new(
- 'org.gnome.Shell',
- Geoclue.AccuracyLevel.CITY,
- this.#cancellable,
- this.#onGeoclueReady.bind(this)
- );
- }
-
- #disconnectFromGeoclue() {
- debug.message('Disconnecting from GeoClue...');
- if (this.#geoclueConnection) {
- this.#geoclue.disconnect(this.#geoclueConnection);
- this.#geoclueConnection = null;
- }
- debug.message('Disconnected from GeoClue.');
- }
-
-
- #onGeoclueReady(_, result) {
- this.#geoclue = Geoclue.Simple.new_finish(result);
- if (!this.#geoclue) {
- console.error(`[${Me.metadata.name}] Unable to retrieve the location, using the manual schedule times instead.`);
- this.#settings.set_double('location-sunrise', this.#settings.get_double('schedule-sunrise'));
- this.#settings.set_double('location-sunset', this.#settings.get_double('schedule-sunset'));
- }
- this.#geoclueConnection = this.#geoclue.connect('notify::location', this.#onLocationUpdated.bind(this));
- debug.message('Connected to GeoClue.');
- this.#onLocationUpdated();
- this.emit('time-changed', this.time);
- }
-
- #onLocationUpdated(_geoclue, _location) {
- debug.message('Location has changed.');
- this.#updateLocation();
- this.#updateSuntimes();
- }
-
-
- #updateLocation() {
- if (this.#geoclue) {
- debug.message('Updating location...');
- const { latitude, longitude } = this.#geoclue.get_location();
- this.location = new Map([
- ['latitude', latitude],
- ['longitude', longitude],
- ]);
- debug.message(`Current location: (${latitude};${longitude})`);
- }
- }
-
- #updateSuntimes() {
- if (!this.location)
- return;
-
- debug.message('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 dtNow = GLib.DateTime.new_now_local();
- const dtZero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
-
- const timeSpan = dtNow.difference(dtZero);
-
- const date = timeSpan / 1000 / 1000 / 60 / 60 / 24 + 2;
- const tzOffset = dtNow.get_utc_offset() / 1000 / 1000 / 60 / 60;
-
- const julianDay = date + 2415018.5 - tzOffset / 24;
- const julianCentury = (julianDay - 2451545) / 36525;
- const geomMeanLongSun = (280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)) % 360;
- const geomMeanAnomSun = 357.52911 + julianCentury * (35999.05029 - 0.0001537 * julianCentury);
- const eccentEarthOrbit = 0.016708634 - julianCentury * (0.000042037 + 0.0000001267 * julianCentury);
- const sunEqOfCtr = Math.sin(Math.rad(geomMeanAnomSun)) * (1.914602 - julianCentury * (0.004817 + 0.000014 * julianCentury)) + Math.sin(Math.rad(2 * geomMeanAnomSun)) * (0.019993 - 0.000101 * julianCentury) + Math.sin(Math.rad(3 * geomMeanAnomSun)) * 0.000289;
- const sunTrueLong = geomMeanLongSun + sunEqOfCtr;
- const sunAppLong = sunTrueLong - 0.00569 - 0.00478 * Math.sin(Math.rad(125.04 - 1934.136 * julianCentury));
- const meanObliqEcliptic = 23 + (26 + ((21.448 - julianCentury * (46.815 + julianCentury * (0.00059 - julianCentury * 0.001813)))) / 60) / 60;
- const obliqCorr = meanObliqEcliptic + 0.00256 * Math.cos(Math.rad(125.04 - 1934.136 * julianCentury));
- const sunDeclin = Math.deg(Math.asin(Math.sin(Math.rad(obliqCorr)) * Math.sin(Math.rad(sunAppLong))));
- const varY = Math.tan(Math.rad(obliqCorr / 2)) * Math.tan(Math.rad(obliqCorr / 2));
- const eqOfTime = 4 * Math.deg(varY * Math.sin(2 * Math.rad(geomMeanLongSun)) - 2 * eccentEarthOrbit * Math.sin(Math.rad(geomMeanAnomSun)) + 4 * eccentEarthOrbit * varY * Math.sin(Math.rad(geomMeanAnomSun)) * Math.cos(2 * Math.rad(geomMeanLongSun)) - 0.5 * varY * varY * Math.sin(4 * Math.rad(geomMeanLongSun)) - 1.25 * eccentEarthOrbit * eccentEarthOrbit * Math.sin(2 * Math.rad(geomMeanAnomSun)));
- const haSunrise = Math.deg(Math.acos(Math.cos(Math.rad(90.833)) / (Math.cos(Math.rad(latitude)) * Math.cos(Math.rad(sunDeclin))) - Math.tan(Math.rad(latitude)) * Math.tan(Math.rad(sunDeclin))));
- const solarNoon = (720 - 4 * longitude - eqOfTime + tzOffset * 60) / 1440;
-
- const timeSunrise = solarNoon - haSunrise * 4 / 1440;
- const timeSunset = solarNoon + haSunrise * 4 / 1440;
-
- const offset = this.#settings.get_double('offset');
- const sunrise = timeSunrise * 24 + offset;
- const sunset = timeSunset * 24 - offset;
-
- this.#settings.set_double('location-sunrise', sunrise);
- this.#settings.set_double('location-sunset', sunset);
-
- debug.message(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
- }
-
- #regularlyUpdateSuntimes() {
- debug.message('Regularly updating sun times...');
- this.#regularlyUpdateSuntimesTimer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3600, () => {
- this.#updateSuntimes();
- return GLib.SOURCE_CONTINUE;
- });
- }
-
- #stopRegularlyUpdatingSuntimes() {
- GLib.Source.remove(this.#regularlyUpdateSuntimesTimer);
- this.#regularlyUpdateSuntimesTimer = null;
- debug.message('Stopped regularly updating sun times.');
- }
-
- #isDaytime() {
- 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('location-sunrise') && hour <= this.#settings.get_double('location-sunset');
- }
-
- #watchForTimeChange() {
- debug.message('Watching for time change...');
- this.#timeChangeTimer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
- if (this.#previouslyDaytime !== this.#isDaytime()) {
- this.#previouslyDaytime = this.#isDaytime();
- this.emit('time-changed', this.time);
- }
- return GLib.SOURCE_CONTINUE;
- });
- }
-
- #stopWatchingForTimeChange() {
- GLib.Source.remove(this.#timeChangeTimer);
- debug.message('Stopped watching for time change.');
- }
-};
-Signals.addSignalMethods(SourceLocation.prototype);
diff --git a/src/modules/SourceSchedule.js b/src/modules/SourceSchedule.js
deleted file mode 100644
index 88be7e8..0000000
--- a/src/modules/SourceSchedule.js
+++ /dev/null
@@ -1,79 +0,0 @@
-// SPDX-FileCopyrightText: 2020-2022 Romain Vigier
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-const { GLib } = imports.gi;
-const { extensionUtils } = imports.misc;
-const Signals = imports.signals;
-
-const Me = extensionUtils.getCurrentExtension();
-
-const debug = Me.imports.debug;
-
-const { Source } = Me.imports.modules.Source;
-
-const { Time } = Me.imports.enums.Time;
-
-
-/**
- * The Schedule source 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 SourceSchedule = class extends Source {
- #settings;
-
- #previouslyDaytime = null;
- #timeChangeTimer = null;
-
- constructor() {
- super();
- this.#settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.time`);
- }
-
- enable() {
- super.enable();
- debug.message('Enabling Schedule source...');
- this.#watchForTimeChange();
- this.emit('time-changed', this.time);
- debug.message('Schedule source enabled.');
- }
-
- disable() {
- debug.message('Disabling Schedule source...');
- this.#stopWatchingForTimeChange();
- debug.message('Schedule source disabled.');
- super.disable();
- }
-
-
- get time() {
- return this.#isDaytime() ? Time.DAY : Time.NIGHT;
- }
-
-
- #isDaytime() {
- 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('schedule-sunrise') && hour <= this.#settings.get_double('schedule-sunset');
- }
-
- #watchForTimeChange() {
- debug.message('Watching for time change...');
- this.#timeChangeTimer = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
- if (this.#previouslyDaytime !== this.#isDaytime()) {
- this.#previouslyDaytime = this.#isDaytime();
- this.emit('time-changed', this.time);
- }
- return GLib.SOURCE_CONTINUE;
- });
- }
-
- #stopWatchingForTimeChange() {
- GLib.Source.remove(this.#timeChangeTimer);
- debug.message('Stopped watching for time change.');
- }
-};
-Signals.addSignalMethods(SourceSchedule.prototype);
diff --git a/src/modules/Switcher.js b/src/modules/Switcher.js
index 67c3483..c406dad 100644
--- a/src/modules/Switcher.js
+++ b/src/modules/Switcher.js
@@ -77,7 +77,7 @@ var Switcher = class {
#connectTimer() {
debug.message(`Connecting ${this.#name} switcher to Timer...`);
- this.#timerConnection = this.#timer.connect('time-changed', this.#onTimeChanged.bind(this));
+ this.#timerConnection = this.#timer.connect('notify::time', this.#onTimeChanged.bind(this));
}
#disconnectTimer() {
diff --git a/src/modules/Timer.js b/src/modules/Timer.js
index 6de11d1..7b69a67 100644
--- a/src/modules/Timer.js
+++ b/src/modules/Timer.js
@@ -1,9 +1,8 @@
// SPDX-FileCopyrightText: 2020-2022 Romain Vigier
// SPDX-License-Identifier: GPL-3.0-or-later
-const { Gio, Meta, Shell } = imports.gi;
+const { Geoclue, Gio, GLib, GObject, Meta, Shell } = imports.gi;
const { extensionUtils } = imports.misc;
-const Signals = imports.signals;
const { main } = imports.ui;
@@ -11,72 +10,100 @@ const Me = extensionUtils.getCurrentExtension();
const debug = Me.imports.debug;
-const { SourceType } = Me.imports.enums.SourceType;
const { Time } = Me.imports.enums.Time;
-const { SourceLocation } = Me.imports.modules.SourceLocation;
-const { SourceSchedule } = Me.imports.modules.SourceSchedule;
/**
* 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.
+ * They can connect to its 'time' property and query it for the current time.
*
* It will try to use the current location as a time source but will fall back
* to a manual schedule if the location services are disabled or if the user
* forced the manual schedule in the preferences.
*/
-var Timer = class {
+var Timer = class extends GObject.Object {
#settings;
#interfaceSettings;
#locationSettings;
#time;
- #source = null;
- #sourceConnectionId = null;
+ #cancellable = null;
#previousKeybinding = null;
+ #timeTimeoutId = null;
+ #geoclue = null;
+ #geoclueLocationConnectionId = null;
+ #suntimesTimeoutId = null;
+ #manuallySetTime = false;
+
#settingsConnections = [];
+ static {
+ GObject.registerClass({
+ Properties: {
+ time: GObject.ParamSpec.string('time', 'Time', 'Time', GObject.ParamFlags.READWRITE, Time.UNKNOWN),
+ },
+ }, this);
+ }
+
constructor() {
+ super();
this.#settings = extensionUtils.getSettings(`${Me.metadata['settings-schema']}.time`);
this.#interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this.#locationSettings = new Gio.Settings({ schema: 'org.gnome.system.location' });
- this.#time = this.#interfaceSettings.get_string('color-scheme') === 'prefer-dark' ? Time.NIGHT : Time.DAY;
}
enable() {
debug.message('Enabling Timer...');
+ this.#cancellable = new Gio.Cancellable();
this.#connectSettings();
- this.#createSource();
- this.#connectSource();
- this.#enableSource();
+ this.#trackTime();
+ if (this.#settings.get_boolean('manual-schedule')) {
+ debug.message('Using the manual schedule.');
+ } else {
+ debug.message('Using location.');
+ this.#trackLocation();
+ this.#trackSuntimes();
+ }
this.#addKeybinding();
+ this.#changeTime(this.#computeTime());
debug.message('Timer enabled.');
}
disable() {
debug.message('Disabling Timer...');
this.#removeKeybinding();
- this.#disconnectSource();
- this.#disableSource();
+ this.#untrackSuntimes();
+ this.#untrackLocation();
+ this.#untrackTime();
this.#disconnectSettings();
+ this.#cancellable.cancel();
debug.message('Timer disabled.');
}
get time() {
- return this.#time;
+ return this.#time || Time.UNKNOWN;
}
- set time(time) {
- if (time === this.#time)
+ #changeTime(time, manual = false) {
+ if (time === this.#time) {
+ if (!manual && this.#manuallySetTime)
+ this.#manuallySetTime = false;
return;
- debug.message(`Time has changed to ${time}.`);
+ }
+
+ if (!manual && time !== this.#time && this.#manuallySetTime)
+ return;
+
this.#time = time;
+ this.#manuallySetTime = manual;
+
+ debug.message(manual ? `Time manually set to ${time}.` : `Time changed to ${time}.`);
+
main.layoutManager.screenTransition.run();
this.#interfaceSettings.set_string('color-scheme', time === Time.NIGHT ? 'prefer-dark' : 'default');
- this.emit('time-changed', time);
+ this.notify('time');
}
@@ -84,11 +111,15 @@ var Timer = class {
debug.message('Connecting Timer to settings...');
this.#settingsConnections.push({
settings: this.#locationSettings,
- id: this.#locationSettings.connect('changed::enabled', this.#onSourceChanged.bind(this)),
+ id: this.#locationSettings.connect('changed::enabled', this.#onLocationStateChanged.bind(this)),
});
this.#settingsConnections.push({
settings: this.#settings,
- id: this.#settings.connect('changed::manual-schedule', this.#onSourceChanged.bind(this)),
+ id: this.#settings.connect('changed::manual-schedule', this.#onManualScheduleStateChanged.bind(this)),
+ });
+ this.#settingsConnections.push({
+ settings: this.#settings,
+ id: this.#settings.connect('changed::offset', this.#onOffsetChanged.bind(this)),
});
this.#settingsConnections.push({
settings: this.#settings,
@@ -106,67 +137,59 @@ var Timer = class {
debug.message('Disconnected Timer from settings.');
}
- #createSource() {
- const source = this.#getSource();
- switch (source) {
- case SourceType.LOCATION:
- this.#source = new SourceLocation();
- break;
- case SourceType.SCHEDULE:
- this.#source = new SourceSchedule();
- break;
+
+ #trackTime() {
+ debug.message('Watching for time change...');
+ this.#timeTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1, () => {
+ this.#changeTime(this.#computeTime());
+ return GLib.SOURCE_CONTINUE;
+ });
+ }
+
+ #untrackTime() {
+ if (this.#timeTimeoutId) {
+ GLib.Source.remove(this.#timeTimeoutId);
+ this.#timeTimeoutId = null;
}
- }
-
- #enableSource() {
- this.#source.enable();
- }
-
- #disableSource() {
- if (this.#source)
- this.#source.disable();
- this.#source = null;
- }
-
- #connectSource() {
- debug.message('Connecting to time source...');
- this.#sourceConnectionId = this.#source.connect('time-changed', this.#onTimeChanged.bind(this));
- }
-
- #disconnectSource() {
- if (this.#sourceConnectionId && this.#source)
- this.#source.disconnect(this.#sourceConnectionId);
- this.#sourceConnectionId = null;
- debug.message('Disconnected from time source.');
+ debug.message('Stopped watching for time change.');
}
- #onSourceChanged() {
- this.disable();
- this.enable();
+ #trackLocation() {
+ debug.message('Connecting to GeoClue...');
+ Geoclue.Simple.new(
+ 'org.gnome.Shell',
+ Geoclue.AccuracyLevel.CITY,
+ this.#cancellable,
+ this.#onGeoclueReady.bind(this)
+ );
}
- #onOndemandKeybindingChanged() {
- this.#removeKeybinding();
- this.#addKeybinding();
- }
-
- #onTimeChanged(_source, newTime) {
- this.time = newTime;
- }
-
- #onColorSchemeChanged() {
- this.time = this.#interfaceSettings.get_string('color-scheme') === 'prefer-dark' ? Time.NIGHT : Time.DAY;
+ #untrackLocation() {
+ debug.message('Disconnecting from GeoClue...');
+ if (this.#geoclue && this.#geoclueLocationConnectionId) {
+ this.#geoclue.disconnect(this.#geoclueLocationConnectionId);
+ this.#geoclueLocationConnectionId = null;
+ this.#geoclue = null;
+ }
+ debug.message('Disconnected from GeoClue.');
}
- #getSource() {
- debug.message('Getting time source...');
- let source = SourceType.SCHEDULE;
- if (this.#locationSettings.get_boolean('enabled') && !this.#settings.get_boolean('manual-schedule'))
- source = SourceType.LOCATION;
- debug.message(`Time source is ${source}.`);
- return source;
+ #trackSuntimes() {
+ debug.message('Regularly updating sun times...');
+ this.#suntimesTimeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 3600, () => {
+ this.#updateSuntimes();
+ return GLib.SOURCE_CONTINUE;
+ });
+ }
+
+ #untrackSuntimes() {
+ if (this.#suntimesTimeoutId) {
+ GLib.Source.remove(this.#suntimesTimeoutId);
+ this.#suntimesTimeoutId = null;
+ }
+ debug.message('Stopped regularly updating sun times.');
}
@@ -181,7 +204,8 @@ var Timer = class {
Meta.KeyBindingFlags.IGNORE_AUTOREPEAT,
Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW,
() => {
- this.time = this.time === Time.NIGHT ? Time.DAY : Time.NIGHT;
+ const time = this.time === Time.NIGHT ? Time.DAY : Time.NIGHT;
+ this.#changeTime(time, true);
}
);
debug.message('Added keybinding.');
@@ -194,5 +218,112 @@ var Timer = class {
debug.message('Removed keybinding.');
}
}
+
+
+ #onLocationStateChanged() {
+ this.disable();
+ this.enable();
+ }
+
+ #onManualScheduleStateChanged() {
+ this.disable();
+ this.enable();
+ }
+
+ #onOffsetChanged() {
+ this.#updateSuntimes();
+ }
+
+ #onOndemandKeybindingChanged() {
+ this.#removeKeybinding();
+ this.#addKeybinding();
+ }
+
+ #onColorSchemeChanged() {
+ const time = this.#interfaceSettings.get_string('color-scheme') === 'prefer-dark' ? Time.NIGHT : Time.DAY;
+ this.#changeTime(time, true);
+ }
+
+ #onGeoclueReady(_, result) {
+ try {
+ this.#geoclue = Geoclue.Simple.new_finish(result);
+ this.#geoclueLocationConnectionId = this.#geoclue.connect('notify::location', this.#onLocationChanged.bind(this));
+ debug.message('Connected to GeoClue.');
+ this.#onLocationChanged();
+ } catch (e) {
+ const [latitude, longitude] = this.#settings.get_value('location').deepUnpack();
+ if (latitude >= -90 && latitude <= 90 && longitude >= -180 && longitude <= 180) {
+ console.error(`[${Me.metadata.name}] Unable to retrieve the location, using the last known location instead.`);
+ this.#updateSuntimes();
+ } else {
+ console.error(`[${Me.metadata.name}] Unable to retrieve the location, using the manual schedule times instead.`);
+ }
+ }
+ }
+
+ #onLocationChanged(_geoclue, _location) {
+ debug.message('Location has changed.');
+ const { latitude, longitude } = this.#geoclue.get_location();
+ this.#settings.set_value('location', new GLib.Variant('(dd)', [latitude, longitude]));
+ debug.message(`Current location: (${latitude};${longitude})`);
+ this.#updateSuntimes();
+ }
+
+
+ #computeTime() {
+ const sunrise = this.#settings.get_double('sunrise');
+ const sunset = this.#settings.get_double('sunset');
+ const datetime = GLib.DateTime.new_now_local();
+ const hour = datetime.get_hour() + datetime.get_minute() / 60 + datetime.get_second() / 3600;
+ return hour >= sunrise && hour <= sunset ? Time.DAY : Time.NIGHT;
+ }
+
+ #updateSuntimes() {
+ const [latitude, longitude] = this.#settings.get_value('location').deepUnpack();
+
+ if (latitude < -90 && latitude > 90 && longitude < -180 && longitude > 180)
+ return;
+
+ debug.message('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 dtNow = GLib.DateTime.new_now_local();
+ const dtZero = GLib.DateTime.new_utc(1900, 1, 1, 0, 0, 0);
+
+ const timeSpan = dtNow.difference(dtZero);
+
+ const date = timeSpan / 1000 / 1000 / 60 / 60 / 24 + 2;
+ const tzOffset = dtNow.get_utc_offset() / 1000 / 1000 / 60 / 60;
+
+ const julianDay = date + 2415018.5 - tzOffset / 24;
+ const julianCentury = (julianDay - 2451545) / 36525;
+ const geomMeanLongSun = (280.46646 + julianCentury * (36000.76983 + julianCentury * 0.0003032)) % 360;
+ const geomMeanAnomSun = 357.52911 + julianCentury * (35999.05029 - 0.0001537 * julianCentury);
+ const eccentEarthOrbit = 0.016708634 - julianCentury * (0.000042037 + 0.0000001267 * julianCentury);
+ const sunEqOfCtr = Math.sin(Math.rad(geomMeanAnomSun)) * (1.914602 - julianCentury * (0.004817 + 0.000014 * julianCentury)) + Math.sin(Math.rad(2 * geomMeanAnomSun)) * (0.019993 - 0.000101 * julianCentury) + Math.sin(Math.rad(3 * geomMeanAnomSun)) * 0.000289;
+ const sunTrueLong = geomMeanLongSun + sunEqOfCtr;
+ const sunAppLong = sunTrueLong - 0.00569 - 0.00478 * Math.sin(Math.rad(125.04 - 1934.136 * julianCentury));
+ const meanObliqEcliptic = 23 + (26 + ((21.448 - julianCentury * (46.815 + julianCentury * (0.00059 - julianCentury * 0.001813)))) / 60) / 60;
+ const obliqCorr = meanObliqEcliptic + 0.00256 * Math.cos(Math.rad(125.04 - 1934.136 * julianCentury));
+ const sunDeclin = Math.deg(Math.asin(Math.sin(Math.rad(obliqCorr)) * Math.sin(Math.rad(sunAppLong))));
+ const varY = Math.tan(Math.rad(obliqCorr / 2)) * Math.tan(Math.rad(obliqCorr / 2));
+ const eqOfTime = 4 * Math.deg(varY * Math.sin(2 * Math.rad(geomMeanLongSun)) - 2 * eccentEarthOrbit * Math.sin(Math.rad(geomMeanAnomSun)) + 4 * eccentEarthOrbit * varY * Math.sin(Math.rad(geomMeanAnomSun)) * Math.cos(2 * Math.rad(geomMeanLongSun)) - 0.5 * varY * varY * Math.sin(4 * Math.rad(geomMeanLongSun)) - 1.25 * eccentEarthOrbit * eccentEarthOrbit * Math.sin(2 * Math.rad(geomMeanAnomSun)));
+ const haSunrise = Math.deg(Math.acos(Math.cos(Math.rad(90.833)) / (Math.cos(Math.rad(latitude)) * Math.cos(Math.rad(sunDeclin))) - Math.tan(Math.rad(latitude)) * Math.tan(Math.rad(sunDeclin))));
+ const solarNoon = (720 - 4 * longitude - eqOfTime + tzOffset * 60) / 1440;
+
+ const timeSunrise = solarNoon - haSunrise * 4 / 1440;
+ const timeSunset = solarNoon + haSunrise * 4 / 1440;
+
+ const offset = this.#settings.get_double('offset');
+ const sunrise = timeSunrise * 24 + offset;
+ const sunset = timeSunset * 24 - offset;
+
+ this.#settings.set_double('sunrise', sunrise);
+ this.#settings.set_double('sunset', sunset);
+
+ debug.message(`New sun times: (sunrise: ${sunrise}; sunset: ${sunset})`);
+ }
};
-Signals.addSignalMethods(Timer.prototype);
diff --git a/src/po/POTFILES b/src/po/POTFILES
index 7005a6d..224ee9e 100644
--- a/src/po/POTFILES
+++ b/src/po/POTFILES
@@ -17,13 +17,8 @@ src/data/ui/ShortcutButton.ui
src/data/ui/ThemesPage.ui
src/data/ui/TimeChooser.ui
-src/enums/SourceType.js
-src/enums/SunTime.js
src/enums/Time.js
-src/modules/Source.js
-src/modules/SourceLocation.js
-src/modules/SourceSchedule.js
src/modules/Switcher.js
src/modules/SwitcherCommands.js
src/modules/SwitcherTheme.js
diff --git a/src/preferences/SchedulePage.js b/src/preferences/SchedulePage.js
index 80551cb..c234b19 100644
--- a/src/preferences/SchedulePage.js
+++ b/src/preferences/SchedulePage.js
@@ -23,8 +23,8 @@ var SchedulePage = GObject.registerClass({
settings.bind('manual-schedule', this._manual_schedule_switch, 'active', Gio.SettingsBindFlags.DEFAULT);
- settings.bind('schedule-sunrise', this._schedule_sunrise_time_chooser, 'time', Gio.SettingsBindFlags.DEFAULT);
- settings.bind('schedule-sunset', this._schedule_sunset_time_chooser, 'time', Gio.SettingsBindFlags.DEFAULT);
+ settings.bind('sunrise', this._schedule_sunrise_time_chooser, 'time', Gio.SettingsBindFlags.DEFAULT);
+ settings.bind('sunset', this._schedule_sunset_time_chooser, 'time', Gio.SettingsBindFlags.DEFAULT);
settings.connect('changed::nightthemeswitcher-ondemand-keybinding', () => {
this._keyboard_shortcut_button.keybinding = settings.get_strv('nightthemeswitcher-ondemand-keybinding')[0];