new WM setup

This commit is contained in:
Henry Hiles 2026-10-20 10:50:11 -04:00
commit 44fbc7a4d4
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
10 changed files with 784 additions and 115 deletions

View file

@ -0,0 +1,89 @@
{
inputs,
pkgs,
...
}:
let
bar = pkgs.writeText "top.blp" ''
using Gtk 4.0;
using Adw 1;
$GayPanel panel {
anchor-direction: top;
monitor: "all";
CenterBox {
margin-start: 4;
margin-end: 4;
margin-top: 4;
margin-bottom: 4;
[start]
$WorkspacesWidget {
show-labels: false;
}
[center]
$ClockWidget {
styles [
"pill",
"flat",
]
format: "%b %d %H:%M";
}
[end]
$SystemControlsWidget {
styles [
"pill",
"flat",
]
}
}
}
'';
# GNOME-style workspace dots, with the active one stretched into a pill
style = pkgs.writeText "style.css" ''
.workspaces button {
min-width: 8px;
min-height: 8px;
padding: 0;
border-radius: 99px;
background: alpha(var(--window-fg-color), 0.4);
transition: min-width 0.2s ease, background 0.2s ease;
}
.workspaces button.selected {
min-width: 32px;
padding: 0;
background: var(--window-fg-color);
}
.workspaces button label {
font-size: 0;
}
'';
in
{
wrappers.gaypanel = {
basePackage =
inputs.gaypanel.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs
(old: {
patches = (old.patches or [ ]) ++ [
./no-sound-panic.patch
];
});
prependFlags = [
"--config"
# Copied rather than linked, since gaypanel ignores symlinks in bars/
"${pkgs.runCommand "gaypanel-config" { } ''
mkdir -p $out/bars
cp ${bar} $out/bars/top.blp
cp ${style} $out/style.css
''}"
];
};
}

View file

@ -0,0 +1,104 @@
--- a/src/system/sound.rs
+++ b/src/system/sound.rs
@@ -6,25 +6,21 @@
use crate::{senders, system::SystemModule};
-/// Gets the default mixer. Throws an error if it doesn't exist (this means you probably don't have ALSA installed)
+/// Gets the default mixer. Returns None if it doesn't exist (yet), e.g. if the sound server hasn't started
macro_rules! mixer {
() => {
- Mixer::new("default", false).expect("[fatal] alsa: Could not find a mixer named \"default\". gaypanel assumes this is the name of the default mixer. \n[fatal] alsa: **If you have ALSA installed and running and you're *sure* of it, open an issue at codeberg.org/pastthepixels/gaypanel!**")
+ Mixer::new("default", false).ok()
};
}
/// Finds the first selem with a playback volume in a mixer. Assumes this is the "default"
/// e.g. this reflects the actual output volume. I don't know how alsa works.
-/// You must have a speaker of some kind or else this'll crap out
+/// Returns None if there's no speaker of some kind (yet)
macro_rules! sink {
($m: expr) => {
$m.iter()
.flat_map(|e| Selem::new(e))
.find(|s| s.has_playback_volume())
- .expect(&format!(
- "[fatal] alsa: Could not find default sink for mixer {:?}",
- $m
- ))
};
}
@@ -54,9 +50,9 @@
senders!(VolumeModule);
fn get() -> Option<Self::ReturnType> {
- let mixer = mixer!();
+ let mixer = mixer!()?;
// Sink stats
- let sink = sink!(mixer);
+ let sink = sink!(mixer)?;
let vol = {
let max = sink.get_playback_volume_range().1 as f64;
let raw_vol = sink
@@ -101,8 +97,12 @@
fn set(val: Self::ReturnType) {
// Sets sink volume
- let mixer = mixer!();
- let sink = sink!(mixer);
+ let Some(mixer) = mixer!() else {
+ return;
+ };
+ let Some(sink) = sink!(mixer) else {
+ return;
+ };
let max = sink.get_playback_volume_range().1 as f64;
sink.set_playback_volume_all((val.0.volume * max) as i64)
.unwrap();
@@ -118,8 +118,17 @@
fn listen() {
std::thread::spawn(|| {
- let mixer = mixer!();
- let sink = sink!(mixer);
+ // Wait for the sound server to provide a mixer with a sink
+ let mixer = loop {
+ if let Some(mixer) = mixer!() {
+ let has_sink = sink!(mixer).is_some();
+ if has_sink {
+ break mixer;
+ }
+ }
+ std::thread::sleep(std::time::Duration::from_secs(1));
+ };
+ let sink = sink!(mixer).unwrap();
let source = source!(mixer);
let mut pollfds = mixer.get().unwrap();
--- a/src/widgets/controls.rs
+++ b/src/widgets/controls.rs
@@ -400,7 +400,10 @@
gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
controller.connect_scroll(|_, _, b| {
glib::spawn_future_local(async move {
- let sound = (system::sound::VolumeModule::get().unwrap().0.volume
+ let Some(stats) = system::sound::VolumeModule::get() else {
+ return;
+ };
+ let sound = (stats.0.volume
- b.signum() * VOLUME_SCROLL_STEP)
.clamp(0., 1.);
system::sound::VolumeModule::set((
@@ -420,7 +423,10 @@
// Setup volume
let this = glib::clone::Downgrade::downgrade(&self);
let rx = system::sound::VolumeModule::rx();
- self.update_sound(system::sound::VolumeModule::get().unwrap());
+ // The listener sends an update once sound is available, so it's fine to skip this
+ if let Some(stats) = system::sound::VolumeModule::get() {
+ self.update_sound(stats);
+ }
glib::spawn_future_local(async move {
while let Ok(stats) = rx.recv().await {
if let Some(this) = this.upgrade() {