172 lines
5.9 KiB
TypeScript
172 lines
5.9 KiB
TypeScript
import Gio from 'gi://Gio';
|
|
import GLib from 'gi://GLib';
|
|
import { Extension } from 'resource:///org/gnome/shell/extensions/extension.js';
|
|
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
|
|
import { BleMonitor } from './bleMonitor.js';
|
|
import { BleUnlockIndicator } from './quickSettingsMenu.js';
|
|
|
|
const SCREENSAVER_IFACE = `
|
|
<node>
|
|
<interface name="org.gnome.ScreenSaver">
|
|
<method name="SetActive">
|
|
<arg type="b" direction="in"/>
|
|
</method>
|
|
</interface>
|
|
</node>`;
|
|
|
|
export default class BleUnlockExtension extends Extension {
|
|
private _monitor: BleMonitor | null = null;
|
|
private _indicator: InstanceType<typeof BleUnlockIndicator> | null = null;
|
|
private _settings: Gio.Settings | null = null;
|
|
private _screenSaverProxy: Gio.DBusProxy | null = null;
|
|
private _unlockTimerId: number | null = null;
|
|
private _settingsChangedIds: number[] = [];
|
|
|
|
enable(): void {
|
|
this._settings = this.getSettings();
|
|
this._monitor = new BleMonitor();
|
|
|
|
// Wire lock/unlock hooks
|
|
this._monitor.onSignalBelowThreshold = () => {
|
|
console.debug('[BleUnlock] Locking screen');
|
|
this._setScreenSaverActive(true);
|
|
};
|
|
|
|
this._monitor.onSignalAboveThreshold = () => {
|
|
console.debug('[BleUnlock] Scheduling unlock');
|
|
if (this._unlockTimerId !== null) {
|
|
GLib.Source.remove(this._unlockTimerId);
|
|
this._unlockTimerId = null;
|
|
}
|
|
const UNLOCK_DELAY_SEC = 3;
|
|
this._unlockTimerId = GLib.timeout_add_seconds(
|
|
GLib.PRIORITY_DEFAULT,
|
|
UNLOCK_DELAY_SEC,
|
|
() => {
|
|
console.debug('[BleUnlock] Unlocking screen');
|
|
this._setScreenSaverActive(false);
|
|
this._unlockTimerId = null;
|
|
return GLib.SOURCE_REMOVE;
|
|
},
|
|
);
|
|
};
|
|
|
|
// Create ScreenSaver proxy
|
|
try {
|
|
this._screenSaverProxy = Gio.DBusProxy.new_sync(
|
|
Gio.DBus.session,
|
|
Gio.DBusProxyFlags.NONE,
|
|
Gio.DBusNodeInfo.new_for_xml(SCREENSAVER_IFACE).lookup_interface('org.gnome.ScreenSaver'),
|
|
'org.gnome.ScreenSaver',
|
|
'/org/gnome/ScreenSaver',
|
|
'org.gnome.ScreenSaver',
|
|
null,
|
|
);
|
|
} catch (e) {
|
|
console.error(`[BleUnlock] Failed to create ScreenSaver proxy: ${e}`);
|
|
}
|
|
|
|
// Create indicator and add to quick settings
|
|
this._indicator = new BleUnlockIndicator(
|
|
this._settings,
|
|
this._monitor,
|
|
() => this.openPreferences(),
|
|
this.uuid,
|
|
);
|
|
Main.panel.statusArea.quickSettings.addExternalIndicator(this._indicator as any);
|
|
|
|
// Start monitoring if enabled
|
|
if (this._settings.get_boolean('enabled')) {
|
|
this._startMonitoring();
|
|
}
|
|
|
|
// Watch settings changes
|
|
this._settingsChangedIds.push(
|
|
this._settings.connect('changed::enabled', () => {
|
|
if (this._settings!.get_boolean('enabled')) {
|
|
this._startMonitoring();
|
|
} else {
|
|
this._monitor?.stopMonitoring();
|
|
}
|
|
}),
|
|
);
|
|
|
|
this._settingsChangedIds.push(
|
|
this._settings.connect('changed::device-address', () => {
|
|
if (this._settings!.get_boolean('enabled')) {
|
|
this._monitor?.stopMonitoring();
|
|
this._startMonitoring();
|
|
}
|
|
}),
|
|
);
|
|
|
|
this._settingsChangedIds.push(
|
|
this._settings.connect('changed::poll-interval', () => {
|
|
if (this._settings!.get_boolean('enabled')) {
|
|
this._monitor?.stopMonitoring();
|
|
this._startMonitoring();
|
|
}
|
|
}),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* disable() is NOT called on screen lock because this extension uses
|
|
* session-modes: ["user", "unlock-dialog"]. It is called when the extension
|
|
* is disabled, GNOME Shell exits, or the user logs out.
|
|
*
|
|
* Stops all timers, disconnects all signals, nulls all references, destroys all widgets.
|
|
*/
|
|
disable(): void {
|
|
if (this._unlockTimerId !== null) {
|
|
GLib.Source.remove(this._unlockTimerId);
|
|
this._unlockTimerId = null;
|
|
}
|
|
|
|
this._monitor?.stopMonitoring();
|
|
this._monitor = null;
|
|
|
|
this._indicator?.destroy();
|
|
this._indicator = null;
|
|
|
|
if (this._settings) {
|
|
for (const id of this._settingsChangedIds) {
|
|
this._settings.disconnect(id);
|
|
}
|
|
}
|
|
this._settingsChangedIds = [];
|
|
this._settings = null;
|
|
this._screenSaverProxy = null;
|
|
}
|
|
|
|
private _startMonitoring(): void {
|
|
if (!this._monitor || !this._settings) return;
|
|
const address = this._settings.get_string('device-address');
|
|
if (!address) return;
|
|
|
|
const config = {
|
|
rssiLockThreshold: this._settings.get_int('rssi-lock-threshold'),
|
|
rssiUnlockThreshold: this._settings.get_int('rssi-unlock-threshold'),
|
|
};
|
|
const pollInterval = this._settings.get_int('poll-interval');
|
|
this._monitor.startMonitoring(address, config, pollInterval);
|
|
}
|
|
|
|
private _setScreenSaverActive(active: boolean): void {
|
|
if (!this._screenSaverProxy) return;
|
|
try {
|
|
this._screenSaverProxy.call(
|
|
'SetActive',
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
new GLib.Variant('(b)', [active]) as any,
|
|
Gio.DBusCallFlags.NONE,
|
|
-1,
|
|
null,
|
|
null,
|
|
);
|
|
} catch (e) {
|
|
console.error(`[BleUnlock] SetActive(${active}) failed: ${e}`);
|
|
}
|
|
}
|
|
}
|