gnome-shell/js/ui/status/backlight.js
Barnabás Pőcze c227d0b38e status/backlight: Do only one dbus call per local change
The values of the two control widgets are syncronized, meaning
that both emit signals when the local value changes, regardless
which one is visible and is actually used by the user.

This is not ideal because it leads to two dbus calls
per local change. To alleviate this, only consider
changes from the widget that is visible.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3086>
2024-01-16 23:30:42 +00:00

225 lines
6.8 KiB
JavaScript

// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */
import Clutter from 'gi://Clutter';
import Gio from 'gi://Gio';
import GObject from 'gi://GObject';
import St from 'gi://St';
import {QuickMenuToggle, SystemIndicator} from '../quickSettings.js';
import * as PopupMenu from '../popupMenu.js';
import {Slider} from '../slider.js';
import {loadInterfaceXML} from '../../misc/fileUtils.js';
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const BrightnessInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Power.Keyboard');
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
const SliderItem = GObject.registerClass({
Properties: {
'value': GObject.ParamSpec.int(
'value', '', '',
GObject.ParamFlags.READWRITE,
0, 100, 0),
},
}, class SliderItem extends PopupMenu.PopupBaseMenuItem {
constructor() {
super({
activate: false,
style_class: 'keyboard-brightness-item',
});
this._slider = new Slider(0);
this._sliderChangedId = this._slider.connect('notify::value',
() => this.notify('value'));
this._slider.accessible_name = _('Keyboard Brightness');
this.add_child(this._slider);
}
get value() {
return this._slider.value * 100;
}
set value(value) {
if (this.value === value)
return;
this._slider.block_signal_handler(this._sliderChangedId);
this._slider.value = value / 100;
this._slider.unblock_signal_handler(this._sliderChangedId);
this.notify('value');
}
});
const DiscreteItem = GObject.registerClass({
Properties: {
'value': GObject.ParamSpec.int(
'value', '', '',
GObject.ParamFlags.READWRITE,
0, 100, 0),
'n-levels': GObject.ParamSpec.int(
'n-levels', '', '',
GObject.ParamFlags.READWRITE,
1, 3, 1),
},
}, class DiscreteItem extends St.BoxLayout {
constructor() {
super({
style_class: 'popup-menu-item',
reactive: true,
});
this._levelButtons = new Map();
this._addLevelButton('off', _('Off'), 'keyboard-brightness-off-symbolic');
this._addLevelButton('low', _('Low'), 'keyboard-brightness-medium-symbolic');
this._addLevelButton('high', _('High'), 'keyboard-brightness-high-symbolic');
this.connect('notify::n-levels', () => this._syncLevels());
this.connect('notify::value', () => this._syncChecked());
this._syncLevels();
}
_valueToLevel(value) {
const checkedIndex = Math.round(value * (this.nLevels - 1) / 100);
if (checkedIndex === this.nLevels - 1)
return 'high';
return [...this._levelButtons.keys()].at(checkedIndex);
}
_levelToValue(level) {
const keyIndex = [...this._levelButtons.keys()].indexOf(level);
return 100 * Math.min(keyIndex, this.nLevels - 1) / (this.nLevels - 1);
}
_addLevelButton(key, label, iconName) {
const box = new St.BoxLayout({
style_class: 'keyboard-brightness-level',
vertical: true,
x_expand: true,
});
box.button = new St.Button({
styleClass: 'icon-button',
canFocus: true,
iconName,
});
box.add_child(box.button);
box.button.connect('clicked', () => {
this.value = this._levelToValue(key);
});
box.add_child(new St.Label({
text: label,
x_align: Clutter.ActorAlign.CENTER,
}));
this.add_child(box);
this._levelButtons.set(key, box);
}
_syncLevels() {
this._levelButtons.get('off').visible = this.nLevels > 0;
this._levelButtons.get('high').visible = this.nLevels > 1;
this._levelButtons.get('low').visible = this.nLevels > 2;
this._syncChecked();
}
_syncChecked() {
const checkedKey = this._valueToLevel(this.value);
this._levelButtons.forEach((b, k) => {
b.button.checked = k === checkedKey;
});
}
});
const KeyboardBrightnessToggle = GObject.registerClass(
class KeyboardBrightnessToggle extends QuickMenuToggle {
_init() {
super._init({
title: _('Keyboard'),
iconName: 'display-brightness-symbolic',
});
this._proxy = new BrightnessProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
(proxy, error) => {
if (error)
console.error(error.message);
else
this._proxy.connect('g-properties-changed', () => this._sync());
this._sync();
});
this.connect('clicked', () => {
this._proxy.Brightness = this.checked ? 0 : 100;
});
this._sliderItem = new SliderItem();
this.menu.box.add_child(this._sliderItem);
this._discreteItem = new DiscreteItem();
this.menu.box.add_child(this._discreteItem);
this._sliderItem.bind_property('visible',
this._discreteItem, 'visible',
GObject.BindingFlags.INVERT_BOOLEAN |
GObject.BindingFlags.SYNC_CREATE);
this._sliderItem.bind_property('value',
this._discreteItem, 'value',
GObject.BindingFlags.SYNC_CREATE);
this._sliderItemChangedId = this._sliderItem.connect('notify::value', () => {
if (this._sliderItem.visible)
this._proxy.Brightness = this._sliderItem.value;
});
this._discreteItemChangedId = this._discreteItem.connect('notify::value', () => {
if (this._discreteItem.visible)
this._proxy.Brightness = this._discreteItem.value;
});
}
_sync() {
const brightness = this._proxy.Brightness;
const visible = Number.isInteger(brightness) && brightness >= 0;
this.visible = visible;
if (!visible)
return;
this.checked = brightness > 0;
const useSlider = this._proxy.Steps >= 4;
this._sliderItem.block_signal_handler(this._sliderItemChangedId);
this._discreteItem.block_signal_handler(this._discreteItemChangedId);
this._sliderItem.set({
visible: useSlider,
value: brightness,
});
if (!useSlider)
this._discreteItem.nLevels = this._proxy.Steps;
this._sliderItem.unblock_signal_handler(this._sliderItemChangedId);
this._discreteItem.unblock_signal_handler(this._discreteItemChangedId);
}
});
export const Indicator = GObject.registerClass(
class Indicator extends SystemIndicator {
_init() {
super._init();
this.quickSettingsItems.push(new KeyboardBrightnessToggle());
}
});