Baştan Beri Çalışan Ses Tuşları: Bir Sway Hata Ayıklama Polisiyesi
Ses tuşlarım “çalışmayı bıraktı” — ve bozuk olmayan tek şeyin tuşlar olduğu ortaya çıktı.
Kara ekran yazısının destanın sonu olduğunu sanmıştım. Meğer bir hobinin başlangıcıymış.
Birkaç gün önce, Dell XPS 13’ümde kara bir ekranı gerçekten sevdiğim bir Sway masaüstüne nasıl çevirdiğimi yazmıştım. Bu, bir sonraki bölüm ve sürekli yeniden öğrendiğim bir kıssası var: Linux’ta bir denetim “hiçbir şey yapmıyorsa”, bastığın şey nadiren bozuk olan şeydir.
Şikâyet
Belirti · ne ses ne çubukSes tuşlarım çalışmayı bıraktı. Her iki türü de: gövdenin yanındaki fiziksel tuş ve klavyedeki Fn tuşları. Basıyorsun ve… hiçbir şey. Ne ses değişiyor ne de ekranda bir çubuk. İki yolda da aynı ölü sessizlik.
Acı ironi — ki bunu sonradan fark ettim — bunun, geçen yazıda evcilleştirmekle övündüğüm tam o ses bağlaması olması. “İhtiyacım olduğunu bilmediğim emniyet kemeri” dediğim şu satırı hatırlıyor musun?
bindsym XF86AudioRaiseVolume exec wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+
O emniyet kemeri hâlâ takılıydı. Sadece araba artık yola bağlı değildi. Bunu aklında tut.
1. Adım: tuşlar Sway’e ulaşıyor mu ki?
İkiye bölme · tuş vuruşu vardı mı?İlk içgüdüm, geçen seferki gibi, “bir kısayolu bozdum” oldu. Ama bir şeyi parçalamadan önce, tüm sorunu ikiye bölen 30 saniyelik bir test var: bağlamanın kendini duyurmasını sağla. Gerçek komutun yanına her ses tuşuna bir günlük satırı ekledim:
bindsym XF86AudioRaiseVolume exec echo "$(date +%T) raise" >> /tmp/volkeys.log; wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+ bindsym XF86AudioLowerVolume exec echo "$(date +%T) lower" >> /tmp/volkeys.log; wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%- bindsym XF86AudioMute exec echo "$(date +%T) mute" >> /tmp/volkeys.log; wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
Sonra swaymsg reload, tuşlara bas ve bak:
cat /tmp/volkeys.log
13:56:45 raise 13:56:46 lower 13:56:53 mute
Günlük doluydu. Her basış — hem gövde tuşu hem klavye — Sway’e ulaştı ve bağlamasını kusursuz tetikledi. (Eğlenceli ayrıntı: gövdedeki tuş, Sway’e klavyeden ayrı bir Intel HID 5 button array aygıtı olarak görünüyor. İkisi de çalıştı.)
Demek tuşlar masumdu. Sorun her ne ise tamamen tuş vuruşunun ardında yaşıyordu. Tıpkı geçen seferki kara çalışma alanı gibi, ilk şüphelim kusursuz davranan parçaydı.
2. Adım: komut boşluğa bağırıyordu
Teşhis · iki ses sunucusuBağlama, PipeWire’ı denetleyen wpctl’yi çalıştırıyordu. Ben de atladığım o bariz soruyu sordum: hoparlörlerimi şu an gerçekte ne sürüyor?
pactl info | grep -i "server name"
# Server Name: pulseaudioİşte oradaydı. Sade, bağımsız PulseAudio ses donanımının sahibiydi — ama tuşlarım nazikçe PipeWire’dan sesi değiştirmesini istiyordu. İkisi de kurulu ve aynı anda çalışıyordu:
pgrep -a pulseaudio # the real PulseAudio — holding the speakers hostage pgrep -a pipewire # also running pgrep -a wireplumber # also running
Her basış kimsenin duyamadığı bir PipeWire çıkışını değiştiriyordu, PulseAudio ise sessizce gerçek çıkışı tutuyordu. Geçen yazıdaki emniyet kemeri bağlaması kusursuzdu; tekerleksiz bir arabaya bağlanmıştı. Birkaç gün önce harika çalışan aynı satırın şimdi hiçbir şey yapmamasının nedeni buydu — iki yazı arasında bir yerde, gerçek pulseaudio paketi geri sızmış ve donanımı PipeWire’ın elinden çalmıştı.
Bu tam da geçen sefer geçmişteki bana karşı uyardığım türden sessiz bir X11/Wayland kalıntısı, sadece ses kılığına girmiş.
Birkaç gün önce harika çalışan aynı satır şimdi hiçbir şey yapmıyordu — tekerleksiz bir arabaya bağlanmış emniyet kemeri.
Çözüm, 1. kısım: iki değil, tek yığın
Çözüm · bağımsız PulseAudio’yu çıkarFedora’nın amaçladığı kurulum baştan aşağı PipeWire’dır; pipewire-pulseaudio, PulseAudio API’sini bir ara katman olarak sağlar. Ben de gerçek arka plan servisini tahliye ettim:
sudo dnf swap --allowerasing pulseaudio pipewire-pulseaudio systemctl --user enable --now pipewire-pulse.socket pipewire-pulse.service
Bir tuzak hemen ısırdı: takastan hemen sonra pactl, Connection refused ile başarısız oldu, çünkü pipewire-pulse kuruluydu ama henüz çalışmıyordu. systemctl --user enable --now satırı onu uyandıran şey. Görmek istediğin sunucu adıyla doğrula:
pactl info | grep -i "server name"
# Server Name: PulseAudio (on PipeWire 1.4.11)O (on PipeWire) ifadesi bütün mesele: tek yığın, donanım için halat çekişmesi yok. Artık wpctl ve tuşlarım gerçek sesi yönetiyor.
Çözüm, 2. kısım: görmek için bir çubuk
Kurulum · wob ile ekranda bir çubukŞikâyetimin diğer yarısı, bir ses göstergesini hiç görmememdi. Bu bir hata değil — Sway’in yerleşik bir OSD’si yok. (waybar’ım bir ses sayısı gösteriyor ama ben tuşa basınca beliren o büyük kayan çubuğu istiyordum.)
Bunun için başvurulan araç swayosd ama Fedora 43 için paketlenmemiş. Depolarda olan şey wob — sevimli derecede küçük, içine aktardığın sayılardan bir çubuk çizen bir “Wayland Overlay Bar”.
sudo dnf install -y wob
wob, stdin’den satır başına bir tam sayı (0–100) okur. Geleneksel kurgu, takip ettiği bir FIFO’dur. Sway yapılandırmamda:
exec rm -f $XDG_RUNTIME_DIR/wob.sock && mkfifo $XDG_RUNTIME_DIR/wob.sock && tail -f $XDG_RUNTIME_DIR/wob.sock | wob
Sonra küçük bir sarmalayıcı sesi değiştiriyor ve yeni seviyeyi FIFO’ya yazıyor. Benimki ~/.config/sway/volume-wob.sh’de duruyor:
#!/bin/sh # Adjust the default sink via wpctl (PipeWire-native) and push the resulting # level (0-100) to the wob FIFO so Sway shows an on-screen bar. export LC_ALL=C WOBSOCK="${XDG_RUNTIME_DIR}/wob.sock" SINK="@DEFAULT_AUDIO_SINK@" case "$1" in up) wpctl set-mute "$SINK" 0; wpctl set-volume -l 1.0 "$SINK" 5%+ ;; down) wpctl set-mute "$SINK" 0; wpctl set-volume "$SINK" 5%- ;; mute) wpctl set-mute "$SINK" toggle ;; esac vol=$(wpctl get-volume "$SINK" 2>/dev/null) # e.g. "Volume: 0.10" or "... [MUTED]" case "$vol" in *MUTED*) level=0 ;; *) level=$(printf '%s' "$vol" | awk '{printf "%d", $2 * 100}') ;; esac [ -z "$level" ] && level=0 [ "$level" -gt 100 ] && level=100 # timeout guards against a dead wob reader hanging the keypress (see gotcha #3) [ -p "$WOBSOCK" ] && timeout 1 sh -c "printf '%s\n' '$level' > '$WOBSOCK'"
Ve bağlamalar, nihayet işe yarar bir yeri gösteriyor — dikkat et, geçen yazının -l 1.0 emniyet kemeri tam ait olduğu yerde duruyor:
bindsym XF86AudioRaiseVolume exec ~/.config/sway/volume-wob.sh up bindsym XF86AudioLowerVolume exec ~/.config/sway/volume-wob.sh down bindsym XF86AudioMute exec ~/.config/sway/volume-wob.sh mute bindsym XF86AudioMicMute exec wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle
Şimdi bir tuşa bas, ekranda çubuk yukarı kayarken ses de gerçekten değişiyor. Şikâyetin iki yarısı da yok oldu.

Kan akıtan üç tuzak
Tuzaklar · kan akıtan üçüGeçen sefer %3359 ses ve tam ekran bir video köprüsüydü. Bu sefer:
- wob tek bir bozuk satırla ölür. Ona tek bir boş dizge ver — diyelim ses ayrıştırman sessizce başarısız oldu — Invalid value received yazıp çıkar. Bu yüzden betik seviyeyi her zaman bir sayıya varsayıyor ve sınırlıyor. wob’a asla boş satır aktarma.
- Yöreselleştirmem beni susturmaya çalıştı. wpctl, 0.10 yazdırır. Almanca yöremde awk, virgülü ondalık ayıracı sanıp 0.10’u düz bir 0’a çevirir. Betikte LC_ALL=C zorlamak ondalığı dürüst tutar. (Durum barına inan; yöreselleştirmene güvenme.)
- Okuyucusu olmayan bir FIFO’ya yazmak sonsuza dek askıda kalır. wob çalışmıyorsa, printf > fifo bloklanır — ve tuş vuruşunu da yanında götürür. Yazımı timeout 1 ile sarmak, ölü bir çubuğun ses tuşlarını donduramayacağı anlamına gelir.
Bir de Sway’e özgü dipnot: exec, swaymsg reload’da değil, girişte çalışır. Yani FIFO-ve-wob satırı bir yeniden yüklemede yeniden başlamaz; çubuk oturum ortasında kaybolursa çıkış yapıp tekrar gir.
Durum barına inan; yöreselleştirmene güvenme.
Yan görev: prize takılıyken beni kilitleme
Yan görev · sadece pilde kilitleHazır buradayken bir sıkıntı daha: masamda, prize takılıyken, ekran beş dakika sonra beni dışarı kilitleyip duruyordu — diğer ekranda bir şey okumak için her dönüşümde bir parola istemi. Bu, swayidle’ın ona söylediğimi yapması:
exec swayidle -w \
timeout 300 'swaylock -f -c 000000' \
timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
before-sleep 'swaylock -f -c 000000'Beş dakikada kilitle, onda arka ışığı söndür. Arka ışığın sönmesi sorun değil — bir fare oynatması paneli geri getiriyor, parola yok. Şarjdayken gevşetmek istediğim şey kilit: onuncu dakikada arka ışık yine sönsün ama prizdeyken tam bir saat geçene dek kilitleme. Pilde beş dakikalık kilit kalsın — hızlı kilitlenmesini tam da o zaman istersin.
swayidle şarj aletini göremez, bu yüzden güç denetiminin komutun içinde yaşaması gerek. Erken kilidi küçük bir betiğin arkasına aldım ve saat işaretine ikinci, koşulsuz bir kilit ekledim:
exec swayidle -w \
timeout 300 "$HOME/.config/sway/lock-idle.sh" \
timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' \
timeout 3600 'swaylock -f -c 000000' \
before-sleep 'swaylock -f -c 000000'lock-idle.sh yalnızca pilde kilitler; prizde çekilip 60 dakikalık zaman aşımına bırakır. Çekirdeğin güncel tuttuğu tek bir dosyayı okuyan beş satır — şarj aleti takılıyken /sys/class/power_supply/AC/online değeri 1’dir:
#!/bin/sh # Called by swayidle at the short (5 min) timeout. On AC, skip locking and let # the 60-min timeout lock instead (the backlight still powers off at 10 min). # On battery, lock right away. for ac in /sys/class/power_supply/A{C,DP}*/online; do [ -r "$ac" ] && [ "$(cat "$ac")" = "1" ] && exit 0 # on AC: don't lock yet done swaylock -f -c 000000
İki not: A{C,DP}* glob’u, bağdaştırıcıyı AC yerine ADP1 diye adlandıran dizüstüleri kapsar; arka ışık satırı da eski dpms off değil, output * power off (Sway 1.11). Ses çubuğuyla aynı giriş-değil-yeniden-yükleme uyarısı geçerli — swayidle, swaymsg reload’da yeniden başlamaz, o yüzden pkill swayidle yapıp yeniden başlat.
Geçmişteki bana söyleyeceklerim (ses sürümü)
- Ölü bir denetim genelde bir tuş sorunu değil, bir yönlendirme sorunudur. Bir günlükleme bağlaması (echo >> dosya) otuz saniye sürer ve tuş vuruşunun gelip gelmediğini anında söyler. Benimki hep geldi.
- Kısayola dokunmadan önce donanımın sahibini sor. Önce çalıştırsaydım pactl info bunu tek satırda bitirirdi. İki ses sunucusu, tek takım hoparlör — sadece biri kazanır ve o, tuşlarımın konuştuğu değildi.
- Dün çalışan bir bağlama bir yazım hatasıyla değil, bir paketle sabote edilebilir. Satır geçen haftakiyle aynıydı. Altındaki yığın değildi.
- Depolarında var olan araçları seç. swayosd hoş ama Fedora 43’te hiç yok; wob mütevazı ve tam orada. Mütevazı-ve-mevcut, zarif-ve-hayalî olana üstün gelir.
- swayidle dünyayı göremez; ona görebilen bir betik ver. Koşullu olan her şey — güç durumu, günün saati — swayidle’ın kendisinde değil, çalıştırdığı komutta yaşar. Çekirdek şarj durumunu zaten tek satırlık bir dosyada veriyor; bırak beş satırlık bir hakem okusun ve karar versin.
Kara ekran yazısı bana sistemin en sakin görünen parçasının çoğu zaman suçlu olan olduğunu öğretmişti. Ses tuşları bunu yine kanıtladı — ben neredeyse onları baştan yazarken onlar orada kusursuz çalışıyordu; tıpkı kara bir dikdörtgenin altına saklanmış o kusursuz, sadık foot terminali gibi. Sanırım Sway çalıştırmanın bütün karakteri bu: hiçbir şey senin için bir şey yapmaz, dolayısıyla bir şey aksadığında çözüm hep gerçek, hep anlaşılır ve neredeyse hiçbir zaman ilk parmağını uzattığın yerde değildir.
Temizliğim bile bana direndi
Sonsöz · kendini vuran komutSon bir kahkaha. Başıboş test süreçlerini toparlarken, eski çubuğu öldürmek için o bariz tek satırlığa uzandım:
pkill -f 'tail -f .*wob.sock'
…ve kabuğum komutun ortasında öldü. İki kez. Görmem utanç verici derecede uzun sürdü: pkill -f, her sürecin tüm komut satırına karşı eşleşir — pkill’imi çalıştıran kabuğun kendisi dâhil; onun komut satırı da harfiyen tail -f .*wob.sock dizgesini içeriyordu. Kendini avlayıp vuran bir komut yazmıştım. Sway yolculuğu, anlaşılan, fraktal: temizliğin bile içinde bekleyen bir kara-çalışma-alanı anı var.