diff --git a/CHANGELOG.md b/CHANGELOG.md index 12ec3b3..8d043f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 application ID or a regular expression on the window title). - Build tooling: `Makefile` (build/lint/pot/pack/install/nested), ESLint config, GitHub Actions CI, and gettext translation scaffolding (`po/`). +- External control: writing `active-profile` from outside the extension now applies + that profile. Previously only the preferences process listened to the key, and only + to refresh its own UI, so an external writer changed nothing on screen. This makes + `gsettings` a supported way for another program to drive the borders without + duplicating the calibration. - `CONTRIBUTING.md` and this changelog. - Brazilian Portuguese translation. diff --git a/README.md b/README.md index e33232b..4be0d64 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,9 @@ for laptop users who need extra illumination without external gear. - **Multi-monitor support**: Show on the primary monitor or all screens. - **Seamless integration**: Native GNOME Shell feel with a toggle in the top bar. - **Convenient shortcut**: `Ctrl + Alt + L` opens the preferences. +- **Scriptable**: apply a profile or toggle the light from any other program by + writing the extension's GSettings keys — see [Controlling Lighter from another + program](#controlling-lighter-from-another-program). ## Requirements @@ -74,6 +77,51 @@ match rule — an application ID (e.g. `firefox.desktop`) and/or a window title matches, the optional *default profile* is applied. Manual selection always works and takes effect immediately. +### Controlling Lighter from another program + +Every appearance setting lives in GSettings, and the extension reacts to external +writes. That makes `gsettings` a supported entry point: another program can drive the +borders without reimplementing them, and without knowing how your borders are +calibrated. + +The schema is not in the default search path, so `--schemadir` is required: + +```bash +SCHEMA_DIR="$HOME/.local/share/gnome-shell/extensions/lighter@gnome-shell-extensions.ferrete.com/schemas" +SCHEMA=org.gnome.shell.extensions.lighter + +# Turn the light on and off +gsettings --schemadir "$SCHEMA_DIR" set $SCHEMA enabled true +gsettings --schemadir "$SCHEMA_DIR" set $SCHEMA enabled false + +# Apply a saved profile by its id +gsettings --schemadir "$SCHEMA_DIR" set $SCHEMA active-profile '' +``` + +**Look profile ids up by name, never hardcode them.** They are UUIDs generated when +the profile is created, so a hardcoded id breaks as soon as you recreate the profile. +The `profiles` key holds a JSON document shaped like +`{"version": 1, "profiles": [{"id": "…", "name": "Meet", "settings": {…}}]}`: + +```bash +gsettings --schemadir "$SCHEMA_DIR" get $SCHEMA profiles \ + | sed "s/^'//; s/'$//" \ + | jq -r '.profiles[] | select(.name == "Meet") | .id' +``` + +Writing `active-profile` applies the profile, including turning the light on. The +appearance keys (`thickness-*`, `opacity`, `color`, `temperature`, `multi-monitor`, +`maximize-brightness`) can also be set individually and take effect immediately. + +Two things worth knowing: + +- **Turn off *Auto-switch profiles* while an external program is in charge**, or the + window watcher will apply its own profile on the next focus change and overwrite + what your program just did. The two are independent controllers of the same state. +- **Changes to the extension's own code need a logout/login on Wayland**, because the + GNOME Shell cannot reload an extension there. Changes to *settings* apply + immediately — only code changes need the session restart. + ## Development See [CONTRIBUTING.md](CONTRIBUTING.md) for the full guide. Quick start: diff --git a/extension.js b/extension.js index c6e6f57..7accaff 100644 --- a/extension.js +++ b/extension.js @@ -49,6 +49,24 @@ export default class LighterExtension extends Extension { if (SYNC_KEYS.includes(key)) this._sync(); }); + + // Applying a profile by writing `active-profile` from outside. + // + // Only the preferences process listened to this key before, and only + // to refresh its own UI — so an external writer could set it and + // nothing would happen on screen. Wiring it here makes the key a + // real entry point: `gsettings set ... active-profile ` applies + // the profile, which lets another program drive the borders without + // knowing the nine appearance keys or duplicating the calibration. + // + // No feedback loop: `applyProfile` writes `active-profile` back with + // the same value, and GSettings does not emit `changed` when the + // value is unchanged. + this._activeProfileId = this._settings.connect('changed::active-profile', () => { + const id = this._settings.get_string('active-profile'); + if (id) + this._profiles.applyProfile(id); + }); this._monitorsChangedId = Main.layoutManager.connect( 'monitors-changed', () => this._sync()); @@ -73,6 +91,11 @@ export default class LighterExtension extends Extension { this._settingsId = 0; } + if (this._activeProfileId) { + this._settings.disconnect(this._activeProfileId); + this._activeProfileId = 0; + } + if (this._monitorsChangedId) { Main.layoutManager.disconnect(this._monitorsChangedId); this._monitorsChangedId = 0;