capybar 0.3.0

Wayland native toolbar
Documentation
use std::{
    collections::HashMap,
    sync::{LazyLock, Mutex, MutexGuard},
};

use anyhow::Result;
use fontconfig::Fontconfig;
use thiserror::Error;

/// Wrapper for fontconfig and fontdue. Only one exists per capybar instance and shared safly
/// between difrent [crate::root::Root] instance
pub struct FontsMap {
    fontconfig: Fontconfig,

    fonts_map: Mutex<HashMap<String, usize>>,

    fonts_vec: Mutex<Vec<fontdue::Font>>,
}

static FONTS: LazyLock<FontsMap> = LazyLock::new(|| FontsMap::new().unwrap());

#[derive(Error, Debug)]
pub enum FontsError {
    #[error(transparent)]
    IO(#[from] std::io::Error),
    #[error("Font {0} was not found")]
    FontNotFound(String),
}

impl FontsMap {
    fn new() -> Option<Self> {
        let fc = Fontconfig::new()?;

        Some(FontsMap {
            fontconfig: fc,
            fonts_map: Mutex::new(HashMap::new()),
            fonts_vec: Mutex::new(Vec::new()),
        })
    }
}

pub fn get() -> &'static LazyLock<FontsMap> {
    &FONTS
}

/// Fonts map contains map of font name to index in vector
pub fn fonts_map() -> MutexGuard<'static, HashMap<String, usize>> {
    FONTS.fonts_map.lock().unwrap()
}

/// Fonts vector contains all loaded fonts
pub fn fonts_vec() -> MutexGuard<'static, Vec<fontdue::Font>> {
    FONTS.fonts_vec.lock().unwrap()
}

/// Adds font to current FontsMap instance. Font name is case insensitive. Font gets added to fonts
/// vector and map
pub fn add_font_by_name(name: &str) -> Result<(), FontsError> {
    let font = match FONTS.fontconfig.find(name, None) {
        Some(f) => f,
        None => return Err(FontsError::FontNotFound(name.to_string())),
    };

    let bytes = match std::fs::read(font.path.as_path()) {
        Ok(b) => b,
        Err(e) => return Err(FontsError::IO(e)),
    };

    let font = fontdue::Font::from_bytes(
        bytes,
        fontdue::FontSettings {
            ..Default::default()
        },
    )
    .unwrap();

    let mut fonts_map = FONTS.fonts_map.lock().unwrap();
    let mut fonts_vec = FONTS.fonts_vec.lock().unwrap();
    fonts_map.insert(name.to_string(), fonts_vec.len());
    fonts_vec.push(font);

    Ok(())
}