# oxide-gui
A portable, `no_std` GUI framework for systems software — designed to power desktop environments like **OxideOS**, with a Linux backend for development and testing.
Inspired by the architecture of [GNOME](https://www.gnome.org/) and modern Wayland compositors, oxide-gui provides the primitive drawing layer that a higher-level shell or compositor would sit on top of. It is intentionally low-level: immediate-mode rendering, a single `Backend` trait, and zero heap allocation in the core.
---
## Vision
OxideOS is a Rust-first operating system. oxide-gui is its graphical foundation:
```
OxideOS Kernel
└── oxide-gui-core (no_std, runs in kernel/userspace)
├── Canvas (widget composition layer)
├── Backend trait (framebuffer + input contract)
└── Event system (keyboard, mouse, window events)
│
├── MinifbBackend (Linux/X11/Wayland, for dev)
└── OxideBackend (kernel framebuffer, production)
```
Like GNOME's Mutter compositor owns the framebuffer and delegates rendering to GTK apps via Wayland, OxideOS will expose a `Backend` implementation that maps directly to kernel graphics and HID drivers. Application code is unchanged between environments.
---
## Workspace Layout
```
oxide-gui/
├── crates/
│ ├── oxide-gui-core/ # no_std portable core — zero dependencies
│ └── oxide-gui-linux/ # Linux backend (minifb, X11/Wayland)
│ └── examples/
│ ├── hello_window.rs # minimal counter app
│ └── widgets_demo.rs # full desktop UI showcase
```
---
## Quick Start
Add the Linux backend to your `Cargo.toml`:
```toml
[dependencies]
# From GitHub (recommended)
oxide-gui-linux = { git = "https://github.com/shukla-surendra/oxide-gui", branch = "master" }
# Or from a local checkout
# oxide-gui-linux = { path = "crates/oxide-gui-linux" }
```
For OxideOS kernel integration (no_std, no Linux backend needed):
```toml
[dependencies]
oxide-gui-core = { git = "https://github.com/shukla-surendra/oxide-gui", branch = "master" }
```
Minimal window loop:
```rust
use oxide_gui_linux::MinifbBackend;
use oxide_gui_core::{Canvas, Event, Key, palette};
fn main() {
let mut backend = MinifbBackend::new("My App", 800, 600).unwrap();
let mut canvas = Canvas::new(&mut backend);
while backend.is_open() {
canvas.clear(palette::DARK_BG);
canvas.draw_text(32, 32, "Hello, OxideOS!", palette::TEXT);
canvas.present();
while let Some(event) = canvas.poll_event() {
if event.is_close() { return; }
}
}
}
```
Run the bundled examples:
```bash
# Gradient counter demo (640×400) — keyboard + mouse input
cargo run --example hello_window -p oxide-gui-linux
# Full GNOME-style desktop (1280×800) — animated, tabbed, interactive
cargo run --example widgets_demo -p oxide-gui-linux
```
---
## Running & Testing the Examples
Both examples require a running X11 or Wayland display (any Linux desktop session). If you are on SSH without X forwarding, prepend `DISPLAY=:0`:
```bash
DISPLAY=:0 cargo run --example widgets_demo -p oxide-gui-linux
```
### `widgets_demo` controls (1280×800)
| `Tab` | Cycle tabs: Overview → Terminal → Apps → Settings |
| Mouse click on tab bar | Jump directly to that tab |
| Mouse hover | Highlights dock icons, app tiles, and buttons |
| `Q` or `Escape` | Quit |
| Window close | Quit |
**What to watch for:**
- **Overview tab** — CPU / RAM / GPU / Net bars animate in real time; mini CPU history chart updates live; clock in the top bar shows real system time
- **Terminal tab** — blinking cursor at the prompt; syntax-colored `ls` and `cat` output
- **Apps tab** — hover over any app tile to see shadow-lift effect
- **Settings tab** — toggle switches shown in teal (on) / dark (off); Apply button brightens on hover
### `hello_window` controls (640×400)
| `↑` or `→` | Increment counter |
| `↓` or `←` | Decrement counter |
| `R` | Reset counter to zero |
| Click `+ Increment` | Increment |
| Click `- Decrement` | Decrement |
| Click `Reset` | Reset |
| `Q` or `Escape` | Quit |
Counter box gradient and label change colour as the value grows: dark → teal → blue → amber → rose.
---
## Core API
### `Backend` trait — `oxide-gui-core`
The single abstraction over any display surface. Implement this to target a new platform.
| `width() -> u32` | Framebuffer width in pixels |
| `height() -> u32` | Framebuffer height in pixels |
| `fill_rect(x, y, w, h, color)` | Draw a solid rectangle |
| `present()` | Flush back-buffer to screen |
| `poll_event() -> Option<Event>` | Dequeue one input event |
Provided methods with default implementations (override for hardware acceleration):
| `draw_char(x, y, ch, color)` | Render a single glyph (8×16 bitmap) |
| `draw_text(x, y, text, color)` | Render a string |
| `hline(x, y, w, color)` | Horizontal 1px line |
| `vline(x, y, h, color)` | Vertical 1px line |
| `draw_rect(x, y, w, h, color)` | Hollow rectangle border |
| `clear(color)` | Fill entire framebuffer |
| `size() -> (u32, u32)` | Return (width, height) |
### `Canvas<B: Backend>` — high-level drawing
Wraps a `Backend` reference and adds composed widget helpers:
| `panel(x, y, w, h, fill, border)` | Filled rectangle with border |
| `title_bar(x, y, w, h, title, bg)` | Header bar with white text |
| `button(x, y, w, h, label, fill, border, text_color)` | Clickable button with centered label |
| `progress_bar(x, y, w, h, percent, track, fill, border)` | Horizontal progress indicator |
| `gradient_progress(x, y, w, h, percent, track, fill_l, fill_r, border)` | Progress bar with two-stop gradient fill |
| `gradient_h(x, y, w, h, left, right)` | Horizontal gradient fill left→right |
| `gradient_v(x, y, w, h, top, bottom)` | Vertical gradient fill top→bottom |
| `shadow_panel(x, y, w, h, fill, border)` | Panel with 4px drop shadow |
| `fill_rounded(x, y, w, h, color)` | Rectangle with 1px corner clip |
| `icon_tile(x, y, size, bg, label)` | Square app icon tile with colored background and centered label |
| `accent_bar(x, y, h, color)` | 3px vertical selection indicator bar |
| `dot(x, y, color)` | 4×4 filled status dot |
| `divider_h(x, y, w)` | Horizontal rule using `palette::DIVIDER` |
| `divider_v(x, y, h)` | Vertical rule using `palette::DIVIDER` |
| `centered_text(x, y, w, text, color)` | Horizontally centered string |
| `right_text(x, y, w, text, color)` | Right-aligned string |
| `backend_mut() -> &mut B` | Access the underlying backend |
### Events
```rust
match event {
Event::KeyDown(Key::Char('q')) => quit(),
Event::KeyDown(Key::Enter) => confirm(),
Event::MouseButton { x, y, button: MouseButton::Left, pressed: true } => click(x, y),
Event::MouseMove { x, y } => hover(x, y),
Event::Scroll { delta } => scroll(delta),
Event::Resize { width, height } => resize(width, height),
Event::Close => quit(),
_ => {}
}
```
`Key` covers: `Char(char)`, `Enter`, `Backspace`, `Delete`, `Escape`, `Tab`, `Space`, arrow keys, Home/End/PageUp/PageDown, `LeftShift`/`RightShift`/`LeftCtrl`/`RightCtrl`/`LeftAlt`/`RightAlt`, and `F(u8)` for F1–F12.
### Color
```rust
use oxide_gui_core::color::{self, palette};
let red = color::rgb(255, 0, 0);
let faded = color::argb(128, 255, 0, 0);
// Extract components
let r = color::red(c);
let g = color::green(c);
let b = color::blue(c);
let a = color::alpha(c);
```
**Built-in palette** (`palette::*`):
| `BLACK`, `WHITE` | Pure extremes |
| `DARK_GRAY`, `GRAY`, `LIGHT_GRAY` | UI neutrals |
| `RED`, `GREEN`, `BLUE`, `YELLOW`, `CYAN` | Standard accents |
| `PURPLE`, `DEEP_PURPLE` | Violet / indigo accents |
| `PINK`, `ROSE` | Hot pink / rose red |
| `ORANGE`, `AMBER` | Warm highlight tones |
| `TEAL`, `NEON_CYAN` | Cool green-cyan accents |
| `INDIGO`, `ELECTRIC_BLUE` | Deep blue accents |
| `SURFACE`, `SURFACE2` | Deep-space dark backgrounds |
| `CARD_BG`, `CARD_BORDER` | Card fill and border |
| `ACCENT` | Primary brand color |
| `DARK_BG`, `PANEL_BG`, `TOOLBAR_BG` | Legacy theme backgrounds |
| `TEXT` | Primary text |
| `TEXT_DIM` | Secondary/muted text |
| `DIVIDER` | Separator lines |
`lerp_color(a, b, t, steps)` interpolates between any two colors using integer arithmetic — safe in `no_std`:
```rust
use oxide_gui_core::color::lerp_color;
let mid = lerp_color(palette::PURPLE, palette::ELECTRIC_BLUE, 1, 2); // midpoint
```
### Font
The embedded 8×16 bitmap font covers ASCII 0x20–0x7E (printable range). No external font files are needed — glyph data is baked into the binary as a const array.
```rust
use oxide_gui_core::font;
let px_wide = font::text_width("Settings"); // returns u32
// CHAR_W = 8, CHAR_H = 16
```
---
## Implementing a Custom Backend
Port to a new platform by implementing `Backend`:
```rust
use oxide_gui_core::{Backend, Color, Event, color};
pub struct KernelFramebuffer {
fb: *mut u32,
width: u32,
height: u32,
}
impl Backend for KernelFramebuffer {
fn width(&self) -> u32 { self.width }
fn height(&self) -> u32 { self.height }
fn fill_rect(&mut self, x: u32, y: u32, w: u32, h: u32, c: Color) {
let packed = color::to_rgb24(c);
for row in y..y + h {
for col in x..x + w {
unsafe {
*self.fb.add((row * self.width + col) as usize) = packed;
}
}
}
}
fn present(&mut self) {
// kernel flush / vsync
}
fn poll_event(&mut self) -> Option<Event> {
// read from kernel HID ring buffer
None
}
}
```
For the full OxideOS reference template see `crates/oxide-gui-core/src/oxide_os_integration.rs`.
---
## OxideOS Integration Roadmap
oxide-gui is designed to grow into a full desktop environment stack:
| `oxide-gui-core` | Done | `no_std` primitives, Backend trait, Canvas widgets |
| `oxide-gui-linux` | Done | minifb backend for Linux development |
| `oxide-gui-oxide` | Planned | Kernel framebuffer backend for OxideOS |
| Window manager | Planned | Tiling/stacking compositor (analogous to Mutter) |
| Application shell | Planned | App launcher, top bar, notification tray (analogous to GNOME Shell) |
| Widget toolkit | Planned | Retained-mode widget tree above Canvas (analogous to GTK4) |
| Wayland protocol | Future | Wayland compositor for running third-party apps |
The immediate-mode `Canvas` layer maps to what GTK does internally: primitive drawing commands against a surface. The retained widget tree, layout engine, and accessibility layer are the next step up.
---
## Design Principles
- **`no_std` core** — compiles in a kernel or on bare metal with no allocator
- **Single trait boundary** — swap the entire platform by changing one type
- **Immediate-mode rendering** — no hidden state; the app owns the render loop
- **Zero external deps in core** — bitmap font, color math, and event types are self-contained
- **GNOME-aligned vocabulary** — panel, title bar, status bar, sidebar, tab bar match GNOME Shell's component model, easing the path to a familiar desktop UX
---
## License
MIT OR Apache-2.0