2653402c5c
meta_later_add() is modelled after g_idle_add() and friends, and the handler's boolean return value determines whether it should be scheduled again or removed. There are some places where we omit the return value, add them (although the implicit return value of "undefined" already gives us the intended result). https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/637
191 lines
6.3 KiB
JavaScript
191 lines
6.3 KiB
JavaScript
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
|
|
|
|
const Main = imports.ui.main;
|
|
const ModalDialog = imports.ui.modalDialog;
|
|
|
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
|
|
|
var AudioDevice = {
|
|
HEADPHONES: 1 << 0,
|
|
HEADSET: 1 << 1,
|
|
MICROPHONE: 1 << 2
|
|
};
|
|
|
|
const AudioDeviceSelectionIface = loadInterfaceXML('org.gnome.Shell.AudioDeviceSelection');
|
|
|
|
var AudioDeviceSelectionDialog = GObject.registerClass({
|
|
Signals: { 'device-selected': { param_types: [GObject.TYPE_UINT] } }
|
|
}, class AudioDeviceSelectionDialog extends ModalDialog.ModalDialog {
|
|
_init(devices) {
|
|
super._init({ styleClass: 'audio-device-selection-dialog' });
|
|
|
|
this._deviceItems = {};
|
|
|
|
this._buildLayout();
|
|
|
|
if (devices & AudioDevice.HEADPHONES)
|
|
this._addDevice(AudioDevice.HEADPHONES);
|
|
if (devices & AudioDevice.HEADSET)
|
|
this._addDevice(AudioDevice.HEADSET);
|
|
if (devices & AudioDevice.MICROPHONE)
|
|
this._addDevice(AudioDevice.MICROPHONE);
|
|
|
|
if (this._selectionBox.get_n_children() < 2)
|
|
throw new Error('Too few devices for a selection');
|
|
}
|
|
|
|
_buildLayout(devices) {
|
|
let title = new St.Label({ style_class: 'audio-selection-title',
|
|
text: _("Select Audio Device"),
|
|
x_align: Clutter.ActorAlign.CENTER });
|
|
|
|
this.contentLayout.style_class = 'audio-selection-content';
|
|
this.contentLayout.add(title);
|
|
|
|
this._selectionBox = new St.BoxLayout({ style_class: 'audio-selection-box' });
|
|
this.contentLayout.add(this._selectionBox, { expand: true });
|
|
|
|
if (Main.sessionMode.allowSettings)
|
|
this.addButton({ action: this._openSettings.bind(this),
|
|
label: _("Sound Settings") });
|
|
this.addButton({ action: this.close.bind(this),
|
|
label: _("Cancel"),
|
|
key: Clutter.Escape });
|
|
}
|
|
|
|
_getDeviceLabel(device) {
|
|
switch (device) {
|
|
case AudioDevice.HEADPHONES:
|
|
return _("Headphones");
|
|
case AudioDevice.HEADSET:
|
|
return _("Headset");
|
|
case AudioDevice.MICROPHONE:
|
|
return _("Microphone");
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
_getDeviceIcon(device) {
|
|
switch (device) {
|
|
case AudioDevice.HEADPHONES:
|
|
return 'audio-headphones-symbolic';
|
|
case AudioDevice.HEADSET:
|
|
return 'audio-headset-symbolic';
|
|
case AudioDevice.MICROPHONE:
|
|
return 'audio-input-microphone-symbolic';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
_addDevice(device) {
|
|
let box = new St.BoxLayout({ style_class: 'audio-selection-device-box',
|
|
vertical: true });
|
|
box.connect('notify::height', () => {
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
|
box.width = box.height;
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
});
|
|
|
|
let icon = new St.Icon({ style_class: 'audio-selection-device-icon',
|
|
icon_name: this._getDeviceIcon(device) });
|
|
box.add(icon);
|
|
|
|
let label = new St.Label({ style_class: 'audio-selection-device-label',
|
|
text: this._getDeviceLabel(device),
|
|
x_align: Clutter.ActorAlign.CENTER });
|
|
box.add(label);
|
|
|
|
let button = new St.Button({ style_class: 'audio-selection-device',
|
|
can_focus: true,
|
|
child: box });
|
|
this._selectionBox.add(button);
|
|
|
|
button.connect('clicked', () => {
|
|
this.emit('device-selected', device);
|
|
this.close();
|
|
Main.overview.hide();
|
|
});
|
|
}
|
|
|
|
_openSettings() {
|
|
let desktopFile = 'gnome-sound-panel.desktop';
|
|
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
|
|
|
|
if (!app) {
|
|
log(`Settings panel for desktop file ${desktopFile} could not be loaded!`);
|
|
return;
|
|
}
|
|
|
|
this.close();
|
|
Main.overview.hide();
|
|
app.activate();
|
|
}
|
|
});
|
|
|
|
var AudioDeviceSelectionDBus = class AudioDeviceSelectionDBus {
|
|
constructor() {
|
|
this._audioSelectionDialog = null;
|
|
|
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AudioDeviceSelectionIface, this);
|
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/AudioDeviceSelection');
|
|
|
|
Gio.DBus.session.own_name('org.gnome.Shell.AudioDeviceSelection', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
|
}
|
|
|
|
_onDialogClosed() {
|
|
this._audioSelectionDialog = null;
|
|
}
|
|
|
|
_onDeviceSelected(dialog, device) {
|
|
let connection = this._dbusImpl.get_connection();
|
|
let info = this._dbusImpl.get_info();
|
|
let deviceName = Object.keys(AudioDevice).filter(
|
|
dev => AudioDevice[dev] == device
|
|
)[0].toLowerCase();
|
|
connection.emit_signal(this._audioSelectionDialog._sender,
|
|
this._dbusImpl.get_object_path(),
|
|
info ? info.name : null,
|
|
'DeviceSelected',
|
|
GLib.Variant.new('(s)', [deviceName]));
|
|
}
|
|
|
|
OpenAsync(params, invocation) {
|
|
if (this._audioSelectionDialog) {
|
|
invocation.return_value(null);
|
|
return;
|
|
}
|
|
|
|
let [deviceNames] = params;
|
|
let devices = 0;
|
|
deviceNames.forEach(n => devices |= AudioDevice[n.toUpperCase()]);
|
|
|
|
let dialog;
|
|
try {
|
|
dialog = new AudioDeviceSelectionDialog(devices);
|
|
} catch (e) {
|
|
invocation.return_value(null);
|
|
return;
|
|
}
|
|
dialog._sender = invocation.get_sender();
|
|
|
|
dialog.connect('closed', this._onDialogClosed.bind(this));
|
|
dialog.connect('device-selected',
|
|
this._onDeviceSelected.bind(this));
|
|
dialog.open();
|
|
|
|
this._audioSelectionDialog = dialog;
|
|
invocation.return_value(null);
|
|
}
|
|
|
|
CloseAsync(params, invocation) {
|
|
if (this._audioSelectionDialog &&
|
|
this._audioSelectionDialog._sender == invocation.get_sender())
|
|
this._audioSelectionDialog.close();
|
|
|
|
invocation.return_value(null);
|
|
}
|
|
};
|