2016-02-09 22:03:26 +00:00
|
|
|
const Clutter = imports.gi.Clutter;
|
|
|
|
const Gio = imports.gi.Gio;
|
|
|
|
const GLib = imports.gi.GLib;
|
|
|
|
const Lang = imports.lang;
|
|
|
|
const Meta = imports.gi.Meta;
|
|
|
|
const Shell = imports.gi.Shell;
|
|
|
|
const St = imports.gi.St;
|
|
|
|
|
|
|
|
const Main = imports.ui.main;
|
|
|
|
const ModalDialog = imports.ui.modalDialog;
|
|
|
|
|
2017-07-18 17:47:27 +00:00
|
|
|
var AudioDevice = {
|
2016-02-09 22:03:26 +00:00
|
|
|
HEADPHONES: 1 << 0,
|
|
|
|
HEADSET: 1 << 1,
|
|
|
|
MICROPHONE: 1 << 2
|
|
|
|
};
|
|
|
|
|
|
|
|
const AudioDeviceSelectionIface = '<node> \
|
|
|
|
<interface name="org.gnome.Shell.AudioDeviceSelection"> \
|
|
|
|
<method name="Open"> \
|
|
|
|
<arg name="devices" direction="in" type="as" /> \
|
|
|
|
</method> \
|
|
|
|
<method name="Close"> \
|
|
|
|
</method> \
|
|
|
|
<signal name="DeviceSelected"> \
|
|
|
|
<arg name="device" type="s" /> \
|
|
|
|
</signal> \
|
|
|
|
</interface> \
|
|
|
|
</node>';
|
|
|
|
|
2017-07-18 17:41:25 +00:00
|
|
|
var AudioDeviceSelectionDialog = new Lang.Class({
|
2016-02-09 22:03:26 +00:00
|
|
|
Name: 'AudioDeviceSelectionDialog',
|
|
|
|
Extends: ModalDialog.ModalDialog,
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_init(devices) {
|
2016-02-09 22:03:26 +00:00
|
|
|
this.parent({ 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');
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
destroy() {
|
2016-02-09 22:03:26 +00:00
|
|
|
this.parent();
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_buildLayout(devices) {
|
2016-02-09 22:03:26 +00:00
|
|
|
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 });
|
|
|
|
|
2017-12-02 00:27:35 +00:00
|
|
|
this.addButton({ action: this._openSettings.bind(this),
|
2016-02-09 22:03:26 +00:00
|
|
|
label: _("Sound Settings") });
|
2017-12-02 00:27:35 +00:00
|
|
|
this.addButton({ action: this.close.bind(this),
|
2016-02-09 22:03:26 +00:00
|
|
|
label: _("Cancel"),
|
|
|
|
key: Clutter.Escape });
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_getDeviceLabel(device) {
|
2016-02-09 22:03:26 +00:00
|
|
|
switch(device) {
|
|
|
|
case AudioDevice.HEADPHONES:
|
|
|
|
return _("Headphones");
|
|
|
|
case AudioDevice.HEADSET:
|
|
|
|
return _("Headset");
|
|
|
|
case AudioDevice.MICROPHONE:
|
|
|
|
return _("Microphone");
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_getDeviceIcon(device) {
|
2016-02-09 22:03:26 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_addDevice(device) {
|
2016-02-09 22:03:26 +00:00
|
|
|
let box = new St.BoxLayout({ style_class: 'audio-selection-device-box',
|
|
|
|
vertical: true });
|
2017-10-31 00:38:18 +00:00
|
|
|
box.connect('notify::height', () => {
|
|
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
|
|
|
box.width = box.height;
|
2016-02-09 22:03:26 +00:00
|
|
|
});
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2016-02-09 22:03:26 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2017-10-31 00:38:18 +00:00
|
|
|
button.connect('clicked', () => {
|
|
|
|
this.emit('device-selected', device);
|
|
|
|
this.close();
|
|
|
|
Main.overview.hide();
|
|
|
|
});
|
2016-02-09 22:03:26 +00:00
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_openSettings() {
|
2016-02-09 22:03:26 +00:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2017-07-18 17:41:25 +00:00
|
|
|
var AudioDeviceSelectionDBus = new Lang.Class({
|
2016-02-09 22:03:26 +00:00
|
|
|
Name: 'AudioDeviceSelectionDBus',
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_init() {
|
2016-02-09 22:03:26 +00:00
|
|
|
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);
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDialogClosed() {
|
2016-02-09 22:03:26 +00:00
|
|
|
this._audioSelectionDialog = null;
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onDeviceSelected(dialog, device) {
|
2016-02-09 22:03:26 +00:00
|
|
|
let connection = this._dbusImpl.get_connection();
|
|
|
|
let info = this._dbusImpl.get_info();
|
|
|
|
let deviceName = Object.keys(AudioDevice).filter(
|
2017-10-31 00:38:18 +00:00
|
|
|
dev => AudioDevice[dev] == device
|
|
|
|
)[0].toLowerCase();
|
2016-02-09 22:03:26 +00:00
|
|
|
connection.emit_signal(this._audioSelectionDialog._sender,
|
|
|
|
this._dbusImpl.get_object_path(),
|
|
|
|
info ? info.name : null,
|
|
|
|
'DeviceSelected',
|
|
|
|
GLib.Variant.new('(s)', [deviceName]));
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
OpenAsync(params, invocation) {
|
2016-02-09 22:03:26 +00:00
|
|
|
if (this._audioSelectionDialog) {
|
|
|
|
invocation.return_value(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let [deviceNames] = params;
|
|
|
|
let devices = 0;
|
2017-10-31 00:38:18 +00:00
|
|
|
deviceNames.forEach(n => { devices |= AudioDevice[n.toUpperCase()]; });
|
2016-02-09 22:03:26 +00:00
|
|
|
|
|
|
|
let dialog;
|
|
|
|
try {
|
|
|
|
dialog = new AudioDeviceSelectionDialog(devices);
|
|
|
|
} catch(e) {
|
|
|
|
invocation.return_value(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dialog._sender = invocation.get_sender();
|
|
|
|
|
2017-12-02 00:27:35 +00:00
|
|
|
dialog.connect('closed', this._onDialogClosed.bind(this));
|
2016-02-09 22:03:26 +00:00
|
|
|
dialog.connect('device-selected',
|
2017-12-02 00:27:35 +00:00
|
|
|
this._onDeviceSelected.bind(this));
|
2016-02-09 22:03:26 +00:00
|
|
|
dialog.open();
|
|
|
|
|
|
|
|
this._audioSelectionDialog = dialog;
|
|
|
|
invocation.return_value(null);
|
|
|
|
},
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
CloseAsync(params, invocation) {
|
2016-02-09 22:03:26 +00:00
|
|
|
if (this._audioSelectionDialog &&
|
|
|
|
this._audioSelectionDialog._sender == invocation.get_sender())
|
|
|
|
this._audioSelectionDialog.close();
|
|
|
|
|
|
|
|
invocation.return_value(null);
|
|
|
|
}
|
|
|
|
});
|