217 lines
6.9 KiB
JavaScript
217 lines
6.9 KiB
JavaScript
|
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;
|
||
|
|
||
|
const AudioDevice = {
|
||
|
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>';
|
||
|
|
||
|
const AudioDeviceSelectionDialog = new Lang.Class({
|
||
|
Name: 'AudioDeviceSelectionDialog',
|
||
|
Extends: ModalDialog.ModalDialog,
|
||
|
|
||
|
_init: function(devices) {
|
||
|
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');
|
||
|
},
|
||
|
|
||
|
destroy: function() {
|
||
|
this.parent();
|
||
|
},
|
||
|
|
||
|
_buildLayout: function(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 });
|
||
|
|
||
|
this.addButton({ action: Lang.bind(this, this._openSettings),
|
||
|
label: _("Sound Settings") });
|
||
|
this.addButton({ action: Lang.bind(this, this.close),
|
||
|
label: _("Cancel"),
|
||
|
key: Clutter.Escape });
|
||
|
},
|
||
|
|
||
|
_getDeviceLabel: function(device) {
|
||
|
switch(device) {
|
||
|
case AudioDevice.HEADPHONES:
|
||
|
return _("Headphones");
|
||
|
case AudioDevice.HEADSET:
|
||
|
return _("Headset");
|
||
|
case AudioDevice.MICROPHONE:
|
||
|
return _("Microphone");
|
||
|
default:
|
||
|
return null;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
_getDeviceIcon: function(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: function(device) {
|
||
|
let box = new St.BoxLayout({ style_class: 'audio-selection-device-box',
|
||
|
vertical: true });
|
||
|
box.connect('notify::height',
|
||
|
function() {
|
||
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
|
||
|
function() {
|
||
|
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', Lang.bind(this,
|
||
|
function() {
|
||
|
this.emit('device-selected', device);
|
||
|
this.close();
|
||
|
Main.overview.hide();
|
||
|
}));
|
||
|
},
|
||
|
|
||
|
_openSettings: function() {
|
||
|
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();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
const AudioDeviceSelectionDBus = new Lang.Class({
|
||
|
Name: 'AudioDeviceSelectionDBus',
|
||
|
|
||
|
_init: function() {
|
||
|
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: function() {
|
||
|
this._audioSelectionDialog = null;
|
||
|
},
|
||
|
|
||
|
_onDeviceSelected: function(dialog, device) {
|
||
|
let connection = this._dbusImpl.get_connection();
|
||
|
let info = this._dbusImpl.get_info();
|
||
|
let deviceName = Object.keys(AudioDevice).filter(
|
||
|
function(dev) {
|
||
|
return 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: function(params, invocation) {
|
||
|
if (this._audioSelectionDialog) {
|
||
|
invocation.return_value(null);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let [deviceNames] = params;
|
||
|
let devices = 0;
|
||
|
deviceNames.forEach(function(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', Lang.bind(this, this._onDialogClosed));
|
||
|
dialog.connect('device-selected',
|
||
|
Lang.bind(this, this._onDeviceSelected));
|
||
|
dialog.open();
|
||
|
|
||
|
this._audioSelectionDialog = dialog;
|
||
|
invocation.return_value(null);
|
||
|
},
|
||
|
|
||
|
CloseAsync: function(params, invocation) {
|
||
|
if (this._audioSelectionDialog &&
|
||
|
this._audioSelectionDialog._sender == invocation.get_sender())
|
||
|
this._audioSelectionDialog.close();
|
||
|
|
||
|
invocation.return_value(null);
|
||
|
}
|
||
|
});
|