2023-07-10 05:53:00 -04:00
|
|
|
import Clutter from 'gi://Clutter';
|
|
|
|
import Gio from 'gi://Gio';
|
|
|
|
import GLib from 'gi://GLib';
|
|
|
|
import GObject from 'gi://GObject';
|
|
|
|
import Meta from 'gi://Meta';
|
|
|
|
import Shell from 'gi://Shell';
|
|
|
|
import St from 'gi://St';
|
|
|
|
|
|
|
|
import * as Dialog from './dialog.js';
|
|
|
|
import * as ModalDialog from './modalDialog.js';
|
|
|
|
|
|
|
|
import * as Main from './main.js';
|
|
|
|
import {loadInterfaceXML} from '../misc/fileUtils.js';
|
|
|
|
|
|
|
|
const AudioDevice = {
|
2016-02-09 17:03:26 -05:00
|
|
|
HEADPHONES: 1 << 0,
|
|
|
|
HEADSET: 1 << 1,
|
2019-08-20 17:43:54 -04:00
|
|
|
MICROPHONE: 1 << 2,
|
2016-02-09 17:03:26 -05:00
|
|
|
};
|
|
|
|
|
2018-09-05 20:55:20 -04:00
|
|
|
const AudioDeviceSelectionIface = loadInterfaceXML('org.gnome.Shell.AudioDeviceSelection');
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2023-07-10 05:53:00 -04:00
|
|
|
const AudioDeviceSelectionDialog = GObject.registerClass({
|
2023-08-06 18:40:20 -04:00
|
|
|
Signals: {'device-selected': {param_types: [GObject.TYPE_UINT]}},
|
2019-05-23 16:45:44 -04:00
|
|
|
}, class AudioDeviceSelectionDialog extends ModalDialog.ModalDialog {
|
|
|
|
_init(devices) {
|
2023-08-06 18:40:20 -04:00
|
|
|
super._init({styleClass: 'audio-device-selection-dialog'});
|
2016-02-09 17:03:26 -05:00
|
|
|
|
|
|
|
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-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2019-02-04 06:30:53 -05:00
|
|
|
_buildLayout() {
|
2020-01-27 16:30:12 -05:00
|
|
|
let content = new Dialog.MessageDialogContent({
|
|
|
|
title: _('Select Audio Device'),
|
|
|
|
});
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2019-10-21 14:44:00 -04:00
|
|
|
this._selectionBox = new St.BoxLayout({
|
|
|
|
style_class: 'audio-selection-box',
|
2020-10-20 21:29:18 -04:00
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
2019-10-21 14:44:00 -04:00
|
|
|
x_expand: true,
|
|
|
|
});
|
2020-01-27 16:30:12 -05:00
|
|
|
content.add_child(this._selectionBox);
|
|
|
|
|
|
|
|
this.contentLayout.add_child(content);
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2019-08-19 20:51:42 -04:00
|
|
|
if (Main.sessionMode.allowSettings) {
|
2021-04-19 11:46:23 -04:00
|
|
|
this.addButton({
|
|
|
|
action: this._openSettings.bind(this),
|
|
|
|
label: _('Sound Settings'),
|
|
|
|
});
|
2019-08-19 20:51:42 -04:00
|
|
|
}
|
2021-04-19 11:46:23 -04:00
|
|
|
this.addButton({
|
2021-04-19 10:01:19 -04:00
|
|
|
action: () => this.close(),
|
2021-04-19 11:46:23 -04:00
|
|
|
label: _('Cancel'),
|
|
|
|
key: Clutter.KEY_Escape,
|
|
|
|
});
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_getDeviceLabel(device) {
|
2019-01-28 20:27:05 -05:00
|
|
|
switch (device) {
|
2019-02-01 07:21:00 -05:00
|
|
|
case AudioDevice.HEADPHONES:
|
2023-08-06 18:34:20 -04:00
|
|
|
return _('Headphones');
|
2019-02-01 07:21:00 -05:00
|
|
|
case AudioDevice.HEADSET:
|
2023-08-06 18:34:20 -04:00
|
|
|
return _('Headset');
|
2019-02-01 07:21:00 -05:00
|
|
|
case AudioDevice.MICROPHONE:
|
2023-08-06 18:34:20 -04:00
|
|
|
return _('Microphone');
|
2019-02-01 07:21:00 -05:00
|
|
|
default:
|
|
|
|
return null;
|
2016-02-09 17:03:26 -05:00
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_getDeviceIcon(device) {
|
2019-01-28 20:27:05 -05:00
|
|
|
switch (device) {
|
2019-02-01 07:21:00 -05:00
|
|
|
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;
|
2016-02-09 17:03:26 -05:00
|
|
|
}
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_addDevice(device) {
|
2020-03-29 17:51:13 -04:00
|
|
|
const box = new St.BoxLayout({
|
|
|
|
style_class: 'audio-selection-device-box',
|
|
|
|
vertical: true,
|
|
|
|
});
|
2017-10-30 20:38:18 -04:00
|
|
|
box.connect('notify::height', () => {
|
2022-09-07 14:23:38 -04:00
|
|
|
const laters = global.compositor.get_laters();
|
|
|
|
laters.add(Meta.LaterType.BEFORE_REDRAW, () => {
|
2017-10-30 20:38:18 -04:00
|
|
|
box.width = box.height;
|
2019-07-22 14:37:33 -04:00
|
|
|
return GLib.SOURCE_REMOVE;
|
2016-02-09 17:03:26 -05:00
|
|
|
});
|
2017-10-30 20:38:18 -04:00
|
|
|
});
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2020-03-29 17:51:13 -04:00
|
|
|
const icon = new St.Icon({
|
|
|
|
style_class: 'audio-selection-device-icon',
|
|
|
|
icon_name: this._getDeviceIcon(device),
|
|
|
|
});
|
2016-02-09 17:03:26 -05:00
|
|
|
box.add(icon);
|
|
|
|
|
2020-03-29 17:51:13 -04:00
|
|
|
const label = new St.Label({
|
|
|
|
style_class: 'audio-selection-device-label',
|
|
|
|
text: this._getDeviceLabel(device),
|
|
|
|
x_align: Clutter.ActorAlign.CENTER,
|
|
|
|
});
|
2016-02-09 17:03:26 -05:00
|
|
|
box.add(label);
|
|
|
|
|
2020-03-29 17:51:13 -04:00
|
|
|
const button = new St.Button({
|
|
|
|
style_class: 'audio-selection-device',
|
|
|
|
can_focus: true,
|
|
|
|
child: box,
|
|
|
|
});
|
2016-02-09 17:03:26 -05:00
|
|
|
this._selectionBox.add(button);
|
|
|
|
|
2017-10-30 20:38:18 -04:00
|
|
|
button.connect('clicked', () => {
|
|
|
|
this.emit('device-selected', device);
|
|
|
|
this.close();
|
|
|
|
Main.overview.hide();
|
|
|
|
});
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_openSettings() {
|
2019-01-28 20:18:52 -05:00
|
|
|
let desktopFile = 'gnome-sound-panel.desktop';
|
2016-02-09 17:03:26 -05:00
|
|
|
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
|
|
|
|
|
|
|
|
if (!app) {
|
2022-02-07 09:14:06 -05:00
|
|
|
log(`Settings panel for desktop file ${desktopFile} could not be loaded!`);
|
2016-02-09 17:03:26 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.close();
|
|
|
|
Main.overview.hide();
|
|
|
|
app.activate();
|
|
|
|
}
|
2019-05-23 16:45:44 -04:00
|
|
|
});
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2023-07-10 05:53:00 -04:00
|
|
|
export class AudioDeviceSelectionDBus {
|
2017-10-30 21:19:44 -04:00
|
|
|
constructor() {
|
2016-02-09 17:03:26 -05: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-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onDialogClosed() {
|
2016-02-09 17:03:26 -05:00
|
|
|
this._audioSelectionDialog = null;
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
_onDeviceSelected(dialog, device) {
|
2016-02-09 17:03:26 -05:00
|
|
|
let connection = this._dbusImpl.get_connection();
|
|
|
|
let info = this._dbusImpl.get_info();
|
2020-04-03 19:52:29 -04:00
|
|
|
const deviceName = Object.keys(AudioDevice)
|
|
|
|
.filter(dev => AudioDevice[dev] === device)[0].toLowerCase();
|
2023-08-06 19:45:22 -04:00
|
|
|
connection.emit_signal(
|
|
|
|
this._audioSelectionDialog._sender,
|
|
|
|
this._dbusImpl.get_object_path(),
|
|
|
|
info ? info.name : null,
|
|
|
|
'DeviceSelected',
|
|
|
|
GLib.Variant.new('(s)', [deviceName]));
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
OpenAsync(params, invocation) {
|
2016-02-09 17:03:26 -05:00
|
|
|
if (this._audioSelectionDialog) {
|
|
|
|
invocation.return_value(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let [deviceNames] = params;
|
|
|
|
let devices = 0;
|
2019-08-19 16:20:35 -04:00
|
|
|
deviceNames.forEach(n => (devices |= AudioDevice[n.toUpperCase()]));
|
2016-02-09 17:03:26 -05:00
|
|
|
|
|
|
|
let dialog;
|
|
|
|
try {
|
|
|
|
dialog = new AudioDeviceSelectionDialog(devices);
|
2019-01-28 20:26:39 -05:00
|
|
|
} catch (e) {
|
2016-02-09 17:03:26 -05:00
|
|
|
invocation.return_value(null);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dialog._sender = invocation.get_sender();
|
|
|
|
|
2017-12-01 19:27:35 -05:00
|
|
|
dialog.connect('closed', this._onDialogClosed.bind(this));
|
2016-02-09 17:03:26 -05:00
|
|
|
dialog.connect('device-selected',
|
2023-08-06 19:45:22 -04:00
|
|
|
this._onDeviceSelected.bind(this));
|
2016-02-09 17:03:26 -05:00
|
|
|
dialog.open();
|
|
|
|
|
|
|
|
this._audioSelectionDialog = dialog;
|
|
|
|
invocation.return_value(null);
|
2017-10-30 21:19:44 -04:00
|
|
|
}
|
2016-02-09 17:03:26 -05:00
|
|
|
|
2017-10-30 20:03:21 -04:00
|
|
|
CloseAsync(params, invocation) {
|
2016-02-09 17:03:26 -05:00
|
|
|
if (this._audioSelectionDialog &&
|
2023-08-06 20:51:19 -04:00
|
|
|
this._audioSelectionDialog._sender === invocation.get_sender())
|
2016-02-09 17:03:26 -05:00
|
|
|
this._audioSelectionDialog.close();
|
|
|
|
|
|
|
|
invocation.return_value(null);
|
|
|
|
}
|
2023-07-10 05:53:00 -04:00
|
|
|
}
|