463757569a
There's no good reason for waiting for the proxy to be initialized to connect signals. In fact, connecting the signal beforehand ensures that the handler is in place when the proxy fetches the properties, so we don't have to call the handler explicitly. That in turn allows us to rely on the signal parameters to only process changed properties. To achieve that, construct the proxy manually, and then initialize it asynchronously in a Promise. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2391>
180 lines
6.3 KiB
JavaScript
180 lines
6.3 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
/* exported Indicator */
|
|
|
|
const { Gio, GLib, GnomeBluetooth, GObject } = imports.gi;
|
|
|
|
const Main = imports.ui.main;
|
|
const PanelMenu = imports.ui.panelMenu;
|
|
const PopupMenu = imports.ui.popupMenu;
|
|
|
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
|
|
|
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
|
|
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
|
|
|
|
const RfkillManagerInterface = loadInterfaceXML('org.gnome.SettingsDaemon.Rfkill');
|
|
const rfkillManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(RfkillManagerInterface);
|
|
|
|
const HAD_BLUETOOTH_DEVICES_SETUP = 'had-bluetooth-devices-setup';
|
|
|
|
var Indicator = GObject.registerClass(
|
|
class Indicator extends PanelMenu.SystemIndicator {
|
|
_init() {
|
|
super._init();
|
|
|
|
this._indicator = this._addIndicator();
|
|
this._indicator.icon_name = 'bluetooth-active-symbolic';
|
|
this._hadSetupDevices = global.settings.get_boolean(HAD_BLUETOOTH_DEVICES_SETUP);
|
|
|
|
this._client = new GnomeBluetooth.Client();
|
|
this._client.connect('notify::default-adapter', () => {
|
|
const newAdapter = this._client.default_adapter ?? null;
|
|
|
|
if (newAdapter && this._adapter)
|
|
this._setHadSetupDevices(this._getDeviceInfos().length > 0);
|
|
|
|
this._adapter = newAdapter;
|
|
|
|
this._deviceNotifyConnected.clear();
|
|
this._sync();
|
|
});
|
|
this._client.connect('notify::default-adapter-powered', this._sync.bind(this));
|
|
|
|
this._proxy = new Gio.DBusProxy({
|
|
g_connection: Gio.DBus.session,
|
|
g_name: BUS_NAME,
|
|
g_object_path: OBJECT_PATH,
|
|
g_interface_name: rfkillManagerInfo.name,
|
|
g_interface_info: rfkillManagerInfo,
|
|
});
|
|
this._proxy.connect('g-properties-changed', (p, properties) => {
|
|
if ('BluetoothHardwareAirplaneMode' in properties.unpack())
|
|
this._sync();
|
|
});
|
|
this._proxy.init_async(GLib.PRIORITY_DEFAULT, null)
|
|
.catch(e => console.error(e.message));
|
|
|
|
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
|
|
|
|
this._toggleItem = new PopupMenu.PopupMenuItem('');
|
|
this._toggleItem.connect('activate', () => {
|
|
if (!this._client.default_adapter_powered) {
|
|
this._proxy.BluetoothAirplaneMode = false;
|
|
this._client.default_adapter_powered = true;
|
|
} else {
|
|
this._proxy.BluetoothAirplaneMode = true;
|
|
}
|
|
});
|
|
this._item.menu.addMenuItem(this._toggleItem);
|
|
|
|
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
|
|
this.menu.addMenuItem(this._item);
|
|
|
|
this._syncId = 0;
|
|
this._adapter = null;
|
|
|
|
this._deviceNotifyConnected = new Set();
|
|
|
|
const deviceStore = this._client.get_devices();
|
|
for (let i = 0; i < deviceStore.get_n_items(); i++)
|
|
this._connectDeviceNotify(deviceStore.get_item(i));
|
|
|
|
this._client.connect('device-removed', (c, path) => {
|
|
this._deviceNotifyConnected.delete(path);
|
|
this._queueSync.bind(this);
|
|
});
|
|
this._client.connect('device-added', (c, device) => {
|
|
this._connectDeviceNotify(device);
|
|
this._sync();
|
|
});
|
|
Main.sessionMode.connect('updated', this._sync.bind(this));
|
|
this._sync();
|
|
}
|
|
|
|
_setHadSetupDevices(value) {
|
|
if (this._hadSetupDevices === value)
|
|
return;
|
|
|
|
this._hadSetupDevices = value;
|
|
global.settings.set_boolean(
|
|
HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices);
|
|
}
|
|
|
|
_connectDeviceNotify(device) {
|
|
const path = device.get_object_path();
|
|
|
|
if (this._deviceNotifyConnected.has(path))
|
|
return;
|
|
|
|
device.connect('notify::alias', this._queueSync.bind(this));
|
|
device.connect('notify::paired', this._queueSync.bind(this));
|
|
device.connect('notify::trusted', this._queueSync.bind(this));
|
|
device.connect('notify::connected', this._queueSync.bind(this));
|
|
|
|
this._deviceNotifyConnected.add(path);
|
|
}
|
|
|
|
_getDeviceInfos() {
|
|
const deviceStore = this._client.get_devices();
|
|
let deviceInfos = [];
|
|
|
|
for (let i = 0; i < deviceStore.get_n_items(); i++) {
|
|
const device = deviceStore.get_item(i);
|
|
|
|
if (device.paired || device.trusted) {
|
|
deviceInfos.push({
|
|
connected: device.connected,
|
|
name: device.alias,
|
|
});
|
|
}
|
|
}
|
|
|
|
return deviceInfos;
|
|
}
|
|
|
|
_queueSync() {
|
|
if (this._syncId)
|
|
return;
|
|
this._syncId = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => {
|
|
this._syncId = 0;
|
|
this._sync();
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
}
|
|
|
|
_sync() {
|
|
const devices = this._getDeviceInfos();
|
|
const connectedDevices = devices.filter(dev => dev.connected);
|
|
const nConnectedDevices = connectedDevices.length;
|
|
|
|
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
|
|
|
|
this.menu.setSensitive(sensitive);
|
|
this._indicator.visible = nConnectedDevices > 0;
|
|
|
|
const adapterPowered = this._client.default_adapter_powered;
|
|
|
|
// Remember if there were setup devices and show the menu
|
|
// if we've seen setup devices and we're not hard blocked
|
|
if (this._hadSetupDevices)
|
|
this._item.visible = !this._proxy.BluetoothHardwareAirplaneMode;
|
|
else
|
|
this._item.visible = adapterPowered;
|
|
|
|
this._item.icon.icon_name = adapterPowered
|
|
? 'bluetooth-active-symbolic' : 'bluetooth-disabled-symbolic';
|
|
|
|
if (nConnectedDevices > 1)
|
|
/* Translators: this is the number of connected bluetooth devices */
|
|
this._item.label.text = ngettext('%d Connected', '%d Connected', nConnectedDevices).format(nConnectedDevices);
|
|
else if (nConnectedDevices === 1)
|
|
this._item.label.text = connectedDevices[0].name;
|
|
else if (adapterPowered)
|
|
this._item.label.text = _('Bluetooth On');
|
|
else
|
|
this._item.label.text = _('Bluetooth Off');
|
|
|
|
this._toggleItem.label.text = adapterPowered ? _('Turn Off') : _('Turn On');
|
|
}
|
|
});
|