From c2cbe9d38b8aa730e0ad96944e96adc5cf938358 Mon Sep 17 00:00:00 2001 From: Rostislav Krasny Date: Wed, 17 Jun 2026 10:40:11 +0300 Subject: [PATCH] sound@cinnamon.org: add a key binding to switch active audio output device --- .../applets/sound@cinnamon.org/applet.js | 29 ++++++++++++++++++- .../sound@cinnamon.org/settings-schema.json | 6 ++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js b/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js index 7e1f51e312..093d1ab535 100644 --- a/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js +++ b/files/usr/share/cinnamon/applets/sound@cinnamon.org/applet.js @@ -23,6 +23,9 @@ const MEDIA_PLAYER_2_PLAYER_NAME = "org.mpris.MediaPlayer2.Player"; // how long to show the output icon when volume is adjusted during media playback. const OUTPUT_ICON_SHOW_TIME_SECONDS = 3; +// cached for reuse by _cycleOutputDevice() +const OUTPUT_DEVICE_ICON = new Gio.ThemedIcon({name: "xsi-audio-speakers"}); + /* global values */ let players_without_seek_support = ['telegram desktop', 'spotify', 'totem', 'xplayer', 'gnome-mplayer', 'pithos', 'smplayer']; @@ -976,6 +979,7 @@ class CinnamonSoundApplet extends Applet.TextIconApplet { if (this.hideSystray) this.registerSystrayIcons(); this.settings.bind("keyOpen", "keyOpen", this._setKeybinding); + this.settings.bind("cycleOutputKey", "cycleOutputKey", this._setCycleKeybinding); this.settings.bind("alwaysShowMuteInput", "alwaysShowMuteInput", this.on_settings_changed); this.settings.bind("tooltipShowVolume", "tooltipShowVolume", this.on_settings_changed); @@ -986,6 +990,7 @@ class CinnamonSoundApplet extends Applet.TextIconApplet { this.menu = new Applet.AppletPopupMenu(this, orientation); this.menuManager.addMenu(this.menu); this._setKeybinding(); + this._setCycleKeybinding(); this.set_applet_icon_symbolic_name('xsi-audio-x-generic'); @@ -1115,6 +1120,27 @@ class CinnamonSoundApplet extends Applet.TextIconApplet { Main.keybindingManager.addXletHotKey(this, "sound-open", this.keyOpen, Lang.bind(this, this._openMenu)); } + _setCycleKeybinding() { + Main.keybindingManager.addXletHotKey(this, "sound-cycle-output", this.cycleOutputKey, Lang.bind(this, this._cycleOutputDevice)); + } + + _cycleOutputDevice() { + let outputDevices = this._devices.filter(d => d.type === "output"); + if (outputDevices.length < 2) + return; + + let nextIdx = (outputDevices.findIndex(d => d.item._dot !== null) + 1) % outputDevices.length; + let nextEntry = outputDevices[nextIdx]; + + nextEntry.item.activate(); + + let deviceLabel = nextEntry.description; + if (nextEntry.origin) + deviceLabel += " - " + nextEntry.origin; + + Main.osdWindowManager.show(-1, OUTPUT_DEVICE_ICON, deviceLabel, null); + } + _on_overamplification_change () { if (this._sound_settings.get_boolean(OVERAMPLIFICATION_KEY)) { this._volumeMax = 1.5 * this._volumeNorm; @@ -1143,6 +1169,7 @@ class CinnamonSoundApplet extends Applet.TextIconApplet { on_applet_removed_from_panel () { Main.keybindingManager.removeXletHotKey(this, "sound-open"); + Main.keybindingManager.removeXletHotKey(this, "sound-cycle-output"); if (this.hideSystray) this.unregisterSystrayIcons(); if (this._iconTimeoutId) { @@ -1675,7 +1702,7 @@ class CinnamonSoundApplet extends Applet.TextIconApplet { if(selectItem.menu.numMenuItems > 1) selectItem.actor.show(); - this._devices.push({id: id, type: type, item: item}); + this._devices.push({id: id, type: type, item: item, description: device.description, origin: device.origin}); } _onDeviceRemoved(control, id, type) { diff --git a/files/usr/share/cinnamon/applets/sound@cinnamon.org/settings-schema.json b/files/usr/share/cinnamon/applets/sound@cinnamon.org/settings-schema.json index 7dd69c5d56..dce52776eb 100644 --- a/files/usr/share/cinnamon/applets/sound@cinnamon.org/settings-schema.json +++ b/files/usr/share/cinnamon/applets/sound@cinnamon.org/settings-schema.json @@ -20,6 +20,12 @@ "default": "s", "tooltip" : "Set keybinding(s) to show the sound applet menu." }, + "cycleOutputKey": { + "type": "keybinding", + "description": "Cycle through audio output devices", + "default": "Tab", + "tooltip" : "Set keybinding(s) to switch between audio output devices." + }, "alwaysShowMuteInput": { "type": "switch", "description": "Always show input switch",