mod x11; mod keymap; mod layout; mod ui; use eframe::egui; struct XkbPreviewApp { key_data: Option, x11_conn: Option, selected_level: usize, active_level: usize, active_group: usize, error: Option, } impl Default for XkbPreviewApp { fn default() -> Self { Self { key_data: None, x11_conn: None, selected_level: 0, active_level: 0, active_group: 0, error: None, } } } impl eframe::App for XkbPreviewApp { fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { if self.key_data.is_none() && self.error.is_none() { match load_keyboard() { Ok(data) => self.key_data = Some(data), Err(e) => self.error = Some(e), } } if self.x11_conn.is_none() { self.x11_conn = x11::X11Connection::open(); } if let Some(ref conn) = self.x11_conn { let mask = conn.modifier_mask(); self.active_level = keymap::level_from_x11_modmask(mask); self.active_group = conn.active_group(); } ui::render( ui, &self.key_data, &mut self.selected_level, self.active_level, self.active_group, &self.error, ); } } fn load_keyboard() -> Result { let names = x11::get_xkb_rule_names().unwrap_or_default(); keymap::compile_keymap(&names) } fn main() -> eframe::Result { let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default() .with_inner_size([980.0, 560.0]), ..Default::default() }; eframe::run_native( "xkbpreview", options, Box::new(|cc| { setup_fonts(&cc.egui_ctx); Ok(Box::new(XkbPreviewApp::default())) }), ) } fn setup_fonts(ctx: &egui::Context) { let mut fonts = egui::FontDefinitions::default(); let font_candidates = [ "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", "/usr/share/fonts/dejavu/DejaVuSans.ttf", "/usr/share/fonts/truetype/dejavu/DejaVuSans-Regular.ttf", "/usr/share/fonts/dejavu/DejaVuSans-Regular.ttf", "/usr/share/fonts/TTF/DejaVuSans.ttf", "/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf", "/usr/share/fonts/noto/NotoSans-Regular.ttf", "/usr/share/fonts/opentype/noto/NotoSans-Regular.ttf", ]; let mut loaded = false; for path in &font_candidates { if let Ok(data) = std::fs::read(path) { fonts .font_data .insert("fallback_font".into(), egui::FontData::from_owned(data).into()); fonts .families .entry(egui::FontFamily::Proportional) .or_default() .push("fallback_font".into()); loaded = true; break; } } if !loaded { if let Ok(home) = std::env::var("HOME") { let user_candidates = [ format!("{}/.fonts/DejaVuSans.ttf", home), format!("{}/.local/share/fonts/DejaVuSans.ttf", home), format!("{}/.fonts/NotoSans-Regular.ttf", home), format!("{}/.local/share/fonts/NotoSans-Regular.ttf", home), ]; for path in &user_candidates { if let Ok(data) = std::fs::read(path) { fonts .font_data .insert("fallback_font".into(), egui::FontData::from_owned(data).into()); fonts .families .entry(egui::FontFamily::Proportional) .or_default() .push("fallback_font".into()); break; } } } } ctx.set_fonts(fonts); }