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