Skip to content

AndrielFR/awf

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

AWF — Andriel's Widget Framework

A toolkit-agnostic widget framework for building desktop shells and system panels in Rust. AWF provides system services (brightness, network, audio, etc.) with adapters for popular GUI toolkits.

Rust License

Overview

AWF separates system service logic from UI toolkit code, allowing you to:

  • Write system services once (brightness, network, battery, etc.)
  • Use them with any async runtime (tokio, compio, smol, async-std)
  • Integrate with any GUI toolkit via adapters

Architecture

Crate Structure

Crate Purpose Dependencies
awf Service traits and implementations async-watch, async-fs, futures-timer
awf-iced Iced toolkit adapter iced
awf-relm4 Relm4/GTK4 toolkit adapter relm4

Key Design Principles

  1. Runtime Agnostic: Core services use only futures and runtime-agnostic crates
  2. Stream-Based: Services expose futures::Stream<Item = Event> for reactive updates
  3. Cold Start: Subscribers immediately receive current state, then updates
  4. Graceful Degradation: Services emit Unavailable/Error events instead of panicking

Quick Start

Basic Usage with Iced

use awf::services::brightness::{BrightnessService, BrightnessEvent};
use awf::WritableService;
use iced::{Subscription, Task};

struct App {
    service: Arc<BrightnessService>,
    brightness: Option<f64>,
}

#[derive(Debug, Clone)]
enum Message {
    Brightness(BrightnessEvent),
    SetBrightness(f64),
}

impl App {
    fn subscription(&self) -> Subscription<Message> {
        awf_iced::subscribe(&*self.service, Message::Brightness)
    }

    fn update(&mut self, msg: Message) -> Task<Message> {
        match msg {
            Message::Brightness(BrightnessEvent::Changed { percentage, .. }) => {
                self.brightness = Some(percentage);
            }
            Message::SetBrightness(val) => {
                return Task::perform(
                    self.service.send(BrightnessCommand::Set(val)),
                    |_| Message::Brightness(BrightnessEvent::Changed {
                        percentage: val, raw: 0, max: 0
                    })
                );
            }
            _ => {}
        }
        Task::none()
    }
}

Basic Usage with Relm4

use awf::services::brightness::{BrightnessService, BrightnessEvent};
use relm4::ComponentSender;

fn init(
    _init: Self::Init,
    root: Self::Root,
    sender: ComponentSender<Self>,
) -> ComponentParts<Self> {
    let service = Arc::new(BrightnessService::new());
    service.spawn();

    // Bridge service events to Relm4 messages
    awf_relm4::subscribe(&*service, sender.clone(), Msg::Brightness);

    // ...
}

Services

Service Module Backend
Brightness services::brightness sysfs (/sys/class/backlight)

Planned Services

Service Backend
Network NetworkManager (D-Bus) through nmrs
Audio PipeWire/PulseAudio
Battery UPower (D-Bus)
Notifications Freedesktop notifications

Creating Custom Services

Implement the Service trait for your own system monitors:

use awf::{Service, ServiceError};
use futures::Stream;
use std::pin::Pin;

pub struct MyService {
    // ...
}

#[derive(Debug, Clone)]
pub enum MyEvent {
    Data(String),
    Unavailable,
    Error(ServiceError),
}

impl Service for MyService {
    type Event = MyEvent;

    fn subscribe(&self) -> Pin<Box<dyn Stream<Item = Self::Event> + Send>> {
        let rx = self.rx.clone();

        Box::pin(futures::stream::unfold(rx, |mut rx| async move {
            let val = rx.recv().await.ok()?;

            Some((val, rx))
        }))
    }
}

Development

Building

cargo build --workspace

Testing

cargo test -p awf

License

Licensed under MIT license (LICENSE)

Acknowledgments

Inspired by:

  • eww — Widget system concepts
  • ashell — Service patterns

About

Andriel's Widget "Framework"

Resources

License

Stars

Watchers

Forks

Contributors