audioDeviceSelection: Add audio device selection dialog
It is not always possible to determine the type of audio device that got plugged in. Add a system modal dialog to query the user in that case and export in on the bus to gnome-settings-daemon. https://bugzilla.gnome.org/show_bug.cgi?id=760284
This commit is contained in:
parent
a13357c2a8
commit
30c7545ff3
@ -399,6 +399,34 @@ StScrollBar {
|
|||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px; }
|
height: 48px; }
|
||||||
|
|
||||||
|
/* Audio selection dialog */
|
||||||
|
.audio-device-selection-dialog {
|
||||||
|
spacing: 30px; }
|
||||||
|
|
||||||
|
.audio-selection-content {
|
||||||
|
spacing: 20px;
|
||||||
|
padding: 24px; }
|
||||||
|
|
||||||
|
.audio-selection-title {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center; }
|
||||||
|
|
||||||
|
.audio-selection-box {
|
||||||
|
spacing: 20px; }
|
||||||
|
|
||||||
|
.audio-selection-device {
|
||||||
|
border: 1px solid rgba(238, 238, 236, 0.2);
|
||||||
|
border-radius: 12px; }
|
||||||
|
.audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus {
|
||||||
|
background-color: #215d9c; }
|
||||||
|
|
||||||
|
.audio-selection-device-box {
|
||||||
|
padding: 20px;
|
||||||
|
spacing: 20px; }
|
||||||
|
|
||||||
|
.audio-selection-device-icon {
|
||||||
|
icon-size: 64px; }
|
||||||
|
|
||||||
/* Network Agent Dialog */
|
/* Network Agent Dialog */
|
||||||
.network-dialog-secret-table {
|
.network-dialog-secret-table {
|
||||||
spacing-rows: 15px;
|
spacing-rows: 15px;
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit c67499686eb40b821784561fbb0596cc470d8017
|
Subproject commit 9fb3918831459cd002f3d621494cf5eac70fe46a
|
@ -399,6 +399,34 @@ StScrollBar {
|
|||||||
width: 48px;
|
width: 48px;
|
||||||
height: 48px; }
|
height: 48px; }
|
||||||
|
|
||||||
|
/* Audio selection dialog */
|
||||||
|
.audio-device-selection-dialog {
|
||||||
|
spacing: 30px; }
|
||||||
|
|
||||||
|
.audio-selection-content {
|
||||||
|
spacing: 20px;
|
||||||
|
padding: 24px; }
|
||||||
|
|
||||||
|
.audio-selection-title {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center; }
|
||||||
|
|
||||||
|
.audio-selection-box {
|
||||||
|
spacing: 20px; }
|
||||||
|
|
||||||
|
.audio-selection-device {
|
||||||
|
border: 1px solid rgba(238, 238, 236, 0.2);
|
||||||
|
border-radius: 12px; }
|
||||||
|
.audio-selection-device:active, .audio-selection-device:hover, .audio-selection-device:focus {
|
||||||
|
background-color: #215d9c; }
|
||||||
|
|
||||||
|
.audio-selection-device-box {
|
||||||
|
padding: 20px;
|
||||||
|
spacing: 20px; }
|
||||||
|
|
||||||
|
.audio-selection-device-icon {
|
||||||
|
icon-size: 64px; }
|
||||||
|
|
||||||
/* Network Agent Dialog */
|
/* Network Agent Dialog */
|
||||||
.network-dialog-secret-table {
|
.network-dialog-secret-table {
|
||||||
spacing-rows: 15px;
|
spacing-rows: 15px;
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<file>ui/animation.js</file>
|
<file>ui/animation.js</file>
|
||||||
<file>ui/appDisplay.js</file>
|
<file>ui/appDisplay.js</file>
|
||||||
<file>ui/appFavorites.js</file>
|
<file>ui/appFavorites.js</file>
|
||||||
|
<file>ui/audioDeviceSelection.js</file>
|
||||||
<file>ui/backgroundMenu.js</file>
|
<file>ui/backgroundMenu.js</file>
|
||||||
<file>ui/background.js</file>
|
<file>ui/background.js</file>
|
||||||
<file>ui/boxpointer.js</file>
|
<file>ui/boxpointer.js</file>
|
||||||
|
216
js/ui/audioDeviceSelection.js
Normal file
216
js/ui/audioDeviceSelection.js
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
@ -11,6 +11,7 @@ const Meta = imports.gi.Meta;
|
|||||||
const Shell = imports.gi.Shell;
|
const Shell = imports.gi.Shell;
|
||||||
const St = imports.gi.St;
|
const St = imports.gi.St;
|
||||||
|
|
||||||
|
const AudioDeviceSelection = imports.ui.audioDeviceSelection;
|
||||||
const Components = imports.ui.components;
|
const Components = imports.ui.components;
|
||||||
const CtrlAltTab = imports.ui.ctrlAltTab;
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
||||||
const EndSessionDialog = imports.ui.endSessionDialog;
|
const EndSessionDialog = imports.ui.endSessionDialog;
|
||||||
@ -62,6 +63,7 @@ let ctrlAltTabManager = null;
|
|||||||
let osdWindowManager = null;
|
let osdWindowManager = null;
|
||||||
let osdMonitorLabeler = null;
|
let osdMonitorLabeler = null;
|
||||||
let sessionMode = null;
|
let sessionMode = null;
|
||||||
|
let shellAudioSelectionDBusService = null;
|
||||||
let shellDBusService = null;
|
let shellDBusService = null;
|
||||||
let shellMountOpDBusService = null;
|
let shellMountOpDBusService = null;
|
||||||
let screenSaverDBus = null;
|
let screenSaverDBus = null;
|
||||||
@ -120,6 +122,7 @@ function start() {
|
|||||||
_loadDefaultStylesheet);
|
_loadDefaultStylesheet);
|
||||||
_initializeUI();
|
_initializeUI();
|
||||||
|
|
||||||
|
shellAudioSelectionDBusService = new AudioDeviceSelection.AudioDeviceSelectionDBus();
|
||||||
shellDBusService = new ShellDBus.GnomeShell();
|
shellDBusService = new ShellDBus.GnomeShell();
|
||||||
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user