Menu
cosmic-mobile
publicLatest change d3d7004802d01b8485c3bf66f0ac23532e0e00ce - Lock dependencies and format shell by AkurAI Build
use eframe::{egui, glow::HasContext};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[derive(Default, PartialEq, Debug, Clone, Copy)]
enum Page {
#[default]
Home,
Notes,
System,
}
struct Shell {
page: Page,
notes: String,
renderer: String,
}
fn clock(seconds: u64) -> String {
format!("{:02}:{:02}", seconds / 3600 % 24, seconds / 60 % 60)
}
impl eframe::App for Shell {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
ctx.request_repaint_after(Duration::from_secs(1));
if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
self.page = Page::Home;
}
egui::TopBottomPanel::bottom("navigation")
.frame(
egui::Frame::new()
.fill(egui::Color32::from_rgb(23, 28, 34))
.inner_margin(20),
)
.show(ctx, |ui| {
ui.columns(3, |cols| {
for (col, (label, page)) in cols.iter_mut().zip([
("Home", Page::Home),
("Notes", Page::Notes),
("System", Page::System),
]) {
if col
.add_sized(
[col.available_width(), 52.],
egui::Button::new(label).selected(self.page == page),
)
.clicked()
{
self.page = page;
}
}
});
});
egui::CentralPanel::default()
.frame(
egui::Frame::new()
.fill(egui::Color32::from_rgb(23, 28, 34))
.inner_margin(28),
)
.show(ctx, |ui| {
ui.label(
egui::RichText::new("COSMIC / MOBILE")
.size(16.)
.color(egui::Color32::from_rgb(156, 207, 218)),
);
ui.add_space(10.);
ui.label("Rust shell · Wayland prototype");
ui.add_space(48.);
match self.page {
Page::Home => {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
ui.label(egui::RichText::new(clock(now)).size(76.));
ui.label("UTC");
ui.add_space(48.);
ui.heading("Your space.");
ui.add_space(12.);
ui.label("A small beginning. No desktop clutter.");
ui.add_space(32.);
if ui
.add_sized(
[ui.available_width(), 60.],
egui::Button::new("Open scratchpad"),
)
.clicked()
{
self.page = Page::Notes;
}
ui.add_space(16.);
ui.label("Notes stay in memory for this session.");
}
Page::Notes => {
ui.heading("Scratchpad");
ui.add_space(16.);
ui.label("Session only — closing the shell clears this text.");
ui.add_space(16.);
ui.add_sized(
[ui.available_width(), 320.],
egui::TextEdit::multiline(&mut self.notes)
.hint_text("Write something…")
.font(egui::TextStyle::Body),
);
}
Page::System => {
ui.heading("System");
ui.add_space(24.);
ui.label("Graphics renderer");
ui.label(egui::RichText::new(&self.renderer).size(19.));
ui.add_space(24.);
ui.label(format!("Architecture: {}", std::env::consts::ARCH));
ui.label("Display: Wayland / OpenGL");
ui.add_space(24.);
ui.label(
"VM hardware is virtual. This does not validate OnePlus 13R drivers.",
);
}
}
});
}
}
fn main() -> eframe::Result {
if std::env::var_os("WAYLAND_DISPLAY").is_none() {
eprintln!("Wayland session required");
std::process::exit(1);
}
let options = eframe::NativeOptions {
renderer: eframe::Renderer::Glow,
viewport: egui::ViewportBuilder::default()
.with_inner_size([450., 990.])
.with_fullscreen(true),
..Default::default()
};
eframe::run_native(
"cosmic-mobile",
options,
Box::new(|cc| {
let gl = cc.gl.as_ref().ok_or("OpenGL context missing")?;
// SAFETY: eframe owns a current GL context during creation.
let renderer = unsafe { gl.get_parameter_string(eframe::glow::RENDERER) };
if ["llvmpipe", "softpipe", "swrast", "swiftshader"]
.iter()
.any(|s| renderer.to_lowercase().contains(s))
{
return Err("Software renderer rejected".into());
}
eprintln!("GPU_RENDERER={renderer}");
cc.egui_ctx.set_pixels_per_point(2.0);
let mut style = (*cc.egui_ctx.style()).clone();
style.visuals = egui::Visuals::dark();
style.visuals.override_text_color = Some(egui::Color32::from_rgb(237, 241, 245));
style.spacing.item_spacing = egui::vec2(12., 12.);
style
.text_styles
.insert(egui::TextStyle::Body, egui::FontId::proportional(18.));
style
.text_styles
.insert(egui::TextStyle::Button, egui::FontId::proportional(18.));
cc.egui_ctx.set_style(style);
Ok(Box::new(Shell {
page: Page::Home,
notes: String::new(),
renderer,
}))
}),
)
}
#[test]
fn clock_wraps_days() {
assert_eq!(clock(0), "00:00");
assert_eq!(clock(86399), "23:59");
assert_eq!(clock(86400), "00:00");
}