1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use crate::widget::{Widget, WidgetUpdate};
use crate::protocol::{Block, ColorRGB};

use alsa::mixer::{Mixer, Selem, SelemId, SelemChannelId};
use alsa::Result;

use std::ffi::CString;

/// The system volume widget
pub struct VolumeWidget {
    device: CString,
    #[allow(dead_code)]
    mixer : CString,
    selem_id: SelemId,
}

impl VolumeWidget {
    fn get_volume(&self) -> Result<Option<(bool, u32)>> {
        let mut handle = Mixer::open(false)?;
        handle.attach(self.device.as_c_str())?;
        Selem::register(&mut handle)?;
        handle.load()?;

        if let Some(selem) = handle.find_selem(&self.selem_id) {
            let (min,max) = selem.get_playback_volume_range();
            let vol = selem.get_playback_volume(SelemChannelId::FrontLeft)?;
            let mute = selem.get_playback_switch(SelemChannelId::FrontLeft)?;
            let vol = ((100 * (vol - min)) as f32 / (max - min) as f32).round() as u32;
            return Ok(Some((mute == 0, vol)));
        }

        return Ok(None);
    }

    /// Creates new widget for the given mixer and channel id
    pub fn new(device:&str, mixer:&str, idx:u32) -> Self {
        let device = CString::new(device).unwrap();
        let mixer = CString::new(mixer).unwrap();

        let mut selem_id = SelemId::empty();
        selem_id.set_name(mixer.as_c_str());
        selem_id.set_index(idx);

        Self { device, mixer, selem_id }
    }
}

impl Widget for VolumeWidget {
    fn update(&mut self) -> Option<WidgetUpdate> {
        if let Ok(Some((mute, vol))) = self.get_volume() {
            let icon = if !mute { "🔊" } else {"🔇"};
            let status = format!("{}%{}", vol, icon);
            let mut data = Block::new().append_full_text(&status).clone();
            if mute { 
                data.color(ColorRGB::yellow());
            } 

            return Some(WidgetUpdate {
               refresh_interval: std::time::Duration::new(1, 0),
               data:Some(data)
            })
        }

        None
    }
}