RAGS - v1.10.0
    Preparing search index...

    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.

    // brightness.js
    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 instance
    const service = new BrightnessService();

    // export to use in other modules
    export default service;
    Caution

    Utils.monitorFile only reports events that a user-space program triggers through the filesystem API. As a result, it does not catch remote events that occur on network filesystems. Furthermore, most pseudo-filesystems such as /proc, /sys and /dev/pts cannot be monitored.

    Note

    For bind to work, the property has to be defined in Service.register

    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"),
    });