Custom Service
Writing a custom Service is as simple as
- declaring a class
- defining its signals and properties
- declaring get/set for properties
This is an example Service for backlight using brightnessctl
to set the brightness and Utils.monitorFile
to watch for changes.
class BrightnessService extends Service { // every subclass of GObject.Object has to register itself static { // takes three arguments // the class itself // an object defining the signals // an object defining its properties Service.register( this, { // 'name-of-signal': [type as a string from GObject.TYPE_<type>], 'screen-changed': ['float'], }, { // 'kebab-cased-name': [type as a string from GObject.TYPE_<type>, 'r' | 'w' | 'rw'] // 'r' means readable // 'w' means writable // guess what 'rw' means 'screen-value': ['float', 'rw'], }, ); }
// this Service assumes only one device with backlight #interface = Utils.exec("sh -c 'ls -w1 /sys/class/backlight | head -1'");
// # prefix means private in JS #screenValue = 0; #max = Number(Utils.exec('brightnessctl max'));
// the getter has to be in snake_case get screen_value() { return this.#screenValue; }
// the setter has to be in snake_case too set screen_value(percent) { if (percent < 0) percent = 0;
if (percent > 1) percent = 1;
Utils.execAsync(`brightnessctl set ${percent * 100}% -q`); // the file monitor will handle the rest }
constructor() { super();
// setup monitor const brightness = `/sys/class/backlight/${this.#interface}/brightness`; Utils.monitorFile(brightness, () => this.#onChange());
// initialize this.#onChange(); }
#onChange() { this.#screenValue = Number(Utils.exec('brightnessctl get')) / this.#max;
// signals have to be explicitly emitted this.emit('changed'); // emits "changed" this.notify('screen-value'); // emits "notify::screen-value"
// or use Service.changed(propName: string) which does the above two // this.changed('screen-value');
// emit screen-changed with the percent as a parameter this.emit('screen-changed', this.#screenValue); }
// overwriting the connect method, let's you // change the default event that widgets connect to connect(event = 'screen-changed', callback) { return super.connect(event, callback); }}
// the singleton instanceconst service = new BrightnessService;
// export to use in other modulesexport default service;
Usage
Using it with widgets is as simple as using the builtin ones.
import brightness from './brightness.js';
const slider = Widget.Slider({ on_change: self => brightness.screen_value = self.value, value: brightness.bind('screen-value'),});
const label = Label({ label: brightness.bind('screen-value').as(v => `${v}`), setup: self => self.hook(brightness, (self, screenValue) => { // screenValue is the passed parameter from the 'screen-changed' signal self.label = screenValue ?? 0;
// NOTE: // since hooks are run upon construction // the passed screenValue will be undefined the first time
// all three are valid self.label = `${brightness.screenValue}`; self.label = `${brightness.screen_value}`; self.label = `${brightness['screen-value']}`;
}, 'screen-changed'),});