diff --git a/Containerfile b/Containerfile index dcf5ec1cbf..80187d8e5a 100644 --- a/Containerfile +++ b/Containerfile @@ -447,7 +447,6 @@ RUN /tmp/image-info.sh && \ systemctl disable rpm-ostreed-automatic.timer && \ systemctl enable ublue-update.timer && \ systemctl enable gamescope-workaround.service && \ - systemctl enable sunshine-workaround.service && \ systemctl enable waydroid-workaround.service && \ systemctl enable bazzite-hardware-setup.service && \ systemctl enable tailscaled.service && \ diff --git a/README-ID.md b/README-ID.md index e858c68e2e..d268c9682c 100644 --- a/README-ID.md +++ b/README-ID.md @@ -4,21 +4,24 @@ [![build-bazzite](https://github.com/ublue-os/bazzite/actions/workflows/build.yml/badge.svg)](https://github.com/ublue-os/bazzite/actions/workflows/build.yml) +# [🇺🇸](https://github.com/ublue-os/bazzite/blob/main/README.md) [🇪🇸](https://github.com/ublue-os/bazzite/blob/main/README-SPA.md) [🇮🇩](https://github.com/ublue-os/bazzite/blob/main/README-ID.md) + --- + # Daftar Isi -- [Fitur untuk **SEMUA** Image Bazzite](https://github.com/ublue-os/bazzite#about--features) - - [Fitur untuk Image **Desktop** ](https://github.com/ublue-os/bazzite#desktop) - - [Fitur untuk Image **Steam Deck / HTPC**](https://github.com/ublue-os/bazzite#steam-deckhome-theater-pcs-htpcs) - - [Fitur untuk Image **GNOME** ](https://github.com/ublue-os/bazzite#gnome) - - [Fitur dari upstream](https://github.com/ublue-os/bazzite#features-from-upstream) -- [Alasan](https://github.com/ublue-os/bazzite#why) -- [Showcase](https://github.com/ublue-os/bazzite#showcase) -- [Dokumentasi & Buletin](https://github.com/ublue-os/bazzite#documentation--newsletters) -- [Paket Kustom](https://github.com/ublue-os/bazzite#custom-packages) -- [Verifikasi & Metrik](https://github.com/ublue-os/bazzite#verification) -- [Ucapan Terimakasih](https://github.com/ublue-os/bazzite#special-thanks) -- [Membuat Image Anda Sendiri](https://github.com/ublue-os/bazzite#build-your-own) -- [Komunitas](https://github.com/ublue-os/bazzite#join-the-community) +- [Fitur untuk **SEMUA** Image Bazzite](#tentang--fitur) + - [Fitur untuk Image **Desktop** ](#desktop) + - [Fitur untuk Image **Steam Deck / HTPC**](#steam-deckhome-theater-pcs-htpcs) + - [Fitur untuk Image **GNOME** ](#gnome) + - [Fitur dari upstream](#fitur-dari-upstream) +- [Alasan](#alasan) +- [Pameran](#pameran) +- [Dokumentasi & Buletin](#dokumentasi--buletin) +- [Paket Kustom](#paket-kustom) +- [Verifikasi & Metrik](#paket-kustom) +- [Ucapan Terimakasih](#ucapan-terimakasih) +- [Membuat Image Anda Sendiri](#membuat-image-anda-sendiri) +- [Komunitas](#bergabung-dengan-komunitas) --- ## Tentang & Fitur @@ -35,13 +38,13 @@ Bazzite dibentuk dari [ublue-os/main](https://github.com/ublue-os/main) and [ubl - Dukungan untuk [Wallpaper Engine](https://www.wallpaperengine.io/en). (Hanya ada di KDE) - Disediakan [Ekstensi shell untuk ROM Properties Page ](https://github.com/GerbilSoft/rom-properties). - Dukungan penuh untuk [Winesync/Fastsync/NTsync](https://github.com/Frogging-Family/wine-tkg-git/issues/936). -- Pra-install [Distrobox](https://github.com/89luca89/distrobox) preinstalled dengan update otomatis untuk kontainer yang dibuat. -- Otomatisasi layanan `duperemove` dan `rmlint`untuk mengurangi penggunaan penyimpanan yang digunakan untuk konten prefix wine. +- Pra-install [Distrobox](https://github.com/89luca89/distrobox) dengan update otomatis untuk kontainer yang dibuat. +- Otomatisasi layanan `duperemove` dan `rmlint`untuk mengurangi penggunaan penyimpanan yang digunakan prefix wine. - Dukungan untuk HDMI CEC dengan [libCEC](https://libcec.pulse-eight.com/). -- Pra-install [System76-Scheduler](https://github.com/pop-os/system76-scheduler), menyediakan otomatisasi providing automatic process priority tweaks to your focused application and keeping CPU time for background processes to a minimum. +- Pra-install [System76-Scheduler](https://github.com/pop-os/system76-scheduler), menyediakan otomatisasi dan oprekan untuk proses yang berjalan dilatar belakang, serta meminimalkan CPU untuk proses latar belakang. - Menkustomisasi konfigurasi System76-Scheduler dengan aturan tambahan. - Menggunakan [Google's BBR TCP congestion control](https://github.com/google/bbr) secara bawaan. -- Pra-install[Input Remapper](https://github.com/sezanzeb/input-remapper) dan diaktifkan secara bawaan. (Dimatikan secara bawaan di varian Deck tapi tersedia dan dapat diaktifkan dengan `ujust enable-input-remapper`) +- Pra-install [Input Remapper](https://github.com/sezanzeb/input-remapper) dan diaktifkan secara bawaan. (Dinonaktifkan secara bawaan di varian Deck tapi tersedia dan dapat diaktifkan dengan `ujust enable-input-remapper`) - Portal Bazzite menyediakan cara mudah untuk menginstall berbagai macam aplikasi dan oprekan, termasuk menginstall [LACT](https://github.com/ilya-zlobintsev/LACT) dan [GreenWithEnvy](https://gitlab.com/leinardi/gwe). - Manajemen paket [Nix](https://nixos.org/) dengan [Fleek](https://getfleek.dev/) tersedia secara optional dan dapat diinstall dengan `ujust`. - Manajemen paket [Brew](https://brew.sh/) tersedia secara optional dan dapat diinstall dengan Portal Bazzite. @@ -49,13 +52,13 @@ Bazzite dibentuk dari [ublue-os/main](https://github.com/ublue-os/main) and [ubl - Mengatur aplikasi dengan [Flatseal](https://github.com/tchx84/Flatseal), [Warehouse](https://github.com/flattool/warehouse), dan [Gear Lever](https://github.com/mijorus/gearlever). - [OpenRGB](https://gitlab.com/CalcProgrammer1/OpenRGB) driver i2c-piix4 dan i2c-nct6775 untuk mengatur RGB di beberapa jenis motherboard. - Disediakan secara bawaan driver [OpenRazer](https://openrazer.github.io), Pilih OpenRazer di portal bazzite atau jalankan perintah `ujust install-openrazer` diterminal untuk menggunakannya. -- Disediakan secara bawaan [OpenTabletDriver](https://opentabletdriver.net/) udev rules , dengan perangkat lunaknya yang dapat diinstall dengan portal bazzite atau dengan mengetikkan `ujust install-opentabletdriver` di terminal. -- Driver [GCAdapter_OC](https://github.com/hannesmann/gcadapter-oc-kmod) untuk men-overclocking Kontroller Gamecube Nintendo sampai dengan 1000hz polling. +- Disediakan secara bawaan [OpenTabletDriver](https://opentabletdriver.net/) udev rules, dengan perangkat lunaknya yang dapat dipasang dengan portal bazzite atau dengan mengetikkan `ujust install-opentabletdriver` di terminal. +- Driver [GCAdapter_OC](https://github.com/hannesmann/gcadapter-oc-kmod) untuk men-overclocking Kontroller Nintendo Gamecube sampai dengan 1000hz polling. - Dukungan untuk keyboard [Wooting](https://wooting.io/). -- Dukungan untuk kartu grafis Southern Islands (HD 7000) and Sea Islands (HD 8000) AMD GPUs dengan driver `amdgpu`. +- Dukungan untuk kartu grafis Southern Islands (HD 7000) and Sea Islands (HD 8000) dengan driver `amdgpu`. - Memperbaiki masalah untuk [game yang menggunakan engine Source 1 (Contoh: TF2)](https://github.com/ValveSoftware/Source-1-Games/issues/5043) yang membuat game-nya crash saat dijalankan `ujust patch-source1-tcmalloc` - [XwaylandVideoBridge](https://invent.kde.org/system/xwaylandvideobridge) untuk Discord screensharing di Wayland. -- [Webapp Manager](https://github.com/linuxmint/webapp-manager) tersedia untuk membuat aplikasi dari situs web dengan berbagai macam peramban, termasuk Firefox. +- [Webapp Manager](https://github.com/linuxmint/webapp-manager) tersedia untuk membuat aplikasi dari situs web dengan berbagai macam peramban, termasuk Firefox. ### Desktop @@ -86,28 +89,28 @@ Perangkat yang bukan Steam Deck masih bisa menggunakan image bazzite-deck, tetap Varian `bazzite-deck` ini didesain untuk digunakan sebagai alternatif untuk SteamOS di perangkat Steam Deck dan HTPC dengan pengalaman seperti konsol: - Langsung boot ke Gamemode seperti SteamOS. -- **Automatic `duperemove` greatly trims the size of compatdata.** -- **Latest version of Mesa creates smaller shader caches and does not require them to prevent stutter.** -- **Able to be booted even if the drive is full.** -- **Support for every language supported by upstream Fedora.** +- **`duperemove` secara otomatis yang berguna untuk mengurangi ukuran folder compatdata.** +- **Versi terbaru dari Mesa yang menghasilkan ukuran shaders cache yang lebih kecil dan tidak diperlukan lagi untuk mencegah stutter.** +- **Langsung bisa booting walaupun diska penuh.** +- **Dukungan untuk setiap bahasa yang disupport oleh Fedora.** - **Menggunakan Wayland di desktop dengan [dukungan untuk Steam input](https://github.com/Supreeeme/extest).** - Fitur yang diporting dari SteamOS meliputi driver, pembaruan perangkat tegar, dan pengatur kecepatan kipas [dari repositori evlaV ](https://gitlab.com/evlaV). - Mesa yang dipatch untuk mengatur framerate di Gamescope. - Hadir dengan patch dari [SteamOS BTRFS](https://gitlab.com/popsulfr/steamos-btrfs) untuk dukungan BTRFS di SD Card secara bawaan. - [SDGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU) tersedia dan diaktifkan secara bawaan. - Pilihan untuk menginstall [Decky Loader](https://github.com/SteamDeckHomebrew/decky-loader), [EmuDeck](https://www.emudeck.com/), [RetroDECK](https://retrodeck.net/), dan [ProtonUp-Qt](https://davidotek.github.io/protonup-qt/), serta berbagai macam paket/aplikasi yang berguna selama pemasangan. -- Sistem pembaruan kustom memungkinkan Sistem Operasi,Flatpal,Paket Nix(dengan fleek), dan Distrobox image untuk diupdate dari Gamemode UI. +- Sistem pembaruan kustom memungkinkan Sistem Operasi,Flatpak,Paket Nix(dengan fleek), dan Distrobox image untuk diupdate dari Gamemode UI. - Dukungan untuk dual-boot dengan Windows karena GRUB dari Fedora. -- Pembaruan merusak sesuatu? tinggal rollback ke versi sebelumnya berkat fitur rollback`rpm-ostree`. Anda bisa memilih versi image sebelumnya di boot menuY. +- Pembaruan merusak sesuatu? tinggal rollback ke versi sebelumnya berkat fitur rollback `rpm-ostree`. Anda bisa memilih versi image sebelumnya di boot menu. - Pra-install Steam dan Lutris sebagai paket sistem. -- Pra-install [Discover Overlay](https://github.com/trigg/Discover) untuk Discord dan otomatis diluncurkan di Gamemode,dan di desktop jika Discord terinstall. [Lihat dokumentasinya disini](https://trigg.github.io/Discover/bazzite). +- Pra-install [Discover Overlay](https://github.com/trigg/Discover) untuk Discord dan otomatis diluncurkan di Gamemode,dan di desktop jika Discord terinstall, [Lihat dokumentasinya disini](https://trigg.github.io/Discover/bazzite). - Menggunakan ZRAM(4GB) dengan kompresi ZSTD secara bawaan dengan opsi untuk menggunakan 1GB swap file dan bisa diatur sesuai kebutuhan. -- Penjadwal I/O untuk mencegah I/O starvation ketika memasang game atau ketika proses latar belakang `duperemove` dan `rmlint`. +- Penjadwal I/O untuk mencegah I/O starvation ketika memasang game atau ketika proses latar belakang `duperemove` dan `rmlint` bekerja. - Mengaplikasikan parameter kernel dari SteamOS. - Kalibrasi Warna Layar untuk layar matte dan reflektif Steam Deck. - Fitur-fitur pengguna advance yang tidak diaktifkan secara bawaan seperti: - - Service untuk undervolting Steam Deck yang beresiko rendah dengan [RyzenAdj](https://github.com/FlyGoat/RyzenAdj) dan [Ryzen SMU](https://gitlab.com/leogx9r/ryzen_smu), anda bisa mencek `ryzenadj.service` dan `/etc/default/ryzenadj`. - - Service untuk membatasi kapasitas maksimal saat mengisi ulang, anda bisa mencek `batterylimit.service` dan `/etc/default/batterylimit`. (Bekerja walaupun perangkat dalam posisi non aktif) + - Service untuk undervolting Steam Deck yang beresiko rendah dengan [RyzenAdj](https://github.com/FlyGoat/RyzenAdj) dan [Ryzen SMU](https://gitlab.com/leogx9r/ryzen_smu), anda bisa mengecek `ryzenadj.service` dan `/etc/default/ryzenadj`. + - Service untuk membatasi kapasitas maksimal saat mengisi ulang, anda bisa mengeceknya di `batterylimit.service` dan `/etc/default/batterylimit`. (Bekerja walaupun perangkat dalam posisi non aktif) - Dukungan bawaan untuk overclocking layar. Contohnya seperti ini, anda menambahkan `GAMESCOPE_OVERRIDE_REFRESH_RATE=40,70` di `/etc/environment`. - Anda bisa menggunakan X11 jika diperlukan dengan mengedit `/etc/default/desktop-wayland`. - Lu punya RAM 32 GB? anda bisa memakai 2x lipat VRAM yang diaplikasikan secara otomatis!. (Coba bagikan skill ngesolder lu dong) @@ -128,7 +131,7 @@ rpm-ostree rebase ostree-unverified-registry:ghcr.io/ublue-os/bazzite-deck:lates ### GNOME -Varian desktop GNOME tersedia dalam versi desktop dan GNOME.varian ini memiliki beberapa fitur tambahan seperti : +Varian desktop GNOME tersedia dalam versi desktop dan deck.varian ini memiliki beberapa fitur tambahan seperti : - [Dukungan untuk Variable refresh rate dan fractional scaling di sesi wayland secara bawaan](https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1154). - Menu kustom di bar atas untuk kembali ke game mode, meluncurkan Steam, dan membuka berbagai macam peralatan yang berguna. @@ -178,19 +181,18 @@ rpm-ostree rebase ostree-unverified-registry:ghcr.io/ublue-os/bazzite-deck-gnome - Paket sistem yang terbarukan. - Layer paket fedora tanpa perlu takut paket tersebut hilang saat memperbarui sistem. - Pra-install [SELinux](https://github.com/SELinuxProject/selinux) untuk keamanan dan telah dikonfigurasi secara bawaan. -- The ability to rebase to different Fedora libostree images, if desired, without losing user data. -- Bisa rebase ke image Fedora ostree yang lain jika mau, tanpa kehilangan data pengguna. -- Dukungan printing berkat [CUPS](https://www.cups.org/) yang terinstall secara bawaan +- Bisa rebase ke image Fedora ostree yang lain jika mau,tanpa kehilangan data pengguna. +- Dukungan printing berkat [CUPS](https://www.cups.org/) yang terinstall secara bawaan. ## Alasan Bazzite dimulai sebagai proyek untuk menyelesaikan berbagai masalah yang ada di SteamOS, terutama paket-paket yang tidak terbarukan (walaupun menggunakan ArchLinux sebagai base) dan tidak ada manajemen paket yang fungsional. -Walaupun proyek ini juga menggunakan image. anda masih bisa menginstall berbagai macam paket Fedora dari perintah shell. Paket-paket ini akan tetap bertahan setelah pembaruan (Jadi anda bisa install aplikasi VPN yang anda g bisa install di SteamOS). +Walaupun proyek ini juga menggunakan image. anda masih bisa menginstall berbagai macam paket Fedora dari perintah shell. Paket-paket ini akan tetap bertahan setelah pembaruan (Jadi anda bisa install aplikasi VPN yang anda g bisa install di SteamOS). Sebagai tambahan, Bazzite sering diupdate beberapa kali dalam satu minggu dengan paket-paket dari upstream Fedora, memberikan anda performa terbaik dan fitur-fitur terbaru di base yang stabil. Bazzite hadir dengan versi Linux kernel yang terbaru dan SELinux diaktifkan secara bawaan dengan dukungan penuh untuk secure boot (Jalankan `ujust enroll-secure-boot-key` dan masukan password `ublue-os` jika diperintahkan untuk menroll key dari kita) -dan enkripsi disk membuat ini aman dan sempurna untuk komputasi general. (Yes, lu bisa ngeprint dari Bazzite!). +dan enkripsi disk membuat ini aman dan sempurna untuk komputasi general. (Yes, anda bisa ngeprint dari Bazzite!). Baca [FAQ](https://universal-blue.discourse.group/docs?topic=33) untuk tahu apa saja yang membuat Bazzite beda dari Sistem Operasi Linux yang lain. @@ -214,7 +216,7 @@ Baca [FAQ](https://universal-blue.discourse.group/docs?topic=33) untuk tahu apa Temukan dokumentasi tambahan project ini [disini](https://universal-blue.discourse.group/docs). -Cek [buletin](https://universal-blue.discourse.group/tag/bazzite-buzz) kami yang selalu update secara regular tentang project ini. +Cek [buletin](https://universal-blue.discourse.group/tag/bazzite-buzz) kami yang selalu update secara regular tentang proyek ini. ## Paket Kustom Paket-paket ini diporting dari SteamOS dan ChimeraOS, dan digunakan oleh Bazzite dan dibuat di Copr [bazzite](https://copr.fedorainfracloud.org/coprs/kylegospo/bazzite/) and [bazzite-multilib](https://copr.fedorainfracloud.org/coprs/kylegospo/bazzite-multilib/). @@ -282,7 +284,7 @@ Sebagai tambahan, paket-paket berikut ini digunakan dari repository Copr: ## Verifikasi -Image berikut ini telah disigned oleh sigstore's [cosign](https://docs.sigstore.dev/cosign/overview/).Anda bisa menverifikasi signature dengan mengunduh key `cosign.pub` dari repo ini dan menjalankan perintah ini: +Image berikut ini telah disigned oleh sigstore's [cosign](https://docs.sigstore.dev/cosign/overview/). Anda bisa menverifikasi signature dengan mengunduh key `cosign.pub` dari repo ini dan menjalankan perintah ini: ```bash cosign verify --key cosign.pub ghcr.io/ublue-os/bazzite @@ -290,11 +292,12 @@ cosign verify --key cosign.pub ghcr.io/ublue-os/bazzite ## Secure Boot -Secure boot didukung dengan key dari kami. Pub key dapat ditemukan di root repositori ini[disini bro](https://github.com/ublue-os/bazzite/blob/main/secure_boot_key.der). +Secure boot didukung dengan key dari kami. Pub key dapat ditemukan di root repositori [ini](https://github.com/ublue-os/bazzite/blob/main/secure_boot.der). Jika anda ingin menenroll key ini sebelum instalasi, unduh key ini dan jalankan: ```bash -sudo mokutil --import secure_boot_key.der +sudo mokutil --timeout -1 +sudo mokutil --import secure_boot.der ``` ### Metrik Kontribusi diff --git a/README-SPA.md b/README-SPA.md index 8a63d77d4d..fefe1ca71b 100644 --- a/README-SPA.md +++ b/README-SPA.md @@ -4,24 +4,26 @@ ![build-bazzite](https://github.com/ublue-os/bazzite/actions/workflows/build.yml/badge.svg) +# [🇺🇸](https://github.com/ublue-os/bazzite/blob/main/README.md) [🇪🇸](https://github.com/ublue-os/bazzite/blob/main/README-SPA.md) [🇮🇩](https://github.com/ublue-os/bazzite/blob/main/README-ID.md) + --- # Tabla de Contenidos -- [Características de **todas** las imágenes de Bazzite](https://github.com/ublue-os/bazzite#about--features) - - [Características de las imágenes para **Computadoras de Escritorio**](https://github.com/ublue-os/bazzite#desktop) - - [Características de las imágenes para **Steam Deck/HTPC**](https://github.com/ublue-os/bazzite#steam-deckhome-theater-pcs-htpcs) - - [Características de las imágenes con el entorno de escritorio **GNOME**](https://github.com/ublue-os/bazzite#gnome) - - [Características del Upstream](https://github.com/ublue-os/bazzite#features-from-upstream) -- [¿Por qué?](https://github.com/ublue-os/bazzite#why) -- [Mira como luce Bazzite (Capturas de Pantalla)](https://github.com/ublue-os/bazzite#showcase) -- [Documentación y Boletín informativo/Newsletters (En inglés)](https://github.com/ublue-os/bazzite#documentation--newsletters) -- [Paquetes Personalizados](https://github.com/ublue-os/bazzite#custom-packages) -- [Arranque Seguro (Secure Boot)](https://github.com/ublue-os/bazzite#secure-boot) -- [Verificación y Métricas](https://github.com/ublue-os/bazzite#verification) -- [Gracias Especiales](https://github.com/ublue-os/bazzite#special-thanks) -- [Créalo tu Mismo](https://github.com/ublue-os/bazzite#build-your-own) -- [Comunidad (en inglés)](https://github.com/ublue-os/bazzite#join-the-community) +- [Características de **todas** las imágenes de Bazzite](#about--features) + - [Características de las imágenes para **Computadoras de Escritorio**](#desktop) + - [Características de las imágenes para **Steam Deck/HTPC**](#steam-deckhome-theater-pcs-htpcs) + - [Características de las imágenes con el entorno de escritorio **GNOME**](#gnome) + - [Características del Upstream](#features-from-upstream) +- [¿Por qué?](#why) +- [Mira como luce Bazzite (Capturas de Pantalla)](#showcase) +- [Documentación y Boletín informativo/Newsletters (En inglés)](#documentation--newsletters) +- [Paquetes Personalizados](#custom-packages) +- [Arranque Seguro (Secure Boot)](#secure-boot) +- [Verificación y Métricas](#verification) +- [Gracias Especiales](#special-thanks) +- [Créalo tu Mismo](#build-your-own) +- [Comunidad (en inglés)](#join-the-community) --- @@ -315,13 +317,14 @@ cosign verify --key cosign.pub ghcr.io/ublue-os/bazzite ## Arranque Seguro (Secure Boot) -El Arranque Seguro (Secure Boot) tiene soporte gracias a nuestra llave digital personalizada. La llave pública puede encontrarse en la raíz de [este](https://github.com/ublue-os/bazzite/blob/main/secure_boot_key.der) repositorio. +El Arranque Seguro (Secure Boot) tiene soporte gracias a nuestra llave digital personalizada. La llave pública puede encontrarse en la raíz de [este](https://github.com/ublue-os/bazzite/blob/main/secure_boot.der) repositorio. Si gustas registrar esta llave antes de instalar Bazzite, descarga la llave y ejecuta el siguiente comando en una terminal: ```bash -sudo mokutil --import secure_boot_key.der +sudo mokutil --timeout -1 +sudo mokutil --import secure_boot.der ``` ### Métricas de Contribución diff --git a/README.md b/README.md index c896b0dc69..bdd38f021f 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,26 @@ [![build-bazzite](https://github.com/ublue-os/bazzite/actions/workflows/build.yml/badge.svg)](https://github.com/ublue-os/bazzite/actions/workflows/build.yml) +# [🇺🇸](https://github.com/ublue-os/bazzite/blob/main/README.md) [🇪🇸](https://github.com/ublue-os/bazzite/blob/main/README-SPA.md) [🇮🇩](https://github.com/ublue-os/bazzite/blob/main/README-ID.md) + --- + # Table of Contents -- [Features for **All** Bazzite Images](https://github.com/ublue-os/bazzite#about--features) - - [Features for **Desktop** Images](https://github.com/ublue-os/bazzite#desktop) - - [Features for **Steam Deck / HTPC** Images](https://github.com/ublue-os/bazzite#steam-deckhome-theater-pcs-htpcs) - - [Features for **GNOME** Images](https://github.com/ublue-os/bazzite#gnome) - - [Features from Upstream](https://github.com/ublue-os/bazzite#features-from-upstream) -- [Why](https://github.com/ublue-os/bazzite#why) -- [Showcase](https://github.com/ublue-os/bazzite#showcase) -- [Documentation & Newsletters](https://github.com/ublue-os/bazzite#documentation--newsletters) -- [Custom Packages](https://github.com/ublue-os/bazzite#custom-packages) -- [Image Verification](https://github.com/ublue-os/bazzite#verification) -- [Secure Boot](https://github.com/ublue-os/bazzite#secure-boot) -- [Metrics](https://github.com/ublue-os/bazzite#contributor-metrics) -- [Special Thanks](https://github.com/ublue-os/bazzite#special-thanks) -- [Building Your Own](https://github.com/ublue-os/bazzite#build-your-own) -- [Community](https://github.com/ublue-os/bazzite#join-the-community) +- [Features for **All** Bazzite Images](#about--features) + - [Features for **Desktop** Images](#desktop) + - [Features for **Steam Deck / HTPC** Images](#steam-deckhome-theater-pcs-htpcs) + - [Features for **GNOME** Images](#gnome) + - [Features from Upstream](#features-from-upstream) +- [Why](#why) +- [Showcase](#showcase) +- [Documentation & Newsletters](#documentation--newsletters) +- [Custom Packages](#custom-packages) +- [Image Verification](#verification) +- [Secure Boot](#secure-boot) +- [Metrics](#contributor-metrics) +- [Special Thanks](#special-thanks) +- [Building Your Own](#build-your-own) +- [Community](#join-the-community) --- ## About & Features @@ -30,8 +33,7 @@ Bazzite is an OCI image that serves as an alternative operating system for the [ Bazzite is built from [ublue-os/main](https://github.com/ublue-os/main) and [ublue-os/nvidia](https://github.com/ublue-os/nvidia) using [Fedora](https://fedoraproject.org/) technology, which means expanded hardware support and built in drivers are included. Additionally, Bazzite adds the following features: - Uses the [fsync kernel](https://copr.fedorainfracloud.org/coprs/sentry/kernel-fsync/) to achieve HDR and expanded hardware support, among numerous other included patches. -- HDR available in Gamescope Session. -- Proprietary Nvidia drivers pre-installed. +- HDR available in Game mode. - NVK available on non-Nvidia builds. - Full hardware accelerated codec support for H264 decoding. - Full support for AMD's ROCM OpenCL/HIP run-times. @@ -87,7 +89,7 @@ or for devices with Nvidia GPUs: rpm-ostree rebase ostree-unverified-registry:ghcr.io/ublue-os/bazzite-nvidia:latest ``` -**For users with Secure Boot enabled:** Run `ujust enroll-secure-boot-key` and enter the password `ublue-os` if prompted to enroll the required key. +**For users with Secure Boot enabled:** Follow our [secure boot documentation](#secure-boot) prior to rebasing. ### Steam Deck/Home Theater PCs (HTPCs) > [!IMPORTANT] @@ -95,7 +97,7 @@ Devices that are NOT the Steam Deck can still use the bazzite-deck images, but m Variant designed for usage as an alternative to SteamOS on the Steam Deck, and for a console-like experience on HTPCs, available as `bazzite-deck`: -- Directly boots to Gamemode matching SteamOS's behavior. +- Directly boots to Game mode matching SteamOS's behavior. - **Automatic `duperemove` greatly trims the size of compatdata.** - **Latest version of Mesa creates smaller shader caches and does not require them to prevent stutter.** - **Able to be booted even if the drive is full.** @@ -107,11 +109,11 @@ Variant designed for usage as an alternative to SteamOS on the Steam Deck, and f - Comes with patches from [SteamOS BTRFS](https://gitlab.com/popsulfr/steamos-btrfs) for full BTRFS support for the SD card by default. - Ships with a ported copy of [SDGyroDSU](https://github.com/kmicki/SteamDeckGyroDSU), enabled by default. - Option to install [Decky Loader](https://github.com/SteamDeckHomebrew/decky-loader), [EmuDeck](https://www.emudeck.com/), [RetroDECK](https://retrodeck.net/), and [ProtonUp-Qt](https://davidotek.github.io/protonup-qt/), among numerous other useful packages on installation. -- Custom update system allows for the OS, Flatpaks, Nix packages (Via Fleek), and Distrobox images to be updated directly from the Gamemode UI. +- Custom update system allows for the OS, Flatpaks, Nix packages (Via Fleek), and Distrobox images to be updated directly from the Game mode UI. - Built in support for Windows dual-boot thanks to Fedora's installation of GRUB being left intact. - Update break something? Easily roll back to the previous version of Bazzite thanks to `rpm-ostree`'s rollback functionality. You can even select previous images at boot. - Steam and Lutris preinstalled on the image as layered packages. -- [Discover Overlay](https://github.com/trigg/Discover) for Discord pre-installed and automatically launches in both Gamemode and on the Desktop if Discord is installed. [View the official documentation here](https://trigg.github.io/Discover/bazzite). +- [Discover Overlay](https://github.com/trigg/Discover) for Discord pre-installed and automatically launches in both Game mode and on the Desktop if Discord is installed. [View the official documentation here](https://trigg.github.io/Discover/bazzite). - Uses ZRAM(4GB) with the ZSTD compression algorithm by default with the option to switch back to a 1GB swap file and set a custom size for it if desired. - Kyber I/O scheduler to prevent I/O starvation when installing games or during background `duperemove` and `rmlint` processes. - Applies SteamOS's kernel parameters. @@ -122,7 +124,7 @@ Variant designed for usage as an alternative to SteamOS on the Steam Deck, and f - Built in support for display overclocking. For example, add `GAMESCOPE_OVERRIDE_REFRESH_RATE=40,70` to `/etc/environment`. - Ability to use X11 on the desktop if desired by editing `/etc/default/desktop-wayland`. - 32GB RAM mod your Steam Deck? Enjoy double the maximum VRAM amount, automatically applied. (Can you share your soldering skills?) -- Steam Deck hardware-specific services can be disabled by running `ujust disable-deck-services` in the terminal, useful for trying this image on other handhelds or for use on HTPCs. +- Steam Deck hardware-specific services can be disabled by running `ujust disable-bios-updates` and `ujust disable-firmware-updates` in the terminal. These are automatically disabled on non-Deck hardware, and on Decks with DeckHD displays or 32GB RAM mods. - More information can be found [here](https://universal-blue.discourse.group/docs?topic=37) on the Bazzite Steam Deck images. > [!WARNING] @@ -172,10 +174,13 @@ To rebase an existing ostree system to the **Steam Deck/HTPC** release: rpm-ostree rebase ostree-unverified-registry:ghcr.io/ublue-os/bazzite-deck-gnome:latest ``` +**For users with Secure Boot enabled:** Follow our [secure boot documentation](#secure-boot) prior to rebasing. + ### Features from Upstream #### Universal Blue +- Proprietary Nvidia drivers pre-installed. - Flathub is enabled by default. - [`ujust`](https://github.com/casey/just) commands for convenience. - Multi-media codecs out of the box. @@ -277,6 +282,7 @@ Additionally, the following packages are used from other Copr repos: | [joycond](https://copr.fedorainfracloud.org/coprs/kylegospo/joycond/) | ![Build Status](https://copr.fedorainfracloud.org/coprs/kylegospo/joycond/package/joycond/status_image/last_build.png?) | | [kernel-fsync](https://copr.fedorainfracloud.org/coprs/sentry/kernel-fsync/) | ![Build Status](https://copr.fedorainfracloud.org/coprs/sentry/kernel-fsync/package/kernel/status_image/last_build.png?) | | [latencyflex-vulkan-layer](https://copr.fedorainfracloud.org/coprs/kylegospo/LatencyFleX/) | ![Build Status](https://copr.fedorainfracloud.org/coprs/kylegospo/LatencyFleX/package/latencyflex-vulkan-layer/status_image/last_build.png?) | +| [nerd-fonts](https://copr.fedorainfracloud.org/coprs/che/nerd-fonts/) | ![Build Status](https://copr.fedorainfracloud.org/coprs/che/nerd-fonts/package/nerd-fonts/status_image/last_build.png?) | | [noise-suppression-for-voice](https://copr.fedorainfracloud.org/coprs/ycollet/audinux/) | ![Build Status](https://copr.fedorainfracloud.org/coprs/ycollet/audinux/package/noise-suppression-for-voice/status_image/last_build.png?) | | [obs-vkcapture](https://copr.fedorainfracloud.org/coprs/kylegospo/obs-vkcapture/) | ![Build Status](https://copr.fedorainfracloud.org/coprs/kylegospo/obs-vkcapture/package/obs-vkcapture/status_image/last_build.png?) | | [prompt](https://gitlab.gnome.org/chergert/prompt) | ![Build Status](https://copr.fedorainfracloud.org/coprs/kylegospo/prompt/package/prompt/status_image/last_build.png?) | @@ -297,17 +303,32 @@ cosign verify --key cosign.pub ghcr.io/ublue-os/bazzite ## Secure Boot -Secure boot is supported with our custom key. The pub key can be found in the root of this repository [here](https://github.com/ublue-os/bazzite/blob/main/secure_boot_key.der). -If you'd like to enroll this key prior to installation, download the key and run the following: +Secure boot is supported with our custom key. The pub key can be found in the root of this repository [here](https://github.com/ublue-os/bazzite/blob/main/secure_boot.der). +If you'd like to enroll this key prior to installation or rebase, download the key and run the following: ```bash -sudo mokutil --import secure_boot_key.der +sudo mokutil --timeout -1 +sudo mokutil --import secure_boot.der ``` +For users already on a Universal Blue image, you may instead run `ujust enroll-secure-boot-key`. + +If asked for a password, use `ublue-os`. + ### Contributor Metrics ![Bazzite](https://repobeats.axiom.co/api/embed/86b500d79c613015ad16f56df76c8e13f3fd98ae.svg "Repobeats analytics image") +#### Star History + + + + + + Star History Chart + + + ## Special Thanks Bazzite is a community effort and wouldn't exist without everyone's support. Below are some of the people who've helped us along the way: diff --git a/secure_boot_key.der b/secure_boot.der similarity index 100% rename from secure_boot_key.der rename to secure_boot.der diff --git a/spec_files/jupiter-fan-control/fedora.patch b/spec_files/jupiter-fan-control/fedora.patch index 631324ae12..95076df6f9 100644 --- a/spec_files/jupiter-fan-control/fedora.patch +++ b/spec_files/jupiter-fan-control/fedora.patch @@ -16,3 +16,12 @@ diff -ur usr/share/jupiter-fan-control/PID.py usr/share/jupiter-fan-control/PID. # # This file is part of IvPID. # Copyright (C) 2015 Ivmech Mechatronics Ltd. +@@ -64,7 +64,7 @@ + """Calculates PID value for given reference feedback + + .. math:: +- u(t) = K_p e(t) + K_i \int_{0}^{t} e(t)dt + K_d {de}/{dt} ++ u(t) = K_p e(t) + K_i \\int_{0}^{t} e(t)dt + K_d {de}/{dt} + + .. figure:: images/pid_1.png + :align: center diff --git a/spec_files/jupiter-hw-support/bazzite.png b/spec_files/jupiter-hw-support/bazzite.png new file mode 100644 index 0000000000..8674610f31 Binary files /dev/null and b/spec_files/jupiter-hw-support/bazzite.png differ diff --git a/spec_files/jupiter-hw-support/jupiter-hw-support-btrfs.spec b/spec_files/jupiter-hw-support/jupiter-hw-support-btrfs.spec index 4d75fed237..aae12f1752 100644 --- a/spec_files/jupiter-hw-support/jupiter-hw-support-btrfs.spec +++ b/spec_files/jupiter-hw-support/jupiter-hw-support-btrfs.spec @@ -10,9 +10,10 @@ License: GPLv3 URL: https://github.com/ublue-os/bazzite Source: https://gitlab.com/evlaV/%{packagename}/-/archive/%{packagever}/%{packagename}-%{packagever}.tar.gz +Source2: bazzite.png Patch0: fedora.patch Patch1: selinux.patch -Patch2: btrfs-automount.patch +Patch2: btrfs-automount.patch Patch3: btrfs-format.patch Patch4: user.patch Patch5: bazzite-btrfs.patch @@ -69,6 +70,7 @@ cp -rv usr/lib/udev %{buildroot}%{_prefix}/lib/udev cp -rv usr/bin/* %{buildroot}%{_bindir} cp -rv usr/lib/systemd/system/* %{buildroot}%{_unitdir} cp -rv etc/* %{buildroot}%{_sysconfdir} +cp %{SOURCE2} %{buildroot}%{_datadir}/plymouth/themes/steamos/bazzite.png sed -i 's@steamos-cursor.png@usr/share/steamos/steamos-cursor.png@g' usr/share/steamos/steamos-cursor-config xcursorgen usr/share/steamos/steamos-cursor-config %{buildroot}%{_datadir}/icons/steam/cursors/default # Remove unneeded files diff --git a/spec_files/mesa/0001-intel-compiler-fix-release-build-unused-variable.patch b/spec_files/mesa/0001-intel-compiler-fix-release-build-unused-variable.patch deleted file mode 100644 index 7cce8273e7..0000000000 --- a/spec_files/mesa/0001-intel-compiler-fix-release-build-unused-variable.patch +++ /dev/null @@ -1,28 +0,0 @@ -From b32d3c9251e18c77b4d58db61b43797ffb7b05cf Mon Sep 17 00:00:00 2001 -From: Dave Airlie -Date: Wed, 3 Jan 2024 16:31:23 +1000 -Subject: [PATCH] intel/compiler: fix release build unused variable. - -This is only used in an assert. - -Fixes: 158ac265dfd0 ("intel/fs: Make helpers for saving/restoring instruction order") ---- - src/intel/compiler/brw_fs.cpp | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/intel/compiler/brw_fs.cpp b/src/intel/compiler/brw_fs.cpp -index 696b5db8d9d..aa01a2241ce 100644 ---- a/src/intel/compiler/brw_fs.cpp -+++ b/src/intel/compiler/brw_fs.cpp -@@ -6834,7 +6834,7 @@ save_instruction_order(const struct cfg_t *cfg) - static void - restore_instruction_order(struct cfg_t *cfg, fs_inst **inst_arr) - { -- int num_insts = cfg->last_block()->end_ip + 1; -+ ASSERTED int num_insts = cfg->last_block()->end_ip + 1; - - int ip = 0; - foreach_block (block, cfg) { --- -2.43.0 - diff --git a/spec_files/mesa/0001-intel-compiler-reemit-boolean-resolve-for-inverted-i.patch b/spec_files/mesa/0001-intel-compiler-reemit-boolean-resolve-for-inverted-i.patch deleted file mode 100644 index dd7a9b540f..0000000000 --- a/spec_files/mesa/0001-intel-compiler-reemit-boolean-resolve-for-inverted-i.patch +++ /dev/null @@ -1,42 +0,0 @@ -From 56a72e014fcda3c52cf119115cb71fce2fad86d8 Mon Sep 17 00:00:00 2001 -From: Dave Airlie -Date: Thu, 21 Dec 2023 10:39:08 +1000 -Subject: [PATCH] intel/compiler: reemit boolean resolve for inverted if on - gen5 - -Gen5 adds some boolean conversion instructions after nir emits, -but that nir srcs don't line up with them, so reemit the boolean -conversion if we reemit the inot. - -Reviewed-by: Lionel Landwerlin -Fixes: 31b5f5a51f3a ("nir/opt_if: Simplify if's with general conditions") -Part-of: ---- - src/intel/compiler/brw_fs_nir.cpp | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/src/intel/compiler/brw_fs_nir.cpp b/src/intel/compiler/brw_fs_nir.cpp -index 33f2a4046b2..ccdd0fe7db8 100644 ---- a/src/intel/compiler/brw_fs_nir.cpp -+++ b/src/intel/compiler/brw_fs_nir.cpp -@@ -422,6 +422,17 @@ fs_visitor::nir_emit_if(nir_if *if_stmt) - invert = true; - cond_reg = get_nir_src(cond->src[0].src); - cond_reg = offset(cond_reg, bld, cond->src[0].swizzle[0]); -+ -+ if (devinfo->ver <= 5 && -+ (cond->instr.pass_flags & BRW_NIR_BOOLEAN_MASK) == BRW_NIR_BOOLEAN_NEEDS_RESOLVE) { -+ /* redo boolean resolve on gen5 */ -+ fs_reg masked = bld.vgrf(BRW_REGISTER_TYPE_D); -+ bld.AND(masked, cond_reg, brw_imm_d(1)); -+ masked.negate = true; -+ fs_reg tmp = bld.vgrf(cond_reg.type); -+ bld.MOV(retype(tmp, BRW_REGISTER_TYPE_D), masked); -+ cond_reg = tmp; -+ } - } else { - invert = false; - cond_reg = get_nir_src(if_stmt->condition); --- -2.43.0 - diff --git a/spec_files/mesa/disable-zink-egl-fallback.patch b/spec_files/mesa/disable-zink-egl-fallback.patch deleted file mode 100644 index c036acead2..0000000000 --- a/spec_files/mesa/disable-zink-egl-fallback.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/src/egl/main/eglapi.c b/src/egl/main/eglapi.c -index d50be23e871..e3697622635 100644 ---- a/src/egl/main/eglapi.c -+++ b/src/egl/main/eglapi.c -@@ -695,17 +695,21 @@ eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) - if (disp->Options.ForceSoftware) - RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE); - else { -+#if 0 - bool success = false; - if (!disp->Options.Zink && !getenv("GALLIUM_DRIVER")) { - disp->Options.Zink = EGL_TRUE; - success = _eglDriver.Initialize(disp); - } - if (!success) { -+#endif - disp->Options.Zink = EGL_FALSE; - disp->Options.ForceSoftware = EGL_TRUE; - if (!_eglDriver.Initialize(disp)) - RETURN_EGL_ERROR(disp, EGL_NOT_INITIALIZED, EGL_FALSE); -+#if 0 - } -+#endif - } - } - diff --git a/spec_files/mesa/mesa-meson-c99.patch b/spec_files/mesa/mesa-meson-c99.patch new file mode 100644 index 0000000000..6cbb053621 --- /dev/null +++ b/spec_files/mesa/mesa-meson-c99.patch @@ -0,0 +1,42 @@ +meson: C type error in strtod_l/strtof_l probe + +Future compilers will fail compilation due to the C type error: + +…/testfile.c: In function 'main': +…/testfile.c:12:30: error: passing argument 2 of 'strtod_l' from incompatible pointer type + 12 | double d = strtod_l(s, end, loc); + | ^~~ + | | + | char * +/usr/include/stdlib.h:416:43: note: expected 'char ** restrict' but argument is of type 'char *' + 416 | char **__restrict __endptr, locale_t __loc) + | ~~~~~~~~~~~~~~~~~~^~~~~~~~ +…/testfile.c:13:29: error: passing argument 2 of 'strtof_l' from incompatible pointer type + 13 | float f = strtof_l(s, end, loc); + | ^~~ + | | + | char * +/usr/include/stdlib.h:420:42: note: expected 'char ** restrict' but argument is of type 'char *' + 420 | char **__restrict __endptr, locale_t __loc) + | ~~~~~~~~~~~~~~~~~~^~~~~~~~ + +This means that the probe no longer tests is objective and always +fails. + +Submitted upstream: + +diff --git a/meson.build b/meson.build +index 35cc5f1cd5fd9079..1a5d2ba492be0b31 100644 +--- a/meson.build ++++ b/meson.build +@@ -1425,8 +1425,8 @@ if cc.links(''' + locale_t loc = newlocale(LC_CTYPE_MASK, "C", NULL); + const char *s = "1.0"; + char *end; +- double d = strtod_l(s, end, loc); +- float f = strtof_l(s, end, loc); ++ double d = strtod_l(s, &end, loc); ++ float f = strtof_l(s, &end, loc); + freelocale(loc); + return 0; + }''', diff --git a/spec_files/mesa/mesa.spec b/spec_files/mesa/mesa.spec index 3eeb61545e..5d3971e335 100644 --- a/spec_files/mesa/mesa.spec +++ b/spec_files/mesa/mesa.spec @@ -61,7 +61,7 @@ Name: mesa Summary: Mesa graphics libraries -%global ver 23.3.2 +%global ver 23.3.3 Version: %{lua:ver = string.gsub(rpm.expand("%{ver}"), "-", "~"); print(ver)} Release: 100.bazzite.{{{ git_dir_version }}} License: MIT AND BSD-3-Clause AND SGI-B-2.0 @@ -74,9 +74,8 @@ Source0: https://archive.mesa3d.org/mesa-%{ver}.tar.xz Source1: Mesa-MLAA-License-Clarification-Email.txt Patch10: gnome-shell-glthread-disable.patch -Patch11: 0001-intel-compiler-reemit-boolean-resolve-for-inverted-i.patch -Patch12: 0001-intel-compiler-fix-release-build-unused-variable.patch -Patch13: disable-zink-egl-fallback.patch +Patch11: zink-fix-resizable-bar-detection-logic.patch +Patch12: mesa-meson-c99.patch # https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26105/ Patch30: 26105.patch diff --git a/spec_files/mesa/zink-fix-resizable-bar-detection-logic.patch b/spec_files/mesa/zink-fix-resizable-bar-detection-logic.patch new file mode 100644 index 0000000000..4a5e916d52 --- /dev/null +++ b/spec_files/mesa/zink-fix-resizable-bar-detection-logic.patch @@ -0,0 +1,39 @@ +From a077c14f150f1c4f670dce381ac2eb548f1a4ac2 Mon Sep 17 00:00:00 2001 +From: Alessandro Astone +Date: Wed, 10 Jan 2024 17:24:30 +0100 +Subject: [PATCH] zink: Fix resizable BAR detection logic + +This was broken in two ways: +* When looking for the MAX biggest_ram it was actually comparing + a candidate against biggest_vis_ram + +* mem_props.memoryTypes[] should be accessed with the memory type + index as found in heap_map + +Closes: https://gitlab.freedesktop.org/mesa/mesa/-/issues/10341 +Cc: 23.3 +Part-of: +--- + src/gallium/drivers/zink/zink_screen.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/gallium/drivers/zink/zink_screen.c b/src/gallium/drivers/zink/zink_screen.c +index 5a6d17cb4fa3..6697d7ab938c 100644 +--- a/src/gallium/drivers/zink/zink_screen.c ++++ b/src/gallium/drivers/zink/zink_screen.c +@@ -3258,10 +3258,10 @@ zink_internal_create_screen(const struct pipe_screen_config *config, int64_t dev + { + uint64_t biggest_vis_vram = 0; + for (unsigned i = 0; i < screen->heap_count[ZINK_HEAP_DEVICE_LOCAL_VISIBLE]; i++) +- biggest_vis_vram = MAX2(biggest_vis_vram, screen->info.mem_props.memoryHeaps[screen->info.mem_props.memoryTypes[i].heapIndex].size); ++ biggest_vis_vram = MAX2(biggest_vis_vram, screen->info.mem_props.memoryHeaps[screen->info.mem_props.memoryTypes[screen->heap_map[ZINK_HEAP_DEVICE_LOCAL_VISIBLE][i]].heapIndex].size); + uint64_t biggest_vram = 0; + for (unsigned i = 0; i < screen->heap_count[ZINK_HEAP_DEVICE_LOCAL]; i++) +- biggest_vram = MAX2(biggest_vis_vram, screen->info.mem_props.memoryHeaps[screen->info.mem_props.memoryTypes[i].heapIndex].size); ++ biggest_vram = MAX2(biggest_vram, screen->info.mem_props.memoryHeaps[screen->info.mem_props.memoryTypes[screen->heap_map[ZINK_HEAP_DEVICE_LOCAL][i]].heapIndex].size); + /* determine if vis vram is roughly equal to total vram */ + if (biggest_vis_vram > biggest_vram * 0.9) + screen->resizable_bar = true; +-- +GitLab + diff --git a/spec_files/pipewire/pipewire.spec b/spec_files/pipewire/pipewire.spec index d941963c56..bb931bc013 100644 --- a/spec_files/pipewire/pipewire.spec +++ b/spec_files/pipewire/pipewire.spec @@ -1,6 +1,6 @@ %global majorversion 1 %global minorversion 0 -%global microversion 0 +%global microversion 1 %global apiversion 0.3 %global spaversion 0.2 @@ -81,7 +81,7 @@ Source1: pipewire.sysusers # Holo: TODO: Bug reference Patch0: bc435841c141ad38768b6cb1a7ad45e8bb13c7d2.patch # Holo: TODO: Bug reference -Patch1: acf7c0af0bf31b937c41e916a73c67ae0a253632.patch +#Patch1: acf7c0af0bf31b937c41e916a73c67ae0a253632.patch # Holo: upstream MR 1792 Patch2: 0001-Bluez5-backend-native-HSP-AG-release-SCO-link-on-AT-.patch @@ -818,6 +818,12 @@ systemctl --no-reload preset --global pipewire.socket >/dev/null 2>&1 || : %endif %changelog +* Thu Jan 11 2024 Wim Taymans - 1.0.1-1 +- Update version to 1.0.1 + +* Thu Dec 14 2023 Wim Taymans - 1.0.0-2 +- Add patch to avoid crash in deviceprovider. + * Sun Nov 26 2023 Wim Taymans - 1.0.0-1 - Update version to 1.0.0 - Disable ROC until updated in Fedora. diff --git a/spec_files/steamdeck-dsp/bazzite.patch b/spec_files/steamdeck-dsp/bazzite.patch new file mode 100644 index 0000000000..be71df44a2 --- /dev/null +++ b/spec_files/steamdeck-dsp/bazzite.patch @@ -0,0 +1,57 @@ +--- a/wireplumber/hardware-profiles/wireplumber-hwconfig ++++ b/wireplumber/hardware-profiles/wireplumber-hwconfig +@@ -45,39 +45,23 @@ + install_hwprofile () + { + local -r prpath="$1" +- local -r runconf=/run/wireplumber ++ local -r confdir=/usr/share/wireplumber + local confd + local conffile + +- echo "Installing wireplumber hardware profile from '$prpath' in $runconf" +- +- rm -rf $runconf ++ echo "Installing wireplumber hardware profile from '$prpath' in $confdir" + + for confd in "${confdirs[@]}" + do + if [ -d "$prpath"/"$confd" ] + then +- mkdir -p $runconf/"$confd" ++ mkdir -p $confdir/"$confd" + for conffile in "$prpath"/"$confd"/*.lua + do +- cp -av $conffile $runconf/"$confd" ++ /usr/bin/cp -avf $conffile $confdir/"$confd" + done + fi + done +- +- cat - < $runconf/README +-This configuration was: +- +- - installed in $runconf +- - by $0 +- - from $prpath +- +-It will be regenerated every time the system restarts. +- +-To alter it permanently either: +- - edit $prpath +- - override in \$XDG_CONFIG_DIR/wireplumber/ +-EOF + } + + unidentified () +--- a/wireplumber/systemd/system/wireplumber-sysconf.service ++++ b/wireplumber/systemd/system/wireplumber-sysconf.service +@@ -11,7 +11,7 @@ + Description=Hardware Specific Wireplumber Configuration + After=multi-user.target + Requisite=multi-user.target +-ConditionPathIsDirectory=/run ++Requires=wireplumber-workaround.service + Before=shutdown.target + + [Service] diff --git a/spec_files/steamdeck-dsp/steamdeck-dsp.spec b/spec_files/steamdeck-dsp/steamdeck-dsp.spec index 134e667c47..2fccf73e12 100644 --- a/spec_files/steamdeck-dsp/steamdeck-dsp.spec +++ b/spec_files/steamdeck-dsp/steamdeck-dsp.spec @@ -7,6 +7,7 @@ URL: https://github.com/ublue-os/bazzite Source: https://gitlab.com/evlaV/valve-hardware-audio-processing/-/archive/main/valve-hardware-audio-processing-main.tar.gz Patch0: fedora.patch +Patch1: bazzite.patch Requires: pipewire-module-filter-chain-lv2 Requires: ladspa-noise-suppression-for-voice @@ -29,8 +30,7 @@ Steamdeck Audio Processing %define debug_package %{nil} %prep -%setup -n valve-hardware-audio-processing-main -%patch 0 -p1 +%autosetup -n valve-hardware-audio-processing-main -p1 %build %make_build FAUSTINC="/usr/include/faust" FAUSTLIB="/usr/share/faust" diff --git a/spec_files/steamdeck-kde-presets/bazzite_logo.patch b/spec_files/steamdeck-kde-presets/bazzite_logo.patch new file mode 100644 index 0000000000..0f5b39af02 --- /dev/null +++ b/spec_files/steamdeck-kde-presets/bazzite_logo.patch @@ -0,0 +1,24 @@ +diff -Naur a/usr/share/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/Splash.qml b/usr/share/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/Splash.qml +--- a/usr/share/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/Splash.qml ++++ b/usr/share/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/Splash.qml +@@ -42,7 +42,7 @@ + + anchors.centerIn: parent + +- source: "images/deck_logo.svgz" ++ source: "images/bazzite_logo.svgz" + + sourceSize.width: 128 + sourceSize.height: 128 +diff -Naur a/usr/share/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/Splash.qml b/usr/share/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/Splash.qml +--- a/usr/share/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/Splash.qml ++++ b/usr/share/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/Splash.qml +@@ -42,7 +42,7 @@ + + anchors.centerIn: parent + +- source: "images/deck_logo.svgz" ++ source: "images/bazzite_logo.svgz" + + sourceSize.width: 128 + sourceSize.height: 128 diff --git a/spec_files/steamdeck-kde-presets/bazzite_logo.svgz b/spec_files/steamdeck-kde-presets/bazzite_logo.svgz new file mode 100644 index 0000000000..94510627da Binary files /dev/null and b/spec_files/steamdeck-kde-presets/bazzite_logo.svgz differ diff --git a/spec_files/steamdeck-kde-presets/steamdeck-kde-presets-desktop.spec b/spec_files/steamdeck-kde-presets/steamdeck-kde-presets-desktop.spec index 782f58a735..9dedec1271 100644 --- a/spec_files/steamdeck-kde-presets/steamdeck-kde-presets-desktop.spec +++ b/spec_files/steamdeck-kde-presets/steamdeck-kde-presets-desktop.spec @@ -8,8 +8,10 @@ URL: https://github.com/ublue-os/bazzite Source0: https://gitlab.com/evlaV/steamdeck-kde-presets/-/archive/master/steamdeck-kde-presets-master.tar.gz Source1: kdeglobals-desktop Source2: steamdeck-le.svg +Source3: bazzite_logo.svgz Patch0: multiuser.patch Patch1: lockscreen.patch +Patch2: bazzite_logo.patch BuildArch: noarch @@ -61,6 +63,10 @@ rm %{buildroot}%{_sysconfdir}/xdg/baloofilerc rm %{buildroot}%{_sysconfdir}/xdg/kdeglobals rm %{buildroot}%{_sysconfdir}/xdg/kcm-about-distrorc cp %{SOURCE1} %{buildroot}%{_sysconfdir}/xdg/kdeglobals +rm %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/images/deck_logo.svgz +rm %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/images/deck_logo.svgz +cp %{SOURCE3} %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/images/bazzite_logo.svgz +cp %{SOURCE3} %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/images/bazzite_logo.svgz # Do post-installation %post diff --git a/spec_files/steamdeck-kde-presets/steamdeck-kde-presets.spec b/spec_files/steamdeck-kde-presets/steamdeck-kde-presets.spec index 03b7f8a52b..fc62fb3e3f 100644 --- a/spec_files/steamdeck-kde-presets/steamdeck-kde-presets.spec +++ b/spec_files/steamdeck-kde-presets/steamdeck-kde-presets.spec @@ -7,10 +7,12 @@ URL: https://github.com/ublue-os/bazzite Source0: https://gitlab.com/evlaV/%{name}/-/archive/master/%{name}-master.tar.gz Source1: steamdeck-le.svg +Source2: bazzite_logo.svgz BuildArch: noarch Patch0: fedora.patch Patch1: nested-desktop-resolution.patch Patch2: kdeglobals.patch +Patch3: bazzite_logo.patch Requires: kde-filesystem @@ -47,6 +49,10 @@ rm %{buildroot}%{_datadir}/applications/org.mozilla.firefox.desktop rm %{buildroot}%{_sysconfdir}/profile.d/kde.sh rm %{buildroot}%{_sysconfdir}/xdg/kcm-about-distrorc rm %{buildroot}%{_sysconfdir}/X11/Xsession.d/50rotate-screen +rm %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/images/deck_logo.svgz +rm %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/images/deck_logo.svgz +cp %{SOURCE2} %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vapor.desktop/contents/splash/images/bazzite_logo.svgz +cp %{SOURCE2} %{buildroot}%{_datadir}/plasma/look-and-feel/com.valve.vgui.desktop/contents/splash/images/bazzite_logo.svgz # Do post-installation %post diff --git a/spec_files/wireplumber/steamdeck.patch b/spec_files/wireplumber/valve.patch similarity index 53% rename from spec_files/wireplumber/steamdeck.patch rename to spec_files/wireplumber/valve.patch index dcd8e7c5a3..f0e97df8ba 100644 --- a/spec_files/wireplumber/steamdeck.patch +++ b/spec_files/wireplumber/valve.patch @@ -1,25 +1,14 @@ -diff --git a/modules/meson.build b/modules/meson.build -index 4930bfae..4a337017 100644 ---- a/modules/meson.build -+++ b/modules/meson.build -@@ -159,6 +159,17 @@ shared_library( - dependencies : [wp_dep, pipewire_dep], - ) - -+shared_library( -+ 'wireplumber-module-filters-api', -+ [ -+ 'module-filters-api.c', -+ ], -+ c_args : [common_c_args, '-DG_LOG_DOMAIN="m-filters-api"'], -+ install : true, -+ install_dir : wireplumber_module_dir, -+ dependencies : [wp_dep, pipewire_dep], -+) -+ - if libsystemd_dep.found() or libelogind_dep.found() - shared_library( - 'wireplumber-module-logind', +From 9911c8532eb9072d93eb39b02aa94699f0a735a1 Mon Sep 17 00:00:00 2001 +From: Julian Bouzas +Date: Tue, 21 Mar 2023 09:18:30 -0400 +Subject: [PATCH 01/11] m-default-nodes: remove echo-cancel configuration + +This will be possible to do with the new module-filters-api. +--- + modules/module-default-nodes.c | 75 -------------------- + src/config/main.lua.d/40-device-defaults.lua | 9 --- + 2 files changed, 84 deletions(-) + diff --git a/modules/module-default-nodes.c b/modules/module-default-nodes.c index aaf29389..3bb93f00 100644 --- a/modules/module-default-nodes.c @@ -178,12 +167,84 @@ index aaf29389..3bb93f00 100644 NULL)); return TRUE; } +diff --git a/src/config/main.lua.d/40-device-defaults.lua b/src/config/main.lua.d/40-device-defaults.lua +index 19202914..91c4e189 100644 +--- a/src/config/main.lua.d/40-device-defaults.lua ++++ b/src/config/main.lua.d/40-device-defaults.lua +@@ -10,15 +10,6 @@ device_defaults.properties = { + -- the default volumes to apply to ACP device nodes, in the linear scale + --["default-volume"] = 0.064, + --["default-input-volume"] = 1.0, +- +- -- Whether to auto-switch to echo cancel sink and source nodes or not +- ["auto-echo-cancel"] = true, +- +- -- Sets the default echo-cancel-sink node name to automatically switch to +- ["echo-cancel-sink-name"] = "echo-cancel-sink", +- +- -- Sets the default echo-cancel-source node name to automatically switch to +- ["echo-cancel-source-name"] = "echo-cancel-source", + } + + -- Sets persistent device profiles that should never change when wireplumber is +-- +2.42.0 + + +From a9b6f3bcb4c525c1e257873875feb6223fa6b660 Mon Sep 17 00:00:00 2001 +From: Julian Bouzas +Date: Fri, 17 Mar 2023 11:06:11 -0400 +Subject: [PATCH 02/11] modules: add new module-filters-api to enable smart + filter policy + +This module provides an API to link filter nodes using the logic configured in +'policy.lua.d/30-filters-config.lua'. This configuration file allows grouping +filters together (sorted by priority) to use a common target node. A node is +considered a filter node if it has the node.link-group property. + +This is disabled by default. You can enable smart filters policy in the +'10-default-policy.lya' configuration file. +--- + modules/meson.build | 11 + + modules/module-filters-api.c | 905 ++++++++++++++++++ + modules/module-lua-scripting/api/json.c | 2 +- + src/config/policy.lua.d/10-default-policy.lua | 9 + + src/config/policy.lua.d/30-filters-config.lua | 73 ++ + src/scripts/filters-metadata.lua | 39 + + src/scripts/policy-node.lua | 120 ++- + 7 files changed, 1156 insertions(+), 3 deletions(-) + create mode 100644 modules/module-filters-api.c + create mode 100644 src/config/policy.lua.d/30-filters-config.lua + create mode 100644 src/scripts/filters-metadata.lua + +diff --git a/modules/meson.build b/modules/meson.build +index 4930bfae..4a337017 100644 +--- a/modules/meson.build ++++ b/modules/meson.build +@@ -159,6 +159,17 @@ shared_library( + dependencies : [wp_dep, pipewire_dep], + ) + ++shared_library( ++ 'wireplumber-module-filters-api', ++ [ ++ 'module-filters-api.c', ++ ], ++ c_args : [common_c_args, '-DG_LOG_DOMAIN="m-filters-api"'], ++ install : true, ++ install_dir : wireplumber_module_dir, ++ dependencies : [wp_dep, pipewire_dep], ++) ++ + if libsystemd_dep.found() or libelogind_dep.found() + shared_library( + 'wireplumber-module-logind', diff --git a/modules/module-filters-api.c b/modules/module-filters-api.c new file mode 100644 -index 00000000..8852a6c5 +index 00000000..32c67c85 --- /dev/null +++ b/modules/module-filters-api.c -@@ -0,0 +1,920 @@ +@@ -0,0 +1,905 @@ +/* WirePlumber + * + * Copyright © 2023 Collabora Ltd. @@ -235,12 +296,6 @@ index 00000000..8852a6c5 +}; +typedef struct _Filter Filter; + -+struct _Target { -+ gboolean exclusive; -+ WpNode *node; -+}; -+typedef struct _Target Target; -+ +static guint +get_filter_priority (const gchar *link_group) +{ @@ -279,22 +334,6 @@ index 00000000..8852a6c5 + g_free (f); +} + -+static Target * -+target_new (gboolean exclusive, WpNode *node) -+{ -+ Target *t = g_malloc0 (sizeof (Target)); -+ t->exclusive = exclusive; -+ t->node = node ? g_object_ref (node) : NULL; -+ return t; -+} -+ -+static void -+target_free (Target *t) -+{ -+ g_clear_object (&t->node); -+ g_free (t); -+} -+ +static gint +filter_equal_func (const Filter *f, const gchar *link_group) +{ @@ -341,18 +380,16 @@ index 00000000..8852a6c5 + return found->enabled; +} + -+static WpSpaJson * ++static gint +wp_filters_api_get_filter_target (WpFiltersApi * self, const gchar *direction, + const gchar *link_group) +{ + WpDirection dir = WP_DIRECTION_INPUT; + GList *filters; + Filter *found; -+ g_autoptr (WpSpaJson) res = wp_spa_json_new_object ( -+ "exclusive", "b", FALSE, "bound_id", "i", -1, NULL); + -+ g_return_val_if_fail (direction, g_steal_pointer (&res)); -+ g_return_val_if_fail (link_group, g_steal_pointer (&res)); ++ g_return_val_if_fail (direction, -1); ++ g_return_val_if_fail (link_group, -1); + + /* Get the filters for the given direction */ + if (g_str_equal (direction, "output") || g_str_equal (direction, "Output")) @@ -363,10 +400,10 @@ index 00000000..8852a6c5 + filters = g_list_find_custom (filters, link_group, + (GCompareFunc) filter_equal_func); + if (!filters) -+ return g_steal_pointer (&res); ++ return -1; + found = filters->data; + if (!found->enabled) -+ return g_steal_pointer (&res); ++ return -1; + + /* Return the previous filter with matching target that is enabled */ + while ((filters = g_list_previous (filters))) { @@ -374,27 +411,18 @@ index 00000000..8852a6c5 + if ((prev->target == found->target || + (prev->target && found->target && + g_str_equal (prev->target, found->target))) && -+ prev->enabled) { -+ return wp_spa_json_new_object ( -+ "exclusive", "b", FALSE, -+ "bound_id", "i", wp_proxy_get_bound_id (WP_PROXY (prev->node)), -+ NULL); -+ } ++ prev->enabled) ++ return wp_proxy_get_bound_id (WP_PROXY (prev->node)); + } + + /* Find the target */ + if (found->target) { -+ Target *t = g_hash_table_lookup (self->targets, found->target); -+ if (t) { -+ return wp_spa_json_new_object ( -+ "exclusive", "b", t->exclusive, -+ "bound_id", "i", -+ t->node ? (gint)wp_proxy_get_bound_id (WP_PROXY (t->node)) : -1, -+ NULL); -+ } ++ WpNode *node = g_hash_table_lookup (self->targets, found->target); ++ if (node) ++ return wp_proxy_get_bound_id (WP_PROXY (node)); + } + -+ return g_steal_pointer (&res); ++ return -1; +} + +static gint @@ -417,17 +445,12 @@ index 00000000..8852a6c5 + /* Find the first target matching target_id */ + while (filters) { + Filter *f = (Filter *) filters->data; -+ if (f->enabled) { -+ gint f_target_id; -+ g_autoptr (WpSpaJson) f_target = wp_filters_api_get_filter_target (self, -+ direction, f->link_group); -+ if (f_target && wp_spa_json_is_object (f_target) && -+ wp_spa_json_object_get (f_target, "bound_id", "i", &f_target_id, NULL) -+ && f_target_id == target_id) { -+ target = f->target; -+ found = TRUE; -+ break; -+ } ++ gint f_target_id = wp_filters_api_get_filter_target (self, direction, ++ f->link_group); ++ if (f_target_id == target_id && f->enabled) { ++ target = f->target; ++ found = TRUE; ++ break; + } + + /* Advance */ @@ -454,6 +477,33 @@ index 00000000..8852a6c5 + return res; +} + ++static gint ++wp_filters_api_get_default_filter (WpFiltersApi * self, const gchar *direction) ++{ ++ WpDirection dir = WP_DIRECTION_INPUT; ++ GList *filters; ++ ++ g_return_val_if_fail (direction, -1); ++ ++ /* Get the filters for the given direction */ ++ if (g_str_equal (direction, "output") || g_str_equal (direction, "Output")) ++ dir = WP_DIRECTION_OUTPUT; ++ filters = self->filters[dir]; ++ ++ /* The default filter is the highest priority filter without target, this is ++ * the first filer that is enabled because the list is sorted by priority */ ++ while (filters) { ++ Filter *f = (Filter *) filters->data; ++ if (f->enabled && !f->target) ++ return wp_proxy_get_bound_id (WP_PROXY (f->node)); ++ ++ /* Advance */ ++ filters = g_list_next (filters); ++ } ++ ++ return -1; ++} ++ +static void +sync_changed (WpCore * core, GAsyncResult * res, WpFiltersApi * self) +{ @@ -547,11 +597,9 @@ index 00000000..8852a6c5 + for (; wp_iterator_next (it, &item); g_value_unset (&item)) { + WpSpaJson *j = g_value_get_boxed (&item); + g_autofree gchar *key = NULL; -+ WpSpaJson *value; -+ gboolean exclusive = FALSE; -+ g_autoptr (WpSpaJson) props = NULL; -+ g_autoptr (WpNode) target_node = NULL; -+ Target *curr_target; ++ WpSpaJson *props; ++ g_autoptr (WpNode) target = NULL; ++ WpNode *curr_target; + + key = wp_spa_json_parse_string (j); + g_value_unset (&item); @@ -560,36 +608,27 @@ index 00000000..8852a6c5 + "Could not get valid key-value pairs from target object"); + break; + } -+ value = g_value_get_boxed (&item); -+ if (!value || !wp_spa_json_is_object (value)) { -+ wp_warning_object (self, "Target value must be a JSON object"); -+ break; -+ } -+ -+ /* Get exclusive */ -+ wp_spa_json_object_get (value, "exclusive", "b", &exclusive, NULL); -+ -+ /* Get target node */ -+ wp_spa_json_object_get (value, "props", "J", &props, NULL); -+ if (props) -+ target_node = find_target_node (self, props); ++ props = g_value_get_boxed (&item); + -+ /* Update values if target exists in the table, otherwise add new target */ ++ /* Get current target */ + curr_target = g_hash_table_lookup (self->targets, key); -+ if (curr_target) { -+ if (curr_target->exclusive != exclusive) { -+ curr_target->exclusive = exclusive; -+ changed = TRUE; -+ } -+ if (curr_target->node != target_node) { -+ g_clear_object (&curr_target->node); -+ curr_target->node = g_steal_pointer (&target_node); -+ changed = TRUE; ++ ++ /* Find the node and insert it into the table if found */ ++ target = find_target_node (self, props); ++ if (target) { ++ /* Check if the target changed */ ++ if (curr_target) { ++ guint32 target_bound_id = wp_proxy_get_bound_id (WP_PROXY (target)); ++ guint32 curr_bound_id = wp_proxy_get_bound_id (WP_PROXY (curr_target)); ++ if (target_bound_id != curr_bound_id) ++ changed = TRUE; + } -+ } else { ++ + g_hash_table_insert (self->targets, g_strdup (key), -+ target_new (exclusive, target_node)); -+ changed = TRUE; ++ g_steal_pointer (&target)); ++ } else { ++ if (curr_target) ++ changed = TRUE; + } + } + @@ -1025,7 +1064,7 @@ index 00000000..8852a6c5 + g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self)); + + self->targets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, -+ (GDestroyNotify) target_free); ++ g_object_unref); + + /* Create the metadata object manager */ + self->metadata_om = wp_object_manager_new (); @@ -1080,7 +1119,7 @@ index 00000000..8852a6c5 + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + (GCallback) wp_filters_api_get_filter_target, + NULL, NULL, NULL, -+ WP_TYPE_SPA_JSON, 2, G_TYPE_STRING, G_TYPE_STRING); ++ G_TYPE_INT, 2, G_TYPE_STRING, G_TYPE_STRING); + + signals[ACTION_GET_FILTER_FROM_TARGET] = g_signal_new_class_handler ( + "get-filter-from-target", G_TYPE_FROM_CLASS (klass), @@ -1089,6 +1128,13 @@ index 00000000..8852a6c5 + NULL, NULL, NULL, + G_TYPE_INT, 2, G_TYPE_STRING, G_TYPE_INT); + ++ signals[ACTION_GET_DEFAULT_FILTER] = g_signal_new_class_handler ( ++ "get-default-filter", G_TYPE_FROM_CLASS (klass), ++ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, ++ (GCallback) wp_filters_api_get_default_filter, ++ NULL, NULL, NULL, ++ G_TYPE_INT, 1, G_TYPE_STRING); ++ + signals[SIGNAL_CHANGED] = g_signal_new ( + "changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, @@ -1117,62 +1163,8 @@ index 2883a021..3d486be8 100644 lua_typename(L, lua_type(L, -1))); break; } -diff --git a/src/config/main.lua.d/40-device-defaults.lua b/src/config/main.lua.d/40-device-defaults.lua -index 19202914..b87eec51 100644 ---- a/src/config/main.lua.d/40-device-defaults.lua -+++ b/src/config/main.lua.d/40-device-defaults.lua -@@ -10,15 +10,6 @@ device_defaults.properties = { - -- the default volumes to apply to ACP device nodes, in the linear scale - --["default-volume"] = 0.064, - --["default-input-volume"] = 1.0, -- -- -- Whether to auto-switch to echo cancel sink and source nodes or not -- ["auto-echo-cancel"] = true, -- -- -- Sets the default echo-cancel-sink node name to automatically switch to -- ["echo-cancel-sink-name"] = "echo-cancel-sink", -- -- -- Sets the default echo-cancel-source node name to automatically switch to -- ["echo-cancel-source-name"] = "echo-cancel-source", - } - - -- Sets persistent device profiles that should never change when wireplumber is -@@ -48,13 +39,13 @@ device_defaults.profile_priorities = { - }, - -- lower the index higher the priority - priorities = { -- -- "a2dp-sink-sbc", -- -- "a2dp-sink-aptx_ll", -- -- "a2dp-sink-aptx", -- -- "a2dp-sink-aptx_hd", -- -- "a2dp-sink-ldac", -- -- "a2dp-sink-aac", -- -- "a2dp-sink-sbc_xq", -+ "a2dp-sink-aptx_ll", -+ "a2dp-sink-aptx", -+ "a2dp-sink-aptx_hd", -+ "a2dp-sink-ldac", -+ "a2dp-sink-aac", -+ "a2dp-sink-sbc", -+ "a2dp-sink-sbc_xq", - } - }, - } -diff --git a/src/config/main.lua.d/40-stream-defaults.lua b/src/config/main.lua.d/40-stream-defaults.lua -index b869099b..d25aab0d 100644 ---- a/src/config/main.lua.d/40-stream-defaults.lua -+++ b/src/config/main.lua.d/40-stream-defaults.lua -@@ -6,7 +6,7 @@ stream_defaults.properties = { - ["restore-props"] = true, - - -- whether to restore the last stream target or not -- ["restore-target"] = true, -+ ["restore-target"] = false, - - -- the default channel volume for new streams whose props were never saved - -- previously. This is only used if "restore-props" is set to true. diff --git a/src/config/policy.lua.d/10-default-policy.lua b/src/config/policy.lua.d/10-default-policy.lua -index 83d0a3b2..412d47a8 100644 +index 83d0a3b2..d3621a73 100644 --- a/src/config/policy.lua.d/10-default-policy.lua +++ b/src/config/policy.lua.d/10-default-policy.lua @@ -12,6 +12,9 @@ default_policy.policy = { @@ -1180,7 +1172,7 @@ index 83d0a3b2..412d47a8 100644 ["filter.forward-format"] = false, + -- Whether to enable smart filter policy or not (experimental feature) -+ ["filter.smart"] = true, ++ ["filter.smart"] = false, + -- Set to 'true' to disable channel splitting & merging on nodes and enable -- passthrough of audio in the same format as the format of the device. @@ -1200,10 +1192,10 @@ index 83d0a3b2..412d47a8 100644 diff --git a/src/config/policy.lua.d/30-filters-config.lua b/src/config/policy.lua.d/30-filters-config.lua new file mode 100644 -index 00000000..8e8725fc +index 00000000..76aecad0 --- /dev/null +++ b/src/config/policy.lua.d/30-filters-config.lua -@@ -0,0 +1,80 @@ +@@ -0,0 +1,73 @@ +-- The smart filter policy configuration. +-- You need to enable "filter.smart" in 10-default-policy.lua +-- @@ -1265,31 +1257,24 @@ index 00000000..8e8725fc + } + }, + -+ -- The filter targets ++ -- The target node properties (any node properties can be defined) + ["targets"] = { + ["speakers"] = { -+ ["exclusive"] = false, -+ ["props"] = { -+ ["media.class"] = "Audio/Sink", -+ ["alsa.card_name"] = "sof-nau8821-max", -+ ["device.profile.description"] = "Speaker", -+ } ++ ["media.class"] = "Audio/Sink", ++ ["alsa.card_name"] = "my-speakers-card-name", + }, + ["microphone"] = { -+ ["exclusive"] = false, -+ ["props"] = { -+ ["media.class"] = "Audio/Source", -+ ["alsa.card_name"] = "sof-nau8821-max", -+ } ++ ["media.class"] = "Audio/Source", ++ ["alsa.card_name"] = "my-microphone-card-name", + } + } +} diff --git a/src/scripts/filters-metadata.lua b/src/scripts/filters-metadata.lua new file mode 100644 -index 00000000..49b4d0e8 +index 00000000..04e3e6c6 --- /dev/null +++ b/src/scripts/filters-metadata.lua -@@ -0,0 +1,42 @@ +@@ -0,0 +1,39 @@ +-- WirePlumber +-- +-- Copyright © 2023 Collabora Ltd. @@ -1322,87 +1307,15 @@ index 00000000..49b4d0e8 + + -- Set targets metadata + local targets = {} -+ for name, value in pairs(config["targets"]) do -+ targets[name] = Json.Object { -+ exclusive = value.exclusive and true or false, -+ props = Json.Object (value.props) -+ } ++ for name, props in pairs(config["targets"]) do ++ targets[name] = Json.Object (props) + end + local targets_json = Json.Object (targets) + m:set (0, "filters.configured.targets", "Spa:String:JSON", + targets_json:to_string()) +end) -diff --git a/src/scripts/policy-device-profile.lua b/src/scripts/policy-device-profile.lua -index af10f9b7..8d3a6d7e 100644 ---- a/src/scripts/policy-device-profile.lua -+++ b/src/scripts/policy-device-profile.lua -@@ -34,12 +34,12 @@ createIntrestObjects(self.config.persistent) - createIntrestObjects(self.config.priorities) - - -- Checks whether a device profile is persistent or not --function isProfilePersistent(device_props, profile_name) -- for _, p in ipairs(self.config.persistent or {}) do -+function isProfilePersistent (device_props, profile_name) -+ for _, p in ipairs (self.config.persistent or {}) do - if p.profile_names then -- for _, interest in ipairs(p.interests) do -- if interest:matches(device_props) then -- for _, pn in ipairs(p.profile_names) do -+ for _, interest in ipairs (p.interests) do -+ if interest:matches (device_props) then -+ for _, pn in ipairs (p.profile_names) do - if pn == profile_name then - return true - end -@@ -72,7 +72,7 @@ function setDeviceProfile (device, dev_id, dev_name, profile) - index = profile.index, - } - Log.info ("Setting profile " .. profile.name .. " on " .. dev_name) -- device:set_param("Profile", param) -+ device:set_param ("Profile", param) - end - - function findDefaultProfile (device) -@@ -85,8 +85,8 @@ function findDefaultProfile (device) - return nil - end - -- for p in device:iterate_params("EnumProfile") do -- local profile = parseParam(p, "EnumProfile") -+ for p in device:iterate_params ("EnumProfile") do -+ local profile = parseParam (p, "EnumProfile") - if profile.name == def_name then - return profile - end -@@ -189,7 +189,7 @@ function handleProfiles (device, new_device) - isProfilePersistent (device.properties, self.active_profiles[dev_id].name) and - def_profile ~= nil and - self.active_profiles[dev_id].name == def_profile.name -- then -+ then - local active_profile = self.active_profiles[dev_id].name - Log.info ("Device profile " .. active_profile .. " is persistent for " .. dev_name) - return -@@ -233,14 +233,14 @@ self.om = ObjectManager { - } - } - --self.om:connect("object-added", function (_, device) -+self.om:connect ("object-added", function(_, device) - device:connect ("params-changed", onDeviceParamsChanged) - handleProfiles (device, true) - end) - --self.om:connect("object-removed", function (_, device) -+self.om:connect ("object-removed", function(_, device) - local dev_id = device["bound-id"] - self.active_profiles[dev_id] = nil - end) - --self.om:activate() -+self.om:activate () diff --git a/src/scripts/policy-node.lua b/src/scripts/policy-node.lua -index 99ad8473..035c3006 100644 +index 99ad8473..f249f343 100644 --- a/src/scripts/policy-node.lua +++ b/src/scripts/policy-node.lua @@ -18,6 +18,7 @@ self.scanning = false @@ -1523,42 +1436,45 @@ index 99ad8473..035c3006 100644 -- Determine if we can handle item by this policy if endpoints_om:get_n_objects () > 0 and si_props["item.factory.name"] == "si-audio-adapter" then -@@ -652,6 +718,34 @@ function checkFollowDefault (si, si_target, has_node_defined_target) +@@ -652,6 +718,37 @@ function checkFollowDefault (si, si_target, has_node_defined_target) end end +function findFilterTarget (si) ++ local node = si:get_associated_proxy ("node") ++ local direction = getTargetDirection (si.properties) ++ local link_group = node.properties["node.link-group"] ++ local target_id = -1 ++ + -- always return nil if filters API is not loaded + if self.filters_api == nil then -+ return nil, false ++ return nil + end + -+ -- always return nil if this is not a filter -+ local node = si:get_associated_proxy ("node") -+ local link_group = node.properties["node.link-group"] + if link_group == nil then -+ return nil, false ++ -- if this is a client stream that is not a filter, link it to the highest ++ -- priority filter that does not have a group, if any. ++ target_id = self.filters_api:call("get-default-filter", direction) ++ else ++ -- if this is a filter, get its target ++ target_id = self.filters_api:call("get-filter-target", ++ direction, link_group) + end + -+ -- get the filter target -+ local direction = getTargetDirection (si.properties) -+ local target_json = self.filters_api:call("get-filter-target", direction, link_group) -+ if target_json == nil then -+ return nil, false ++ if (target_id == -1) then ++ return nil + end -+ target = target_json:parse() + -+ Log.info (".. filter target ID is " .. tostring(target.bound_id) .. -+ " (" .. tostring (target.exclusive) .. ")") ++ Log.info (".. filter target ID is " .. tostring(target_id)) + return linkables_om:lookup { -+ Constraint { "node.id", "=", tostring(target.bound_id) } -+ }, target.exclusive ++ Constraint { "node.id", "=", tostring(target_id) } ++ } +end + function handleLinkable (si) if checkPending () then return -@@ -683,11 +777,23 @@ function handleLinkable (si) +@@ -683,11 +780,19 @@ function handleLinkable (si) local si_target, has_defined_target, has_node_defined_target = findDefinedTarget(si_props) local can_passthrough = si_target and canPassthrough(si, si_target) @@ -1569,11 +1485,7 @@ index 99ad8473..035c3006 100644 + -- find filter target (always returns nil for non filters) + if si_target == nil then -+ si_target, exclusive = findFilterTarget(si) -+ -- don't fallback if filter target is not found and exclusive is true -+ if si_target == nil and exclusive then -+ return -+ end ++ si_target = findFilterTarget(si) + local can_passthrough = si_target and canPassthrough(si, si_target) + if si_target and si_must_passthrough and not can_passthrough then + si_target = nil @@ -1583,7 +1495,7 @@ index 99ad8473..035c3006 100644 -- if the client has seen a target that we haven't yet prepared, schedule -- a rescan one more time and hope for the best local si_id = si.id -@@ -876,6 +982,17 @@ if config.follow and default_nodes ~= nil then +@@ -876,6 +981,17 @@ if config.follow and default_nodes ~= nil then end) end @@ -1601,3 +1513,1301 @@ index 99ad8473..035c3006 100644 -- listen for target.node metadata changes if config.move is enabled if config.move then metadata_om:connect("object-added", function (om, metadata) +-- +2.42.0 + + +From bea7195a12ba1014f62e40f80c0f2e127a1aaa2e Mon Sep 17 00:00:00 2001 +From: Julian Bouzas +Date: Thu, 23 Mar 2023 09:26:45 -0400 +Subject: [PATCH 03/11] config: do not restore stream target by default + +--- + src/config/main.lua.d/40-stream-defaults.lua | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/config/main.lua.d/40-stream-defaults.lua b/src/config/main.lua.d/40-stream-defaults.lua +index b869099b..d25aab0d 100644 +--- a/src/config/main.lua.d/40-stream-defaults.lua ++++ b/src/config/main.lua.d/40-stream-defaults.lua +@@ -6,7 +6,7 @@ stream_defaults.properties = { + ["restore-props"] = true, + + -- whether to restore the last stream target or not +- ["restore-target"] = true, ++ ["restore-target"] = false, + + -- the default channel volume for new streams whose props were never saved + -- previously. This is only used if "restore-props" is set to true. +-- +2.42.0 + + +From f4d88774670d561b3a2d37c0d6534b204cdcb14f Mon Sep 17 00:00:00 2001 +From: Julian Bouzas +Date: Thu, 23 Mar 2023 09:41:23 -0400 +Subject: [PATCH 04/11] config: enable smart filter policy + +--- + src/config/policy.lua.d/10-default-policy.lua | 2 +- + src/config/policy.lua.d/30-filters-config.lua | 5 +++-- + 2 files changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/config/policy.lua.d/10-default-policy.lua b/src/config/policy.lua.d/10-default-policy.lua +index d3621a73..412d47a8 100644 +--- a/src/config/policy.lua.d/10-default-policy.lua ++++ b/src/config/policy.lua.d/10-default-policy.lua +@@ -13,7 +13,7 @@ default_policy.policy = { + ["filter.forward-format"] = false, + + -- Whether to enable smart filter policy or not (experimental feature) +- ["filter.smart"] = false, ++ ["filter.smart"] = true, + + -- Set to 'true' to disable channel splitting & merging on nodes and enable + -- passthrough of audio in the same format as the format of the device. +diff --git a/src/config/policy.lua.d/30-filters-config.lua b/src/config/policy.lua.d/30-filters-config.lua +index 76aecad0..8d919fb7 100644 +--- a/src/config/policy.lua.d/30-filters-config.lua ++++ b/src/config/policy.lua.d/30-filters-config.lua +@@ -63,11 +63,12 @@ default_policy.filters_metadata = { + ["targets"] = { + ["speakers"] = { + ["media.class"] = "Audio/Sink", +- ["alsa.card_name"] = "my-speakers-card-name", ++ ["alsa.card_name"] = "acp5x", ++ ["device.profile.description"] = "Speaker", + }, + ["microphone"] = { + ["media.class"] = "Audio/Source", +- ["alsa.card_name"] = "my-microphone-card-name", ++ ["alsa.card_name"] = "acp5x", + } + } + } +-- +2.42.0 + + +From 30d26697a83ce11c4a2343c0ad4ddb113e64f72b Mon Sep 17 00:00:00 2001 +From: Ashok Sidipotu +Date: Wed, 12 Jul 2023 10:17:37 +0530 +Subject: [PATCH 05/11] policy-device-profile.lua: introduce user profile + priority list + +--- + src/config/main.lua.d/40-device-defaults.lua | 14 +++++++------- + 1 file changed, 7 insertions(+), 7 deletions(-) + +diff --git a/src/config/main.lua.d/40-device-defaults.lua b/src/config/main.lua.d/40-device-defaults.lua +index 91c4e189..b87eec51 100644 +--- a/src/config/main.lua.d/40-device-defaults.lua ++++ b/src/config/main.lua.d/40-device-defaults.lua +@@ -39,13 +39,13 @@ device_defaults.profile_priorities = { + }, + -- lower the index higher the priority + priorities = { +- -- "a2dp-sink-sbc", +- -- "a2dp-sink-aptx_ll", +- -- "a2dp-sink-aptx", +- -- "a2dp-sink-aptx_hd", +- -- "a2dp-sink-ldac", +- -- "a2dp-sink-aac", +- -- "a2dp-sink-sbc_xq", ++ "a2dp-sink-aptx_ll", ++ "a2dp-sink-aptx", ++ "a2dp-sink-aptx_hd", ++ "a2dp-sink-ldac", ++ "a2dp-sink-aac", ++ "a2dp-sink-sbc", ++ "a2dp-sink-sbc_xq", + } + }, + } +-- +2.42.0 + + +From 4c6762c41a0d8f6298e2f35cea42a86ea3ef8ff9 Mon Sep 17 00:00:00 2001 +From: Julian Bouzas +Date: Fri, 3 Nov 2023 09:24:59 -0400 +Subject: [PATCH 06/11] m-filters-api: remove get-default-filter API + +This was redundant as you can get the default filter by calling +'get-filter-from-target' using the default target. The policy logic +should not change. +--- + modules/module-filters-api.c | 34 ---------------------------------- + src/scripts/policy-node.lua | 17 +++++++---------- + 2 files changed, 7 insertions(+), 44 deletions(-) + +diff --git a/modules/module-filters-api.c b/modules/module-filters-api.c +index 32c67c85..d7dc781a 100644 +--- a/modules/module-filters-api.c ++++ b/modules/module-filters-api.c +@@ -230,33 +230,6 @@ wp_filters_api_get_filter_from_target (WpFiltersApi * self, + return res; + } + +-static gint +-wp_filters_api_get_default_filter (WpFiltersApi * self, const gchar *direction) +-{ +- WpDirection dir = WP_DIRECTION_INPUT; +- GList *filters; +- +- g_return_val_if_fail (direction, -1); +- +- /* Get the filters for the given direction */ +- if (g_str_equal (direction, "output") || g_str_equal (direction, "Output")) +- dir = WP_DIRECTION_OUTPUT; +- filters = self->filters[dir]; +- +- /* The default filter is the highest priority filter without target, this is +- * the first filer that is enabled because the list is sorted by priority */ +- while (filters) { +- Filter *f = (Filter *) filters->data; +- if (f->enabled && !f->target) +- return wp_proxy_get_bound_id (WP_PROXY (f->node)); +- +- /* Advance */ +- filters = g_list_next (filters); +- } +- +- return -1; +-} +- + static void + sync_changed (WpCore * core, GAsyncResult * res, WpFiltersApi * self) + { +@@ -881,13 +854,6 @@ wp_filters_api_class_init (WpFiltersApiClass * klass) + NULL, NULL, NULL, + G_TYPE_INT, 2, G_TYPE_STRING, G_TYPE_INT); + +- signals[ACTION_GET_DEFAULT_FILTER] = g_signal_new_class_handler ( +- "get-default-filter", G_TYPE_FROM_CLASS (klass), +- G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, +- (GCallback) wp_filters_api_get_default_filter, +- NULL, NULL, NULL, +- G_TYPE_INT, 1, G_TYPE_STRING); +- + signals[SIGNAL_CHANGED] = g_signal_new ( + "changed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, +diff --git a/src/scripts/policy-node.lua b/src/scripts/policy-node.lua +index f249f343..c5a15ec6 100644 +--- a/src/scripts/policy-node.lua ++++ b/src/scripts/policy-node.lua +@@ -719,9 +719,6 @@ function checkFollowDefault (si, si_target, has_node_defined_target) + end + + function findFilterTarget (si) +- local node = si:get_associated_proxy ("node") +- local direction = getTargetDirection (si.properties) +- local link_group = node.properties["node.link-group"] + local target_id = -1 + + -- always return nil if filters API is not loaded +@@ -729,16 +726,16 @@ function findFilterTarget (si) + return nil + end + ++ -- always return nil if this is not a filter ++ local node = si:get_associated_proxy ("node") ++ local link_group = node.properties["node.link-group"] + if link_group == nil then +- -- if this is a client stream that is not a filter, link it to the highest +- -- priority filter that does not have a group, if any. +- target_id = self.filters_api:call("get-default-filter", direction) +- else +- -- if this is a filter, get its target +- target_id = self.filters_api:call("get-filter-target", +- direction, link_group) ++ return nil + end + ++ -- get the filter target ++ local direction = getTargetDirection (si.properties) ++ target_id = self.filters_api:call("get-filter-target", direction, link_group) + if (target_id == -1) then + return nil + end +-- +2.42.0 + + +From 1acb97e5606dfd842a71c9a2de520ee665f17fd0 Mon Sep 17 00:00:00 2001 +From: Julian Bouzas +Date: Fri, 3 Nov 2023 11:44:57 -0400 +Subject: [PATCH 07/11] m-filters-api: add support for exclusive targets + +Filters whose target are exclusive won't be linked to the default device if the +target does not exist. +--- + modules/module-filters-api.c | 127 ++++++++++++------ + src/config/policy.lua.d/30-filters-config.lua | 18 ++- + src/scripts/filters-metadata.lua | 7 +- + src/scripts/policy-node.lua | 26 ++-- + 4 files changed, 120 insertions(+), 58 deletions(-) + +diff --git a/modules/module-filters-api.c b/modules/module-filters-api.c +index d7dc781a..8852a6c5 100644 +--- a/modules/module-filters-api.c ++++ b/modules/module-filters-api.c +@@ -49,6 +49,12 @@ struct _Filter { + }; + typedef struct _Filter Filter; + ++struct _Target { ++ gboolean exclusive; ++ WpNode *node; ++}; ++typedef struct _Target Target; ++ + static guint + get_filter_priority (const gchar *link_group) + { +@@ -87,6 +93,22 @@ filter_free (Filter *f) + g_free (f); + } + ++static Target * ++target_new (gboolean exclusive, WpNode *node) ++{ ++ Target *t = g_malloc0 (sizeof (Target)); ++ t->exclusive = exclusive; ++ t->node = node ? g_object_ref (node) : NULL; ++ return t; ++} ++ ++static void ++target_free (Target *t) ++{ ++ g_clear_object (&t->node); ++ g_free (t); ++} ++ + static gint + filter_equal_func (const Filter *f, const gchar *link_group) + { +@@ -133,16 +155,18 @@ wp_filters_api_is_filter_enabled (WpFiltersApi * self, const gchar *direction, + return found->enabled; + } + +-static gint ++static WpSpaJson * + wp_filters_api_get_filter_target (WpFiltersApi * self, const gchar *direction, + const gchar *link_group) + { + WpDirection dir = WP_DIRECTION_INPUT; + GList *filters; + Filter *found; ++ g_autoptr (WpSpaJson) res = wp_spa_json_new_object ( ++ "exclusive", "b", FALSE, "bound_id", "i", -1, NULL); + +- g_return_val_if_fail (direction, -1); +- g_return_val_if_fail (link_group, -1); ++ g_return_val_if_fail (direction, g_steal_pointer (&res)); ++ g_return_val_if_fail (link_group, g_steal_pointer (&res)); + + /* Get the filters for the given direction */ + if (g_str_equal (direction, "output") || g_str_equal (direction, "Output")) +@@ -153,10 +177,10 @@ wp_filters_api_get_filter_target (WpFiltersApi * self, const gchar *direction, + filters = g_list_find_custom (filters, link_group, + (GCompareFunc) filter_equal_func); + if (!filters) +- return -1; ++ return g_steal_pointer (&res); + found = filters->data; + if (!found->enabled) +- return -1; ++ return g_steal_pointer (&res); + + /* Return the previous filter with matching target that is enabled */ + while ((filters = g_list_previous (filters))) { +@@ -164,18 +188,27 @@ wp_filters_api_get_filter_target (WpFiltersApi * self, const gchar *direction, + if ((prev->target == found->target || + (prev->target && found->target && + g_str_equal (prev->target, found->target))) && +- prev->enabled) +- return wp_proxy_get_bound_id (WP_PROXY (prev->node)); ++ prev->enabled) { ++ return wp_spa_json_new_object ( ++ "exclusive", "b", FALSE, ++ "bound_id", "i", wp_proxy_get_bound_id (WP_PROXY (prev->node)), ++ NULL); ++ } + } + + /* Find the target */ + if (found->target) { +- WpNode *node = g_hash_table_lookup (self->targets, found->target); +- if (node) +- return wp_proxy_get_bound_id (WP_PROXY (node)); ++ Target *t = g_hash_table_lookup (self->targets, found->target); ++ if (t) { ++ return wp_spa_json_new_object ( ++ "exclusive", "b", t->exclusive, ++ "bound_id", "i", ++ t->node ? (gint)wp_proxy_get_bound_id (WP_PROXY (t->node)) : -1, ++ NULL); ++ } + } + +- return -1; ++ return g_steal_pointer (&res); + } + + static gint +@@ -198,12 +231,17 @@ wp_filters_api_get_filter_from_target (WpFiltersApi * self, + /* Find the first target matching target_id */ + while (filters) { + Filter *f = (Filter *) filters->data; +- gint f_target_id = wp_filters_api_get_filter_target (self, direction, +- f->link_group); +- if (f_target_id == target_id && f->enabled) { +- target = f->target; +- found = TRUE; +- break; ++ if (f->enabled) { ++ gint f_target_id; ++ g_autoptr (WpSpaJson) f_target = wp_filters_api_get_filter_target (self, ++ direction, f->link_group); ++ if (f_target && wp_spa_json_is_object (f_target) && ++ wp_spa_json_object_get (f_target, "bound_id", "i", &f_target_id, NULL) ++ && f_target_id == target_id) { ++ target = f->target; ++ found = TRUE; ++ break; ++ } + } + + /* Advance */ +@@ -323,9 +361,11 @@ reevaluate_targets (WpFiltersApi *self) + for (; wp_iterator_next (it, &item); g_value_unset (&item)) { + WpSpaJson *j = g_value_get_boxed (&item); + g_autofree gchar *key = NULL; +- WpSpaJson *props; +- g_autoptr (WpNode) target = NULL; +- WpNode *curr_target; ++ WpSpaJson *value; ++ gboolean exclusive = FALSE; ++ g_autoptr (WpSpaJson) props = NULL; ++ g_autoptr (WpNode) target_node = NULL; ++ Target *curr_target; + + key = wp_spa_json_parse_string (j); + g_value_unset (&item); +@@ -334,27 +374,36 @@ reevaluate_targets (WpFiltersApi *self) + "Could not get valid key-value pairs from target object"); + break; + } +- props = g_value_get_boxed (&item); ++ value = g_value_get_boxed (&item); ++ if (!value || !wp_spa_json_is_object (value)) { ++ wp_warning_object (self, "Target value must be a JSON object"); ++ break; ++ } + +- /* Get current target */ +- curr_target = g_hash_table_lookup (self->targets, key); ++ /* Get exclusive */ ++ wp_spa_json_object_get (value, "exclusive", "b", &exclusive, NULL); + +- /* Find the node and insert it into the table if found */ +- target = find_target_node (self, props); +- if (target) { +- /* Check if the target changed */ +- if (curr_target) { +- guint32 target_bound_id = wp_proxy_get_bound_id (WP_PROXY (target)); +- guint32 curr_bound_id = wp_proxy_get_bound_id (WP_PROXY (curr_target)); +- if (target_bound_id != curr_bound_id) +- changed = TRUE; +- } ++ /* Get target node */ ++ wp_spa_json_object_get (value, "props", "J", &props, NULL); ++ if (props) ++ target_node = find_target_node (self, props); + +- g_hash_table_insert (self->targets, g_strdup (key), +- g_steal_pointer (&target)); +- } else { +- if (curr_target) ++ /* Update values if target exists in the table, otherwise add new target */ ++ curr_target = g_hash_table_lookup (self->targets, key); ++ if (curr_target) { ++ if (curr_target->exclusive != exclusive) { ++ curr_target->exclusive = exclusive; ++ changed = TRUE; ++ } ++ if (curr_target->node != target_node) { ++ g_clear_object (&curr_target->node); ++ curr_target->node = g_steal_pointer (&target_node); + changed = TRUE; ++ } ++ } else { ++ g_hash_table_insert (self->targets, g_strdup (key), ++ target_new (exclusive, target_node)); ++ changed = TRUE; + } + } + +@@ -790,7 +839,7 @@ wp_filters_api_enable (WpPlugin * plugin, WpTransition * transition) + g_autoptr (WpCore) core = wp_object_get_core (WP_OBJECT (self)); + + self->targets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, +- g_object_unref); ++ (GDestroyNotify) target_free); + + /* Create the metadata object manager */ + self->metadata_om = wp_object_manager_new (); +@@ -845,7 +894,7 @@ wp_filters_api_class_init (WpFiltersApiClass * klass) + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + (GCallback) wp_filters_api_get_filter_target, + NULL, NULL, NULL, +- G_TYPE_INT, 2, G_TYPE_STRING, G_TYPE_STRING); ++ WP_TYPE_SPA_JSON, 2, G_TYPE_STRING, G_TYPE_STRING); + + signals[ACTION_GET_FILTER_FROM_TARGET] = g_signal_new_class_handler ( + "get-filter-from-target", G_TYPE_FROM_CLASS (klass), +diff --git a/src/config/policy.lua.d/30-filters-config.lua b/src/config/policy.lua.d/30-filters-config.lua +index 8d919fb7..bf4b8d75 100644 +--- a/src/config/policy.lua.d/30-filters-config.lua ++++ b/src/config/policy.lua.d/30-filters-config.lua +@@ -59,16 +59,22 @@ default_policy.filters_metadata = { + } + }, + +- -- The target node properties (any node properties can be defined) ++ -- The filter targets + ["targets"] = { + ["speakers"] = { +- ["media.class"] = "Audio/Sink", +- ["alsa.card_name"] = "acp5x", +- ["device.profile.description"] = "Speaker", ++ ["exclusive"] = false, ++ ["props"] = { ++ ["media.class"] = "Audio/Sink", ++ ["alsa.card_name"] = "acp5x", ++ ["device.profile.description"] = "Speaker", ++ } + }, + ["microphone"] = { +- ["media.class"] = "Audio/Source", +- ["alsa.card_name"] = "acp5x", ++ ["exclusive"] = false, ++ ["props"] = { ++ ["media.class"] = "Audio/Source", ++ ["alsa.card_name"] = "acp5x", ++ } + } + } + } +diff --git a/src/scripts/filters-metadata.lua b/src/scripts/filters-metadata.lua +index 04e3e6c6..49b4d0e8 100644 +--- a/src/scripts/filters-metadata.lua ++++ b/src/scripts/filters-metadata.lua +@@ -30,8 +30,11 @@ f_metadata:activate(Features.ALL, function (m, e) + + -- Set targets metadata + local targets = {} +- for name, props in pairs(config["targets"]) do +- targets[name] = Json.Object (props) ++ for name, value in pairs(config["targets"]) do ++ targets[name] = Json.Object { ++ exclusive = value.exclusive and true or false, ++ props = Json.Object (value.props) ++ } + end + local targets_json = Json.Object (targets) + m:set (0, "filters.configured.targets", "Spa:String:JSON", +diff --git a/src/scripts/policy-node.lua b/src/scripts/policy-node.lua +index c5a15ec6..035c3006 100644 +--- a/src/scripts/policy-node.lua ++++ b/src/scripts/policy-node.lua +@@ -719,31 +719,31 @@ function checkFollowDefault (si, si_target, has_node_defined_target) + end + + function findFilterTarget (si) +- local target_id = -1 +- + -- always return nil if filters API is not loaded + if self.filters_api == nil then +- return nil ++ return nil, false + end + + -- always return nil if this is not a filter + local node = si:get_associated_proxy ("node") + local link_group = node.properties["node.link-group"] + if link_group == nil then +- return nil ++ return nil, false + end + + -- get the filter target + local direction = getTargetDirection (si.properties) +- target_id = self.filters_api:call("get-filter-target", direction, link_group) +- if (target_id == -1) then +- return nil ++ local target_json = self.filters_api:call("get-filter-target", direction, link_group) ++ if target_json == nil then ++ return nil, false + end ++ target = target_json:parse() + +- Log.info (".. filter target ID is " .. tostring(target_id)) ++ Log.info (".. filter target ID is " .. tostring(target.bound_id) .. ++ " (" .. tostring (target.exclusive) .. ")") + return linkables_om:lookup { +- Constraint { "node.id", "=", tostring(target_id) } +- } ++ Constraint { "node.id", "=", tostring(target.bound_id) } ++ }, target.exclusive + end + + function handleLinkable (si) +@@ -783,7 +783,11 @@ function handleLinkable (si) + + -- find filter target (always returns nil for non filters) + if si_target == nil then +- si_target = findFilterTarget(si) ++ si_target, exclusive = findFilterTarget(si) ++ -- don't fallback if filter target is not found and exclusive is true ++ if si_target == nil and exclusive then ++ return ++ end + local can_passthrough = si_target and canPassthrough(si, si_target) + if si_target and si_must_passthrough and not can_passthrough then + si_target = nil +-- +2.42.0 + + +From 0010f03fafa2841f7618dad4b609466b6dad11c7 Mon Sep 17 00:00:00 2001 +From: Julian Bouzas +Date: Mon, 6 Nov 2023 14:33:34 -0500 +Subject: [PATCH 08/11] policy-bluetooth: remove application names array and + use BT loopback filter + +Uses a BT loopback filter to know when an application wants to capture audio +from the current BT device. If the BT loopback filter is used, wireplumber will +automatically switch the device to HSP/HFP profile, otherwise the BT device +profile is always set to A2DP. +--- + src/config/policy.lua.d/10-default-policy.lua | 10 -- + src/config/policy.lua.d/30-filters-config.lua | 17 ++- + src/config/wireplumber.conf | 20 +++ + src/scripts/policy-bluetooth.lua | 139 ++++++++++-------- + 4 files changed, 110 insertions(+), 76 deletions(-) + +diff --git a/src/config/policy.lua.d/10-default-policy.lua b/src/config/policy.lua.d/10-default-policy.lua +index 412d47a8..7d4ea77c 100644 +--- a/src/config/policy.lua.d/10-default-policy.lua ++++ b/src/config/policy.lua.d/10-default-policy.lua +@@ -33,16 +33,6 @@ bluetooth_policy.policy = { + + -- Whether to use headset profile in the presence of an input stream. + ["media-role.use-headset-profile"] = true, +- +- -- Application names correspond to application.name in stream properties. +- -- Applications which do not set media.role but which should be considered +- -- for role based profile switching can be specified here. +- ["media-role.applications"] = { +- "Firefox", "Chromium input", "Google Chrome input", "Brave input", +- "Microsoft Edge input", "Vivaldi input", "ZOOM VoiceEngine", +- "Telegram Desktop", "telegram-desktop", "linphone", "Mumble", +- "WEBRTC VoiceEngine", "Skype", "Firefox Developer Edition", +- }, + } + + dsp_policy = {} +diff --git a/src/config/policy.lua.d/30-filters-config.lua b/src/config/policy.lua.d/30-filters-config.lua +index bf4b8d75..88429cdc 100644 +--- a/src/config/policy.lua.d/30-filters-config.lua ++++ b/src/config/policy.lua.d/30-filters-config.lua +@@ -33,11 +33,19 @@ default_policy.filters_metadata = { + }, + + -- Output filters (meant to be linked with Audio/Source device nodes) ++ { ++ ["stream-name"] = "virtual-bluetooth-source-in", -- loopback bluetooth capture ++ ["node-name"] = "virtual-bluetooth-source-out", -- loopback bluetooth source ++ ["direction"] = "output", -- can only be 'input' or 'output' ++ ["target"] = "bluetooth-source", -- if nil, the default node will be used as target ++ ["mode"] = "always", -- can be 'always', 'never', 'playback-only' or 'capture-only' ++ ["priority"] = 30, ++ }, + { + ["stream-name"] = "input.virtual-source", -- loopback capture + ["node-name"] = "output.virtual-source", -- loopback source + ["direction"] = "output", -- can only be 'input' or 'output' +- ["target"] = nil, -- if nil, the default node will be used as target ++ ["target"] = "microphone", -- if nil, the default node will be used as target + ["mode"] = "always", -- can be 'always', 'never', 'playback-only' or 'capture-only' + ["priority"] = 30, + }, +@@ -75,6 +83,13 @@ default_policy.filters_metadata = { + ["media.class"] = "Audio/Source", + ["alsa.card_name"] = "acp5x", + } ++ }, ++ ["bluetooth-source"] = { ++ ["exclusive"] = true, ++ ["props"] = { ++ ["media.class"] = "Audio/Source", ++ ["device.api"] = "bluez5" ++ } + } + } + } +diff --git a/src/config/wireplumber.conf b/src/config/wireplumber.conf +index 85d7be12..4c9dd568 100644 +--- a/src/config/wireplumber.conf ++++ b/src/config/wireplumber.conf +@@ -77,6 +77,26 @@ context.modules = [ + + # Provides factories to make SPA node objects. + { name = libpipewire-module-spa-node-factory } ++ ++ # Virtual Bluetooth Source ++ { ++ name = libpipewire-module-loopback ++ args = { ++ capture.props = { ++ node.name = virtual-bluetooth-source-in ++ node.description = "Virtual Bluetooth Source In" ++ audio.position = [ MONO ] ++ stream.dont-remix = true ++ node.passive = true ++ } ++ playback.props = { ++ node.name = virtual-bluetooth-source-out ++ node.description = "Virtual Bluetooth Source Out" ++ audio.position = [ MONO ] ++ media.class = Audio/Source ++ } ++ } ++ } + ] + + wireplumber.components = [ +diff --git a/src/scripts/policy-bluetooth.lua b/src/scripts/policy-bluetooth.lua +index f8f69a14..7aecb8b0 100644 +--- a/src/scripts/policy-bluetooth.lua ++++ b/src/scripts/policy-bluetooth.lua +@@ -26,7 +26,6 @@ + + local config = ... + local use_persistent_storage = config["use-persistent-storage"] or false +-local applications = {} + local use_headset_profile = config["media-role.use-headset-profile"] or false + local profile_restore_timeout_msec = 2000 + +@@ -41,17 +40,6 @@ local last_profiles = {} + local active_streams = {} + local previous_streams = {} + +-for _, value in ipairs(config["media-role.applications"] or {}) do +- applications[value] = true +-end +- +-metadata_om = ObjectManager { +- Interest { +- type = "metadata", +- Constraint { "metadata.name", "=", "default" }, +- } +-} +- + devices_om = ObjectManager { + Interest { + type = "device", +@@ -68,6 +56,16 @@ streams_om = ObjectManager { + } + } + ++nodes_om = ObjectManager { ++ Interest { ++ type = "node", ++ Constraint { "node.name", "=", "virtual-bluetooth-source-out", type = "pw-global" }, ++ Constraint { "media.class", "matches", "Audio/Source", type = "pw-global" }, ++ } ++} ++ ++links_om = ObjectManager { Interest { type = "link" } } ++ + local function parseParam(param_to_parse, id) + local param = param_to_parse:parse() + if param.pod_type == "Object" and param.object_id == id then +@@ -117,19 +115,6 @@ local function isSwitched(device) + return getSavedLastProfile(device) ~= nil + end + +-local function isBluez5AudioSink(sink_name) +- if sink_name and string.find(sink_name, "bluez_output.") ~= nil then +- return true +- end +- return false +-end +- +-local function isBluez5DefaultAudioSink() +- local metadata = metadata_om:lookup() +- local default_audio_sink = metadata:find(0, "default.audio.sink") +- return isBluez5AudioSink(default_audio_sink) +-end +- + local function findProfile(device, index, name) + for p in device:iterate_params("EnumProfile") do + local profile = parseParam(p, "EnumProfile") +@@ -228,7 +213,6 @@ local function switchProfile() + end + + local cur_profile_name = getCurrentProfile(device) +- saveLastProfile(device, cur_profile_name) + + _, index, name = findProfile(device, nil, cur_profile_name) + if hasProfileInputRoute(device, index) then +@@ -251,6 +235,8 @@ local function switchProfile() + index = index + } + ++ saveLastProfile(device, cur_profile_name) ++ + Log.info("Setting profile of '" + .. device.properties["device.description"] + .. "' from: " .. cur_profile_name +@@ -270,8 +256,6 @@ local function restoreProfile() + local profile_name = getSavedLastProfile(device) + local cur_profile_name = getCurrentProfile(device) + +- saveLastProfile(device, nil) +- + if cur_profile_name then + Log.info("Setting saved headset profile to: " .. cur_profile_name) + saveHeadsetProfile(device, cur_profile_name) +@@ -286,6 +270,8 @@ local function restoreProfile() + index = index + } + ++ saveLastProfile(device, nil) ++ + Log.info("Restoring profile of '" + .. device.properties["device.description"] + .. "' from: " .. cur_profile_name +@@ -312,18 +298,14 @@ local function triggerRestoreProfile() + end) + end + +--- We consider a Stream of interest to have role Communication if it has +--- media.role set to Communication in props or it is in our list of +--- applications as these applications do not set media.role correctly or at +--- all. +-local function checkStreamStatus(stream) +- local app_name = stream.properties["application.name"] +- local stream_role = stream.properties["media.role"] ++function parseBool(var) ++ return var and (var:lower() == "true" or var == "1") ++end + +- if not (stream_role == "Communication" or applications[app_name]) then +- return false +- end +- if not isBluez5DefaultAudioSink() then ++local function checkStreamStatus (stream) ++ -- Ignore monitor streams ++ local is_monitor = parseBool (stream.properties["stream.monitor"]) ++ if is_monitor then + return false + end + +@@ -334,7 +316,25 @@ local function checkStreamStatus(stream) + return false + end + +- return true ++ -- Make sure the virtual BT filter node exists ++ local node = nodes_om:lookup () ++ if node == nil then ++ return false ++ end ++ ++ -- Check if the stream is linked to the bluetooth loopback filter ++ local stream_id = tonumber(stream["bound-id"]) ++ local bt_out_id = tonumber(node["bound-id"]) ++ for l in links_om:iterate() do ++ local p = l.properties ++ local out_id = tonumber(p["link.output.node"]) ++ local in_id = tonumber(p["link.input.node"]) ++ if in_id == stream_id and out_id == bt_out_id then ++ return true ++ end ++ end ++ ++ return false + end + + local function handleStream(stream) +@@ -361,38 +361,47 @@ local function handleAllStreams() + end + end + +-streams_om:connect("object-added", function (_, stream) +- stream:connect("state-changed", function (stream, old_state, cur_state) +- handleStream(stream) +- end) +- stream:connect("params-changed", handleStream) +- handleStream(stream) +-end) +- +-streams_om:connect("object-removed", function (_, stream) +- active_streams[stream["bound-id"]] = nil +- previous_streams[stream["bound-id"]] = nil +- triggerRestoreProfile() +-end) +- + devices_om:connect("object-added", function (_, device) + -- Devices are unswitched initially +- if isSwitched(device) then +- saveLastProfile(device, nil) +- end ++ saveLastProfile(device, nil) + handleAllStreams() + end) + +-metadata_om:connect("object-added", function (_, metadata) +- metadata:connect("changed", function (m, subject, key, t, value) +- if (use_headset_profile and subject == 0 and key == "default.audio.sink" +- and isBluez5AudioSink(value)) then +- -- If bluez sink is set as default, rescan for active input streams +- handleAllStreams() ++links_om:connect("object-added", function (_, link) ++ if handleAllStreams then ++ local p = link.properties ++ for stream in streams_om:iterate { ++ Constraint { "media.class", "matches", "Stream/Input/Audio", type = "pw-global" }, ++ Constraint { "stream.monitor", "!", "true" } ++ } do ++ local in_id = tonumber(p["link.input.node"]) ++ local stream_id = tonumber(stream["bound-id"]) ++ if in_id == stream_id then ++ handleStream(stream) ++ end + end +- end) ++ end ++end) ++ ++links_om:connect("object-removed", function (_, link) ++ if handleAllStreams then ++ local p = link.properties ++ for stream in streams_om:iterate { ++ Constraint { "media.class", "matches", "Stream/Input/Audio", type = "pw-global" }, ++ Constraint { "stream.monitor", "!", "true" } ++ } do ++ local in_id = tonumber(p["link.input.node"]) ++ local stream_id = tonumber(stream["bound-id"]) ++ if in_id == stream_id then ++ active_streams[stream["bound-id"]] = nil ++ previous_streams[stream["bound-id"]] = nil ++ triggerRestoreProfile() ++ end ++ end ++ end + end) + +-metadata_om:activate() + devices_om:activate() + streams_om:activate() ++nodes_om:activate() ++links_om:activate() +-- +2.42.0 + + +From ae7cde6dd7c7660da5bbd07f27fe18a624c8c67d Mon Sep 17 00:00:00 2001 +From: Ethan Geller +Date: Mon, 13 Nov 2023 22:10:05 -0800 +Subject: [PATCH 09/11] fix speaker tunings for galileo + +--- + src/config/policy.lua.d/30-filters-config.lua | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/config/policy.lua.d/30-filters-config.lua b/src/config/policy.lua.d/30-filters-config.lua +index 88429cdc..a4b617d8 100644 +--- a/src/config/policy.lua.d/30-filters-config.lua ++++ b/src/config/policy.lua.d/30-filters-config.lua +@@ -73,7 +73,7 @@ default_policy.filters_metadata = { + ["exclusive"] = false, + ["props"] = { + ["media.class"] = "Audio/Sink", +- ["alsa.card_name"] = "acp5x", ++ ["alsa.card_name"] = "sof-nau8821-max", + ["device.profile.description"] = "Speaker", + } + }, +-- +2.42.0 + + +From 0ed6dc783bcb4033fb82c0f9b3e2c26977c3e19e Mon Sep 17 00:00:00 2001 +From: Ethan Geller +Date: Wed, 15 Nov 2023 14:32:56 -0800 +Subject: [PATCH 10/11] Revert "policy-bluetooth: remove application names + array and use BT loopback filter" + +This reverts commit 5a760629b6e81268383f32406119fbb4ac3a42b0. +--- + src/config/policy.lua.d/10-default-policy.lua | 10 ++ + src/config/policy.lua.d/30-filters-config.lua | 17 +-- + src/config/wireplumber.conf | 20 --- + src/scripts/policy-bluetooth.lua | 139 ++++++++---------- + 4 files changed, 76 insertions(+), 110 deletions(-) + +diff --git a/src/config/policy.lua.d/10-default-policy.lua b/src/config/policy.lua.d/10-default-policy.lua +index 7d4ea77c..412d47a8 100644 +--- a/src/config/policy.lua.d/10-default-policy.lua ++++ b/src/config/policy.lua.d/10-default-policy.lua +@@ -33,6 +33,16 @@ bluetooth_policy.policy = { + + -- Whether to use headset profile in the presence of an input stream. + ["media-role.use-headset-profile"] = true, ++ ++ -- Application names correspond to application.name in stream properties. ++ -- Applications which do not set media.role but which should be considered ++ -- for role based profile switching can be specified here. ++ ["media-role.applications"] = { ++ "Firefox", "Chromium input", "Google Chrome input", "Brave input", ++ "Microsoft Edge input", "Vivaldi input", "ZOOM VoiceEngine", ++ "Telegram Desktop", "telegram-desktop", "linphone", "Mumble", ++ "WEBRTC VoiceEngine", "Skype", "Firefox Developer Edition", ++ }, + } + + dsp_policy = {} +diff --git a/src/config/policy.lua.d/30-filters-config.lua b/src/config/policy.lua.d/30-filters-config.lua +index a4b617d8..37badd80 100644 +--- a/src/config/policy.lua.d/30-filters-config.lua ++++ b/src/config/policy.lua.d/30-filters-config.lua +@@ -33,19 +33,11 @@ default_policy.filters_metadata = { + }, + + -- Output filters (meant to be linked with Audio/Source device nodes) +- { +- ["stream-name"] = "virtual-bluetooth-source-in", -- loopback bluetooth capture +- ["node-name"] = "virtual-bluetooth-source-out", -- loopback bluetooth source +- ["direction"] = "output", -- can only be 'input' or 'output' +- ["target"] = "bluetooth-source", -- if nil, the default node will be used as target +- ["mode"] = "always", -- can be 'always', 'never', 'playback-only' or 'capture-only' +- ["priority"] = 30, +- }, + { + ["stream-name"] = "input.virtual-source", -- loopback capture + ["node-name"] = "output.virtual-source", -- loopback source + ["direction"] = "output", -- can only be 'input' or 'output' +- ["target"] = "microphone", -- if nil, the default node will be used as target ++ ["target"] = nil, -- if nil, the default node will be used as target + ["mode"] = "always", -- can be 'always', 'never', 'playback-only' or 'capture-only' + ["priority"] = 30, + }, +@@ -83,13 +75,6 @@ default_policy.filters_metadata = { + ["media.class"] = "Audio/Source", + ["alsa.card_name"] = "acp5x", + } +- }, +- ["bluetooth-source"] = { +- ["exclusive"] = true, +- ["props"] = { +- ["media.class"] = "Audio/Source", +- ["device.api"] = "bluez5" +- } + } + } + } +diff --git a/src/config/wireplumber.conf b/src/config/wireplumber.conf +index 4c9dd568..85d7be12 100644 +--- a/src/config/wireplumber.conf ++++ b/src/config/wireplumber.conf +@@ -77,26 +77,6 @@ context.modules = [ + + # Provides factories to make SPA node objects. + { name = libpipewire-module-spa-node-factory } +- +- # Virtual Bluetooth Source +- { +- name = libpipewire-module-loopback +- args = { +- capture.props = { +- node.name = virtual-bluetooth-source-in +- node.description = "Virtual Bluetooth Source In" +- audio.position = [ MONO ] +- stream.dont-remix = true +- node.passive = true +- } +- playback.props = { +- node.name = virtual-bluetooth-source-out +- node.description = "Virtual Bluetooth Source Out" +- audio.position = [ MONO ] +- media.class = Audio/Source +- } +- } +- } + ] + + wireplumber.components = [ +diff --git a/src/scripts/policy-bluetooth.lua b/src/scripts/policy-bluetooth.lua +index 7aecb8b0..f8f69a14 100644 +--- a/src/scripts/policy-bluetooth.lua ++++ b/src/scripts/policy-bluetooth.lua +@@ -26,6 +26,7 @@ + + local config = ... + local use_persistent_storage = config["use-persistent-storage"] or false ++local applications = {} + local use_headset_profile = config["media-role.use-headset-profile"] or false + local profile_restore_timeout_msec = 2000 + +@@ -40,6 +41,17 @@ local last_profiles = {} + local active_streams = {} + local previous_streams = {} + ++for _, value in ipairs(config["media-role.applications"] or {}) do ++ applications[value] = true ++end ++ ++metadata_om = ObjectManager { ++ Interest { ++ type = "metadata", ++ Constraint { "metadata.name", "=", "default" }, ++ } ++} ++ + devices_om = ObjectManager { + Interest { + type = "device", +@@ -56,16 +68,6 @@ streams_om = ObjectManager { + } + } + +-nodes_om = ObjectManager { +- Interest { +- type = "node", +- Constraint { "node.name", "=", "virtual-bluetooth-source-out", type = "pw-global" }, +- Constraint { "media.class", "matches", "Audio/Source", type = "pw-global" }, +- } +-} +- +-links_om = ObjectManager { Interest { type = "link" } } +- + local function parseParam(param_to_parse, id) + local param = param_to_parse:parse() + if param.pod_type == "Object" and param.object_id == id then +@@ -115,6 +117,19 @@ local function isSwitched(device) + return getSavedLastProfile(device) ~= nil + end + ++local function isBluez5AudioSink(sink_name) ++ if sink_name and string.find(sink_name, "bluez_output.") ~= nil then ++ return true ++ end ++ return false ++end ++ ++local function isBluez5DefaultAudioSink() ++ local metadata = metadata_om:lookup() ++ local default_audio_sink = metadata:find(0, "default.audio.sink") ++ return isBluez5AudioSink(default_audio_sink) ++end ++ + local function findProfile(device, index, name) + for p in device:iterate_params("EnumProfile") do + local profile = parseParam(p, "EnumProfile") +@@ -213,6 +228,7 @@ local function switchProfile() + end + + local cur_profile_name = getCurrentProfile(device) ++ saveLastProfile(device, cur_profile_name) + + _, index, name = findProfile(device, nil, cur_profile_name) + if hasProfileInputRoute(device, index) then +@@ -235,8 +251,6 @@ local function switchProfile() + index = index + } + +- saveLastProfile(device, cur_profile_name) +- + Log.info("Setting profile of '" + .. device.properties["device.description"] + .. "' from: " .. cur_profile_name +@@ -256,6 +270,8 @@ local function restoreProfile() + local profile_name = getSavedLastProfile(device) + local cur_profile_name = getCurrentProfile(device) + ++ saveLastProfile(device, nil) ++ + if cur_profile_name then + Log.info("Setting saved headset profile to: " .. cur_profile_name) + saveHeadsetProfile(device, cur_profile_name) +@@ -270,8 +286,6 @@ local function restoreProfile() + index = index + } + +- saveLastProfile(device, nil) +- + Log.info("Restoring profile of '" + .. device.properties["device.description"] + .. "' from: " .. cur_profile_name +@@ -298,14 +312,18 @@ local function triggerRestoreProfile() + end) + end + +-function parseBool(var) +- return var and (var:lower() == "true" or var == "1") +-end ++-- We consider a Stream of interest to have role Communication if it has ++-- media.role set to Communication in props or it is in our list of ++-- applications as these applications do not set media.role correctly or at ++-- all. ++local function checkStreamStatus(stream) ++ local app_name = stream.properties["application.name"] ++ local stream_role = stream.properties["media.role"] + +-local function checkStreamStatus (stream) +- -- Ignore monitor streams +- local is_monitor = parseBool (stream.properties["stream.monitor"]) +- if is_monitor then ++ if not (stream_role == "Communication" or applications[app_name]) then ++ return false ++ end ++ if not isBluez5DefaultAudioSink() then + return false + end + +@@ -316,25 +334,7 @@ local function checkStreamStatus (stream) + return false + end + +- -- Make sure the virtual BT filter node exists +- local node = nodes_om:lookup () +- if node == nil then +- return false +- end +- +- -- Check if the stream is linked to the bluetooth loopback filter +- local stream_id = tonumber(stream["bound-id"]) +- local bt_out_id = tonumber(node["bound-id"]) +- for l in links_om:iterate() do +- local p = l.properties +- local out_id = tonumber(p["link.output.node"]) +- local in_id = tonumber(p["link.input.node"]) +- if in_id == stream_id and out_id == bt_out_id then +- return true +- end +- end +- +- return false ++ return true + end + + local function handleStream(stream) +@@ -361,47 +361,38 @@ local function handleAllStreams() + end + end + +-devices_om:connect("object-added", function (_, device) +- -- Devices are unswitched initially +- saveLastProfile(device, nil) +- handleAllStreams() ++streams_om:connect("object-added", function (_, stream) ++ stream:connect("state-changed", function (stream, old_state, cur_state) ++ handleStream(stream) ++ end) ++ stream:connect("params-changed", handleStream) ++ handleStream(stream) + end) + +-links_om:connect("object-added", function (_, link) +- if handleAllStreams then +- local p = link.properties +- for stream in streams_om:iterate { +- Constraint { "media.class", "matches", "Stream/Input/Audio", type = "pw-global" }, +- Constraint { "stream.monitor", "!", "true" } +- } do +- local in_id = tonumber(p["link.input.node"]) +- local stream_id = tonumber(stream["bound-id"]) +- if in_id == stream_id then +- handleStream(stream) +- end +- end ++streams_om:connect("object-removed", function (_, stream) ++ active_streams[stream["bound-id"]] = nil ++ previous_streams[stream["bound-id"]] = nil ++ triggerRestoreProfile() ++end) ++ ++devices_om:connect("object-added", function (_, device) ++ -- Devices are unswitched initially ++ if isSwitched(device) then ++ saveLastProfile(device, nil) + end ++ handleAllStreams() + end) + +-links_om:connect("object-removed", function (_, link) +- if handleAllStreams then +- local p = link.properties +- for stream in streams_om:iterate { +- Constraint { "media.class", "matches", "Stream/Input/Audio", type = "pw-global" }, +- Constraint { "stream.monitor", "!", "true" } +- } do +- local in_id = tonumber(p["link.input.node"]) +- local stream_id = tonumber(stream["bound-id"]) +- if in_id == stream_id then +- active_streams[stream["bound-id"]] = nil +- previous_streams[stream["bound-id"]] = nil +- triggerRestoreProfile() +- end ++metadata_om:connect("object-added", function (_, metadata) ++ metadata:connect("changed", function (m, subject, key, t, value) ++ if (use_headset_profile and subject == 0 and key == "default.audio.sink" ++ and isBluez5AudioSink(value)) then ++ -- If bluez sink is set as default, rescan for active input streams ++ handleAllStreams() + end +- end ++ end) + end) + ++metadata_om:activate() + devices_om:activate() + streams_om:activate() +-nodes_om:activate() +-links_om:activate() +-- +2.42.0 + + +From 043390080937c05df74a48eaff5a9713ff0ea12f Mon Sep 17 00:00:00 2001 +From: Ethan Geller +Date: Wed, 15 Nov 2023 14:35:00 -0800 +Subject: [PATCH 11/11] fix filter chain targeting for mic. + +--- + src/config/policy.lua.d/30-filters-config.lua | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/config/policy.lua.d/30-filters-config.lua b/src/config/policy.lua.d/30-filters-config.lua +index 37badd80..8e8725fc 100644 +--- a/src/config/policy.lua.d/30-filters-config.lua ++++ b/src/config/policy.lua.d/30-filters-config.lua +@@ -73,7 +73,7 @@ default_policy.filters_metadata = { + ["exclusive"] = false, + ["props"] = { + ["media.class"] = "Audio/Source", +- ["alsa.card_name"] = "acp5x", ++ ["alsa.card_name"] = "sof-nau8821-max", + } + } + } +-- +2.42.0 + diff --git a/spec_files/wireplumber/wireplumber.spec b/spec_files/wireplumber/wireplumber.spec index c7b30f446b..e426426d87 100644 --- a/spec_files/wireplumber/wireplumber.spec +++ b/spec_files/wireplumber/wireplumber.spec @@ -1,5 +1,5 @@ Name: wireplumber -Version: 0.4.16 +Version: 0.4.17 Release: 1%{?dist}.bazzite.{{{ git_dir_version }}} Summary: A modular session/policy manager for PipeWire @@ -8,7 +8,7 @@ URL: https://pipewire.pages.freedesktop.org/wireplumber/ Source0: https://gitlab.freedesktop.org/pipewire/%{name}/-/archive/%{version}/%{name}-%{version}.tar.bz2 ## upstream patches -Patch0: steamdeck.patch +Patch0: valve.patch ## upstreamable patches @@ -70,8 +70,7 @@ managing PipeWire. -Dsystemd=enabled \ -Dsystemd-user-service=true \ -Dintrospection=enabled \ - -Delogind=disabled \ - --sysconfdir=%{_rundir} + -Delogind=disabled %meson_build %install @@ -80,6 +79,9 @@ managing PipeWire. # Create local config skeleton mkdir -p %{buildroot}%{_sysconfdir}/wireplumber/{bluetooth.lua.d,common,main.lua.d,policy.lua.d} +# Create missing empty system config dirs for other packages to drop files in +mkdir -p %{buildroot}%{_datadir}/wireplumber/wireplumber.conf.d + %find_lang %{name} %posttrans @@ -105,6 +107,7 @@ fi %dir %{_sysconfdir}/wireplumber/main.lua.d %dir %{_sysconfdir}/wireplumber/policy.lua.d %{_datadir}/wireplumber/ +%dir %{_datadir}/wireplumber/wireplumber.conf.d %{_datadir}/zsh/site-functions/_wpctl %{_userunitdir}/wireplumber.service %{_userunitdir}/wireplumber@.service @@ -123,6 +126,12 @@ fi %{_datadir}/gir-1.0/Wp-0.4.gir %changelog +* Mon Dec 4 2023 Wim Taymans - 0.4.17-1 +- wireplumber 0.4.17 + +* Mon Dec 4 2023 Hector Martin - 0.4.16-2 +- Create and own /usr/share/wireplumber/wireplumber.conf.d + * Thu Nov 23 2023 Wim Taymans - 0.4.16-1 - wireplumber 0.4.16 diff --git a/system_files/deck/shared/usr/libexec/bazzite-handle-legion-go-rotation b/system_files/deck/shared/usr/libexec/bazzite-rotation-fix similarity index 81% rename from system_files/deck/shared/usr/libexec/bazzite-handle-legion-go-rotation rename to system_files/deck/shared/usr/libexec/bazzite-rotation-fix index 58cebb2276..c620e0d3a5 100755 --- a/system_files/deck/shared/usr/libexec/bazzite-handle-legion-go-rotation +++ b/system_files/deck/shared/usr/libexec/bazzite-rotation-fix @@ -3,6 +3,8 @@ # This script fixes the screen orientation in the Desktop Mode in Bazzite-Deck KDE # Author: d3Xt3r +SYS_ID="$(cat /sys/devices/virtual/dmi/id/product_name)" + sleep 1 echo $(date '+%Y-%m-%d %H:%M:%S') Starting Bazzite Desktop Orientation Fix script...| tee -a /tmp/bazrotfix.log @@ -28,7 +30,11 @@ kscreen-doctor --outputs 2>&1 | tee -a /tmp/bazrotfix.log # Fix desktop orientation # Rotation options: right, normal, left, inverted echo $(date '+%Y-%m-%d %H:%M:%S') Fixing desktop orientation... | tee -a /tmp/bazrotfix.log -kscreen-doctor output.1.rotation.left 2>&1 | tee -a /tmp/bazrotfix.log +if [[ ":83E1:" =~ ":$SYS_ID" ]]; then + kscreen-doctor output.1.rotation.left 2>&1 | tee -a /tmp/bazrotfix.log +else + kscreen-doctor output.1.rotation.normal 2>&1 | tee -a /tmp/bazrotfix.log +fi echo $(date '+%Y-%m-%d %H:%M:%S') Ending Bazzite Desktop Orientation Fix script >> /tmp/bazrotfix.log echo -e '\n' >> /tmp/bazrotfix.log diff --git a/system_files/deck/shared/usr/share/ublue-os/just/80-bazzite.just b/system_files/deck/shared/usr/share/ublue-os/just/80-bazzite.just index e1207c2821..1ac55189c1 100644 --- a/system_files/deck/shared/usr/share/ublue-os/just/80-bazzite.just +++ b/system_files/deck/shared/usr/share/ublue-os/just/80-bazzite.just @@ -60,15 +60,22 @@ configure-waydroid: # Install Sunshine install-sunshine: #!/usr/bin/bash - flatpak install --system --noninteractive flathub dev.lizardbyte.app.Sunshine - flatpak run --command=additional-install.sh app/dev.lizardbyte.app.Sunshine/x86_64/stable || true - echo "Sunshine is installed!" + sudo sed -i '0,/enabled=0/s//enabled=1/' /etc/yum.repos.d/rpmfusion-nonfree.repo + sudo sed -i '0,/enabled=0/s//enabled=1/' /etc/yum.repos.d/rpmfusion-nonfree-updates.repo + sudo systemctl enable sunshine-workaround.service + ublue-update --wait + rpm-ostree install -y sunshine + echo "Sunshine is installed, please reboot to apply changes." # Remove Sunshine remove-sunshine: #!/usr/bin/bash - flatpak run --command=remove-additional-install.sh app/dev.lizardbyte.app.Sunshine/x86_64/stable || true - flatpak uninstall --delete-data --noninteractive dev.lizardbyte.sunshine + sudo sed -i '0,/enabled=1/s//enabled=0/' /etc/yum.repos.d/rpmfusion-nonfree.repo + sudo sed -i '0,/enabled=1/s//enabled=0/' /etc/yum.repos.d/rpmfusion-nonfree-updates.repo + sudo systemctl disable sunshine-workaround.service + ublue-update --wait + rpm-ostree remove -y sunshine + echo "Sunshine has been uninstalled, please reboot to apply changes." # Autostart Sunshine autostart-sunshine: @@ -154,7 +161,7 @@ get-steamcmd: install-openrazer: sudo wget https://download.opensuse.org/repositories/hardware:/razer/Fedora_$(rpm -E %fedora)/hardware:razer.repo -O /etc/yum.repos.d/hardware:razer.repo && \ ublue-update --wait && \ - rpm-ostree install openrazer-meta razergenie && \ + rpm-ostree install -y openrazer-meta razergenie && \ if ! grep -q "plugdev" /etc/group; then \ sudo bash -c 'grep "plugdev" /lib/group >> /etc/group' \ ; fi && \ diff --git a/system_files/desktop/shared/usr/bin/bazzite-flatpak-manager b/system_files/desktop/shared/usr/bin/bazzite-flatpak-manager index 67ed0aefd6..2096e10e14 100755 --- a/system_files/desktop/shared/usr/bin/bazzite-flatpak-manager +++ b/system_files/desktop/shared/usr/bin/bazzite-flatpak-manager @@ -1,7 +1,7 @@ #!/usr/bin/bash # SCRIPT VERSION -VER=13 +VER=14 VER_FILE="/etc/bazzite/flatpak_manager_version" VER_RAN=$(cat $VER_FILE) IMAGE_INFO="/usr/share/ublue-os/image-info.json" diff --git a/system_files/desktop/shared/usr/bin/bazzite-hardware-setup b/system_files/desktop/shared/usr/bin/bazzite-hardware-setup index 5b2a796dd3..94f0576459 100755 --- a/system_files/desktop/shared/usr/bin/bazzite-hardware-setup +++ b/system_files/desktop/shared/usr/bin/bazzite-hardware-setup @@ -7,7 +7,7 @@ IMAGE_FLAVOR=$(jq -r '."image-flavor"' < $IMAGE_INFO) FEDORA_VERSION=$(jq -r '."fedora-version"' < $IMAGE_INFO) # SCRIPT VERSION -HWS_VER=20 +HWS_VER=21 HWS_VER_FILE="/etc/bazzite/hws_version" HWS_VER_RAN=$(cat $HWS_VER_FILE) diff --git a/system_files/desktop/shared/usr/bin/bazzite-user-setup b/system_files/desktop/shared/usr/bin/bazzite-user-setup index ee66457cc9..2b440e2761 100755 --- a/system_files/desktop/shared/usr/bin/bazzite-user-setup +++ b/system_files/desktop/shared/usr/bin/bazzite-user-setup @@ -11,7 +11,7 @@ BAZZITE_CONFIG_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/bazzite" mkdir -p "$BAZZITE_CONFIG_DIR" # SCRIPT VERSION -USER_SETUP_VER=21 +USER_SETUP_VER=22 USER_SETUP_VER_FILE="$BAZZITE_CONFIG_DIR/version" USER_SETUP_FEDORA_VER_FILE="$BAZZITE_CONFIG_DIR/fedora_version" @@ -126,15 +126,21 @@ if [[ $IMAGE_NAME =~ "deck" || $IMAGE_NAME =~ "ally" || $IMAGE_NAME =~ "framegam # Legion Rotation Fix AUTOSTART_FOLDER=${XDG_CONFIG_HOME:-$HOME/.config} - if [[ ":83E1:" =~ ":$SYS_ID" ]] && [[ $BASE_IMAGE_NAME =~ "kinoite" ]]; then - if [[ ! -f "$AUTOSTART_FOLDER/autostart/bazzite-handle-legion-go-rotation.desktop" ]]; then - echo 'Adding legion rotation fix' + + # Remove old legion-only file + if [[ -f "$AUTOSTART_FOLDER/autostart/bazzite-handle-legion-go-rotation.desktop" ]]; then + rm -f "$AUTOSTART_FOLDER/autostart/bazzite-handle-legion-go-rotation.desktop" + fi + + if [[ ":ROG Ally RC71L_RC71L:83E1:" =~ ":$SYS_ID" ]] && [[ $BASE_IMAGE_NAME =~ "kinoite" ]]; then + if [[ ! -f "$AUTOSTART_FOLDER/autostart/bazzite-rotation-fix.desktop" ]]; then + echo 'Adding rotation fix' mkdir -p "$AUTOSTART_FOLDER/autostart" - printf "[Desktop Entry]\nExec=/usr/libexec/bazzite-handle-legion-go-rotation\nIcon=dialog-scripts\nName=bazzite-handle-legion-go-rotation\nType=Application\nX-KDE-AutostartScript=true\n" > "$AUTOSTART_FOLDER/autostart/bazzite-handle-legion-go-rotation.desktop" + printf "[Desktop Entry]\nExec=/usr/libexec/bazzite-rotation-fix\nIcon=dialog-scripts\nName=bazzite-rotation-fix\nType=Application\nX-KDE-AutostartScript=true\n" > "$AUTOSTART_FOLDER/autostart/bazzite-rotation-fix.desktop" fi - elif [[ -f "$AUTOSTART_FOLDER/autostart/bazzite-handle-legion-go-rotation.desktop" ]]; then - echo 'Non-legion or GNOME detected, removing legionfix' - rm -f "$AUTOSTART_FOLDER/autostart/bazzite-handle-legion-go-rotation.desktop" + elif [[ -f "$AUTOSTART_FOLDER/autostart/bazzite-rotation-fix.desktop" ]]; then + echo 'Non-supported or GNOME detected, removing rotaion fix' + rm -f "$AUTOSTART_FOLDER/autostart/bazzite-rotation-fix.desktop" fi fi diff --git a/system_files/desktop/shared/usr/etc/skel/.var/app/com.github.marhkb.Pods/config/pods/connections.json b/system_files/desktop/shared/usr/etc/skel/.var/app/com.github.marhkb.Pods/config/pods/connections.json new file mode 100644 index 0000000000..642aa82962 --- /dev/null +++ b/system_files/desktop/shared/usr/etc/skel/.var/app/com.github.marhkb.Pods/config/pods/connections.json @@ -0,0 +1,8 @@ +{ + "32218213-d8cf-444e-84c7-7f67b9765acd": { + "uuid": "32218213-d8cf-444e-84c7-7f67b9765acd", + "name": "System", + "url": "unix:///run/user/1000/podman/podman.sock", + "rgb": null + } +} \ No newline at end of file diff --git a/system_files/desktop/shared/usr/share/ublue-os/just/80-bazzite.just b/system_files/desktop/shared/usr/share/ublue-os/just/80-bazzite.just index 93e26bfcd6..97690c89e0 100644 --- a/system_files/desktop/shared/usr/share/ublue-os/just/80-bazzite.just +++ b/system_files/desktop/shared/usr/share/ublue-os/just/80-bazzite.just @@ -50,15 +50,22 @@ configure-waydroid: # Install Sunshine install-sunshine: #!/usr/bin/bash - flatpak install --system --noninteractive flathub dev.lizardbyte.app.Sunshine - flatpak run --command=additional-install.sh app/dev.lizardbyte.app.Sunshine/x86_64/stable || true + sudo sed -i '0,/enabled=0/s//enabled=1/' /etc/yum.repos.d/rpmfusion-nonfree.repo + sudo sed -i '0,/enabled=0/s//enabled=1/' /etc/yum.repos.d/rpmfusion-nonfree-updates.repo + systemctl enable sunshine-workaround.service + ublue-update --wait + rpm-ostree install -y sunshine echo "Sunshine is installed!" # Remove Sunshine remove-sunshine: #!/usr/bin/bash - flatpak run --command=remove-additional-install.sh app/dev.lizardbyte.app.Sunshine/x86_64/stable || true - flatpak uninstall --delete-data --noninteractive dev.lizardbyte.sunshine + sudo sed -i '0,/enabled=1/s//enabled=0/' /etc/yum.repos.d/rpmfusion-nonfree.repo + sudo sed -i '0,/enabled=1/s//enabled=0/' /etc/yum.repos.d/rpmfusion-nonfree-updates.repo + systemctl disable sunshine-workaround.service + ublue-update --wait + rpm-ostree remove -y sunshine + echo "Sunshine has been uninstalled." # Autostart Sunshine autostart-sunshine: diff --git a/system_files/desktop/silverblue/usr/etc/dconf/db/local.d/04-bazzite-folders b/system_files/desktop/silverblue/usr/etc/dconf/db/local.d/04-bazzite-folders index 0e9f27bf28..7ef2da1fa4 100644 --- a/system_files/desktop/silverblue/usr/etc/dconf/db/local.d/04-bazzite-folders +++ b/system_files/desktop/silverblue/usr/etc/dconf/db/local.d/04-bazzite-folders @@ -12,7 +12,7 @@ name='X-Pardus-Apps.directory' translate=true [org/gnome/desktop/app-folders/folders/Utilities] -apps=['yafti.desktop', 'webapp-manager.desktop', 'fish.desktop', 'tuned-gui.desktop', 'nvtop.desktop', 'yelp.desktop', 'btop.desktop', 'com.github.tchx84.Flatseal.desktop', 'io.github.flattool.Warehouse.desktop', 'it.mijorus.gearlever.desktop', 'com.mattjakeman.ExtensionManager.desktop', 'org.gnome.tweaks.desktop', 'com.github.GradienceTeam.Gradience', 'io.github.fastrizwaan.WineZGUI.desktop'] +apps=['yafti.desktop', 'webapp-manager.desktop', 'tuned-gui.desktop', 'fish.desktop', 'tuned-gui.desktop', 'nvtop.desktop', 'yelp.desktop', 'btop.desktop', 'com.github.tchx84.Flatseal.desktop', 'io.github.flattool.Warehouse.desktop', 'it.mijorus.gearlever.desktop', 'com.mattjakeman.ExtensionManager.desktop', 'org.gnome.tweaks.desktop', 'com.github.GradienceTeam.Gradience', 'io.github.fastrizwaan.WineZGUI.desktop'] categories=['X-GNOME-Utilities'] name='X-GNOME-Utilities.directory' translate=true