Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package wiremix for openSUSE:Factory checked in at 2026-06-08 14:19:51 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/wiremix (Old) and /work/SRC/openSUSE:Factory/.wiremix.new.2375 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "wiremix" Mon Jun 8 14:19:51 2026 rev:2 rq:1357763 version:0.11.0~0 Changes: -------- --- /work/SRC/openSUSE:Factory/wiremix/wiremix.changes 2026-03-16 14:20:14.120524420 +0100 +++ /work/SRC/openSUSE:Factory/.wiremix.new.2375/wiremix.changes 2026-06-08 14:25:33.033630589 +0200 @@ -1,0 +2,14 @@ +Sun Jun 7 10:53:36 UTC 2026 - Jakob Lorenz <[email protected]> + +- bump version to 0.11.0: + * Added + - Configure which tabs are present and their order. + * Changed + - No longer exits on PipeWire errors in release builds. + +------------------------------------------------------------------- +Mon Mar 16 09:48:52 UTC 2026 - ecsos <[email protected]> + +- Fix build error for Leap. + +------------------------------------------------------------------- Old: ---- wiremix-0.10.0~0.tar.zst New: ---- wiremix-0.11.0~0.tar.zst ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ wiremix.spec ++++++ --- /var/tmp/diff_new_pack.gYQkRN/_old 2026-06-08 14:25:33.725659286 +0200 +++ /var/tmp/diff_new_pack.gYQkRN/_new 2026-06-08 14:25:33.729659452 +0200 @@ -16,15 +16,17 @@ # Name: wiremix -Version: 0.10.0~0 +Version: 0.11.0~0 Release: 0 Summary: Simple TUI audio mixer for PipeWire License: MIT OR Apache-2.0 -Url: https://github.com/tsowell/wiremix +URL: https://github.com/tsowell/wiremix Source0: %{name}-%{version}.tar.zst Source1: registry.tar.zst BuildRequires: cargo BuildRequires: cargo-packaging +BuildRequires: pkgconfig +BuildRequires: cmake(Clang) BuildRequires: pkgconfig(libpipewire-0.3) %description ++++++ _service ++++++ --- /var/tmp/diff_new_pack.gYQkRN/_old 2026-06-08 14:25:33.781661608 +0200 +++ /var/tmp/diff_new_pack.gYQkRN/_new 2026-06-08 14:25:33.785661774 +0200 @@ -3,7 +3,7 @@ <param name="url">https://github.com/tsowell/wiremix.git</param> <param name="versionformat">@PARENT_TAG@~@TAG_OFFSET@</param> <param name="scm">git</param> - <param name="revision">v0.10.0</param> + <param name="revision">v0.11.0</param> <param name="versionrewrite-pattern">v(\d+\.\d+\.\d+)</param> </service> <service mode="manual" name="recompress"> ++++++ registry.tar.zst ++++++ /work/SRC/openSUSE:Factory/wiremix/registry.tar.zst /work/SRC/openSUSE:Factory/.wiremix.new.2375/registry.tar.zst differ: char 258400, line 975 ++++++ wiremix-0.10.0~0.tar.zst -> wiremix-0.11.0~0.tar.zst ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/CHANGELOG.md new/wiremix-0.11.0~0/CHANGELOG.md --- old/wiremix-0.10.0~0/CHANGELOG.md 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/CHANGELOG.md 2026-06-05 05:22:41.000000000 +0200 @@ -7,6 +7,16 @@ ## [Unreleased] +## [0.11.0] - 2026-06-04 + +### Added + +- Configure which tabs are present and their order. + +### Changed + +- No longer exits on PipeWire errors in release builds. + ## [0.10.0] - 2026-03-05 ### Added diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/Cargo.lock new/wiremix-0.11.0~0/Cargo.lock --- old/wiremix-0.10.0~0/Cargo.lock 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/Cargo.lock 2026-06-05 05:22:41.000000000 +0200 @@ -1094,7 +1094,7 @@ "lru", "paste", "serde", - "strum 0.26.3", + "strum", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.0", @@ -1386,16 +1386,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ - "strum_macros 0.26.4", -] - -[[package]] -name = "strum" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" -dependencies = [ - "strum_macros 0.27.1", + "strum_macros", ] [[package]] @@ -1412,19 +1403,6 @@ ] [[package]] -name = "strum_macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] name = "sval" version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2029,7 +2007,7 @@ [[package]] name = "wiremix" -version = "0.10.0" +version = "0.11.0" dependencies = [ "anyhow", "bytemuck", @@ -2052,7 +2030,6 @@ "serde_json", "serde_with", "smallvec", - "strum 0.27.1", "toml 1.0.3+spec-1.1.0", "tracing", "tracing-error", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/Cargo.toml new/wiremix-0.11.0~0/Cargo.toml --- old/wiremix-0.10.0~0/Cargo.toml 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/Cargo.toml 2026-06-05 05:22:41.000000000 +0200 @@ -1,6 +1,6 @@ [package] name = "wiremix" -version = "0.10.0" +version = "0.11.0" authors = ["Thomas Sowell <[email protected]>"] description = "A TUI mixer for PipeWire" readme = "README.md" @@ -40,7 +40,6 @@ [dev-dependencies] paste = "1.0.15" -strum = { version = "0.27.1", features = ["derive"] } [features] trace = ["dep:tracing", "dep:tracing-error", "dep:tracing-subscriber"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/README.md new/wiremix-0.11.0~0/README.md --- old/wiremix-0.10.0~0/README.md 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/README.md 2026-06-05 05:22:41.000000000 +0200 @@ -74,6 +74,9 @@ -v, --tab <TAB> Initial tab view [possible values: playback, recording, output, input, configuration] + -T, --tabs <TABS>... + Which tabs are present and their order [possible values: playback, + recording, output, input, configuration] -m, --max-volume-percent <PERCENT> Maximum volume for volume sliders --no-enforce-max-volume @@ -165,6 +168,7 @@ char_set = "default" theme = "default" tab = "playback" +tabs = [ "playback", "recording", "output", "input", "configuration" ] max_volume_percent = 150.0 enforce_max_volume = false lazy_capture = false diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/src/app.rs new/wiremix-0.11.0~0/src/app.rs --- old/wiremix-0.10.0~0/src/app.rs 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/src/app.rs 2026-06-05 05:22:41.000000000 +0200 @@ -4,11 +4,11 @@ use std::sync::{mpsc, Arc}; use std::time::{Duration, Instant}; -use crate::config::{Config, Peaks}; +use crate::config::{Config, Peaks, TabKind}; use crate::wirehose::state::CaptureEligibility; use crate::wirehose::{ media_class, CommandSender, Event as PipewireEvent, PeakProcessor, - StateEvent, + PipewireError, StateEvent, }; use anyhow::{anyhow, Result}; @@ -128,34 +128,38 @@ } } -#[derive( - Deserialize, Default, Debug, Clone, Copy, PartialEq, clap::ValueEnum, -)] -#[serde(rename_all = "lowercase")] -#[cfg_attr(test, derive(strum::EnumIter))] -pub enum TabKind { - #[default] - Playback, - Recording, - Output, - Input, - Configuration, -} - -impl TabKind { - pub fn index(&self) -> usize { - *self as usize - } -} - -impl std::fmt::Display for TabKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TabKind::Playback => write!(f, "Playback"), - TabKind::Recording => write!(f, "Recording"), - TabKind::Output => write!(f, "Output Devices"), - TabKind::Input => write!(f, "Input Devices"), - TabKind::Configuration => write!(f, "Configuration"), +impl From<TabKind> for Tab { + fn from(tab_kind: TabKind) -> Tab { + match tab_kind { + TabKind::Playback => Tab::new( + String::from("Playback"), + ObjectList::new(ListKind::Node(view::NodeKind::Playback), None), + ), + TabKind::Recording => Tab::new( + String::from("Recording"), + ObjectList::new( + ListKind::Node(view::NodeKind::Recording), + None, + ), + ), + TabKind::Output => Tab::new( + String::from("Output Devices"), + ObjectList::new( + ListKind::Node(view::NodeKind::Output), + Some(DeviceKind::Sink), + ), + ), + TabKind::Input => Tab::new( + String::from("Input Devices"), + ObjectList::new( + ListKind::Node(view::NodeKind::Input), + Some(DeviceKind::Source), + ), + ), + TabKind::Configuration => Tab::new( + String::from("Configuration"), + ObjectList::new(ListKind::Device, None), + ), } } } @@ -224,37 +228,7 @@ rx: mpsc::Receiver<Event>, config: Config, ) -> Self { - let tabs = vec![ - Tab::new( - TabKind::Playback.to_string(), - ObjectList::new(ListKind::Node(view::NodeKind::Playback), None), - ), - Tab::new( - TabKind::Recording.to_string(), - ObjectList::new( - ListKind::Node(view::NodeKind::Recording), - None, - ), - ), - Tab::new( - TabKind::Output.to_string(), - ObjectList::new( - ListKind::Node(view::NodeKind::Output), - Some(DeviceKind::Sink), - ), - ), - Tab::new( - TabKind::Input.to_string(), - ObjectList::new( - ListKind::Node(view::NodeKind::Input), - Some(DeviceKind::Source), - ), - ), - Tab::new( - TabKind::Configuration.to_string(), - ObjectList::new(ListKind::Device, None), - ), - ]; + let tabs = config.tabs.iter().copied().map(Tab::from).collect(); // Update peaks with VU-meter-style ballistics let peak_processor = |new_peak, current_peak, samples, rate| { @@ -274,7 +248,7 @@ rx, error_message: None, tabs, - current_tab_index: config.tab.index(), + current_tab_index: config.tab, mouse_areas: Vec::new(), is_ready: false, state, @@ -740,21 +714,17 @@ } } -impl Handle for String { +impl Handle for PipewireError { fn handle(self, app: &mut App) -> Result<bool> { - // Handle errors - match self { - // These happen when objects are removed while wirehose is still in - // the process of setting up listeners - error if error.starts_with("no global ") => {} - error if error.starts_with("unknown resource ") => {} - // I see this one when disconnecting a Bluetooth sink - error if error == "Received error event" => {} - // This occurs sometimes when Bluetooth devices disconnect - error if error == "Buffer allocation failed" => {} - _ => app.exit(Some(self)), + // These errors don't seem to be fatal, so ignore them. + // In debug builds, exit and log the error. In the event that we caused + // the error, it will be helpful to know. + if cfg!(debug_assertions) { + app.exit(Some(self)); + return Ok(true); } - Ok(false) // This makes sense for now + + Ok(false) } } @@ -892,7 +862,6 @@ use std::cell::RefCell; use std::collections::VecDeque; use std::sync::Arc; - use strum::IntoEnumIterator; fn fixture<'a>(wirehose: &'a mock::WirehoseHandle<'a>) -> App<'a> { let (_, event_rx) = mpsc::channel(); @@ -909,7 +878,8 @@ keybindings: Default::default(), help: Default::default(), names: Default::default(), - tab: Default::default(), + tab: 0, + tabs: vec![TabKind::Playback], lazy_capture: Default::default(), filters: Default::default(), }; @@ -1003,7 +973,14 @@ keybindings, help: Default::default(), names: Default::default(), - tab: Default::default(), + tab: 0, + tabs: vec![ + TabKind::Playback, + TabKind::Recording, + TabKind::Output, + TabKind::Input, + TabKind::Configuration, + ], lazy_capture: Default::default(), filters: Default::default(), }; @@ -1017,28 +994,6 @@ assert_eq!(app.current_tab_index, 2); } - /// Ensure that the tabs enum variants are in the same order as the app's - /// tab Vec. Making the initial tab configurable depends on this property - /// because it uses the position of the enum variants to derivce an index - /// into the tab Vec. - #[test] - fn tab_enum_order_matches_tab_vec() { - let wirehose = mock::WirehoseHandle::default(); - let app = fixture(&wirehose); - - assert_eq!(TabKind::iter().count(), app.tabs.len()); - - for (tab, Tab { title, .. }) in TabKind::iter().zip(app.tabs.iter()) { - match tab { - TabKind::Playback => assert_eq!(title, "Playback"), - TabKind::Recording => assert_eq!(title, "Recording"), - TabKind::Output => assert_eq!(title, "Output Devices"), - TabKind::Input => assert_eq!(title, "Input Devices"), - TabKind::Configuration => assert_eq!(title, "Configuration"), - } - } - } - #[test] fn help_underflow() { let wirehose = mock::WirehoseHandle::default(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/src/config.rs new/wiremix-0.11.0~0/src/config.rs --- old/wiremix-0.10.0~0/src/config.rs 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/src/config.rs 2026-06-05 05:22:41.000000000 +0200 @@ -23,7 +23,7 @@ use serde::Deserialize; use toml; -use crate::app::{Action, TabKind}; +use crate::app::Action; pub use crate::config::matching::MatchCondition; use crate::opt::Opt; @@ -41,7 +41,8 @@ pub keybindings: HashMap<KeyEvent, Action>, pub help: help::Help, pub names: Names, - pub tab: TabKind, + pub tab: usize, + pub tabs: Vec<TabKind>, pub lazy_capture: bool, pub filters: Vec<MatchCondition>, } @@ -83,6 +84,8 @@ themes: HashMap<String, Theme>, #[serde(default = "default_tab")] tab: Option<TabKind>, + #[serde(default = "default_tabs")] + tabs: Vec<TabKind>, #[serde(default = "default_lazy_capture")] lazy_capture: bool, #[serde(default = "Filter::defaults", deserialize_with = "Filter::merge")] @@ -208,6 +211,19 @@ pub matches: Vec<MatchCondition>, } +#[derive( + Deserialize, Default, Debug, Clone, Copy, PartialEq, clap::ValueEnum, +)] +#[serde(rename_all = "lowercase")] +pub enum TabKind { + #[default] + Playback, + Recording, + Output, + Input, + Configuration, +} + fn default_fps() -> Option<f32> { Some(60.0) } @@ -224,6 +240,16 @@ Some(TabKind::default()) } +fn default_tabs() -> Vec<TabKind> { + vec![ + TabKind::Playback, + TabKind::Recording, + TabKind::Output, + TabKind::Input, + TabKind::Configuration, + ] +} + fn default_char_set_name() -> String { String::from("default") } @@ -279,6 +305,10 @@ self.tab = Some(*tab); } + if let Some(tabs) = &opt.tabs { + self.tabs = tabs.clone(); + } + if let Some(max_volume_percent) = &opt.max_volume_percent { self.max_volume_percent = Some(*max_volume_percent); } @@ -334,6 +364,16 @@ } } + if config_file.tabs.is_empty() { + anyhow::bail!("tabs must be non-empty"); + } + + let tab = config_file + .tabs + .iter() + .position(|&t| t == config_file.tab.unwrap_or_default()) + .context("initial tab not found in tabs")?; + // Emulate signals. This is intentionally done after generating help. config_file .keybindings @@ -353,7 +393,8 @@ keybindings: config_file.keybindings, help, names: config_file.names, - tab: config_file.tab.unwrap_or_default(), + tab, + tabs: config_file.tabs, lazy_capture: config_file.lazy_capture, filters, }) @@ -438,6 +479,7 @@ #[serde(deserialize_with = "themes")] themes: HashMap<String, Theme>, tab: Option<TabKind>, + tabs: Vec<TabKind>, lazy_capture: bool, filters: Vec<Filter>, } @@ -458,6 +500,7 @@ char_sets: strict.char_sets, themes: strict.themes, tab: strict.tab, + tabs: strict.tabs, lazy_capture: strict.lazy_capture, filters: strict.filters, } @@ -629,6 +672,43 @@ } #[test] + fn tabs_empty_is_error() { + let config_file: ConfigFile = toml::from_str("tabs = []").unwrap(); + assert!(Config::try_from(config_file).is_err()); + } + + #[test] + fn tab_not_in_tabs_is_error() { + let config = r#" + tabs = ["output", "input"] + "#; + let config_file: ConfigFile = toml::from_str(config).unwrap(); + assert!(Config::try_from(config_file).is_err()); + } + + #[test] + fn tab_index_resolves_to_position_in_tabs() { + let config = r#" + tab = "output" + tabs = ["playback", "output", "input"] + "#; + let config = Config::from_toml_str(config); + assert_eq!(config.tab, 1); + } + + #[test] + fn opt_tabs_overrides_config_tabs() { + let mut config_file: ConfigFile = toml::from_str("").unwrap(); + let opt = Opt { + tabs: Some(vec![TabKind::Playback, TabKind::Input]), + ..Default::default() + }; + config_file.apply_opt(&opt); + let config = Config::try_from(config_file).unwrap(); + assert_eq!(config.tabs, vec![TabKind::Playback, TabKind::Input]); + } + + #[test] fn name_override_with_matches() { let config = r#" [[names.overrides]] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/src/opt.rs new/wiremix-0.11.0~0/src/opt.rs --- old/wiremix-0.10.0~0/src/opt.rs 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/src/opt.rs 2026-06-05 05:22:41.000000000 +0200 @@ -4,8 +4,7 @@ use clap::Parser; -use crate::app::TabKind; -use crate::config; +use crate::config::{self, TabKind}; const VERSION: &str = concat!("v", env!("CARGO_PKG_VERSION")); @@ -54,6 +53,10 @@ )] pub tab: Option<TabKind>, + /// Which tabs are present and their order + #[clap(short = 'T', long, num_args = 1.., value_enum)] + pub tabs: Option<Vec<TabKind>>, + /// Maximum volume for volume sliders #[clap(short = 'm', long, value_name = "PERCENT")] pub max_volume_percent: Option<f32>, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/src/wirehose/event.rs new/wiremix-0.11.0~0/src/wirehose/event.rs --- old/wiremix-0.10.0~0/src/wirehose/event.rs 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/src/wirehose/event.rs 2026-06-05 05:22:41.000000000 +0200 @@ -6,13 +6,15 @@ use crate::wirehose::state::State; use crate::wirehose::{ObjectId, PropertyStore}; +pub type PipewireError = String; + /// Events emitted by the PipeWire monitoring thread. #[derive(Debug)] pub enum Event { /// The PipeWire state has changed State(StateEvent), /// An error occurred during monitoring - Error(String), + Error(PipewireError), /// The [StateEvent]s representing the PipeWire state at the time of /// connection have been sent. wirehose is listening for changes now. Ready, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/src/wirehose.rs new/wiremix-0.11.0~0/src/wirehose.rs --- old/wiremix-0.10.0~0/src/wirehose.rs 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/src/wirehose.rs 2026-06-05 05:22:41.000000000 +0200 @@ -20,7 +20,7 @@ mod sync_registry; pub use command::{Command, CommandSender}; -pub use event::{Event, StateEvent}; +pub use event::{Event, PipewireError, StateEvent}; pub use event_sender::EventHandler; pub use object_id::ObjectId; pub use property_store::PropertyStore; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/wiremix-0.10.0~0/wiremix.toml new/wiremix-0.11.0~0/wiremix.toml --- old/wiremix-0.10.0~0/wiremix.toml 2026-03-06 04:45:37.000000000 +0100 +++ new/wiremix-0.11.0~0/wiremix.toml 2026-06-05 05:22:41.000000000 +0200 @@ -32,6 +32,9 @@ # Initial tab tab = "playback" +# Which tabs are present and their order +tabs = [ "playback", "recording", "output", "input", "configuration" ] + # Maximum percentage for volume sliders max_volume_percent = 150.0
