mouse control with buttons

This commit is contained in:
2025-08-06 20:30:05 +02:00
parent ecb12842f4
commit b7d40b7c90

View File

@@ -1,11 +1,11 @@
use eframe::UserEvent;
use eframe::egui; use eframe::egui;
use egui::{FontData, FontDefinitions, FontFamily};
use egui::{Key, RichText}; use egui::{Key, RichText};
use std::collections::VecDeque; use std::collections::VecDeque;
use egui::{FontData, FontDefinitions, FontFamily};
use std::sync::{Arc, Mutex};
use winit::event_loop::{EventLoop};
use eframe::UserEvent;
use std::env; use std::env;
use std::sync::{Arc, Mutex};
use winit::event_loop::EventLoop;
const MAX_KEYS: usize = 6; const MAX_KEYS: usize = 6;
const SECRET_COMBO: [Key; MAX_KEYS] = [ const SECRET_COMBO: [Key; MAX_KEYS] = [
@@ -17,7 +17,6 @@ const SECRET_COMBO: [Key; MAX_KEYS] = [
Key::ArrowRight, Key::ArrowRight,
]; ];
struct AdminGateApp { struct AdminGateApp {
key_history: VecDeque<Key>, key_history: VecDeque<Key>,
should_run_admin_command: Arc<Mutex<bool>>, should_run_admin_command: Arc<Mutex<bool>>,
@@ -32,7 +31,6 @@ impl Default for AdminGateApp {
} }
} }
impl eframe::App for AdminGateApp { impl eframe::App for AdminGateApp {
fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
@@ -55,20 +53,81 @@ impl eframe::App for AdminGateApp {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(" "); .join(" ");
ui.label( ui.label(RichText::new(&key_display).size(60.0).strong());
RichText::new(&key_display)
.size(60.0)
.strong(),
);
ui.add_space(20.0); ui.add_space(20.0);
ui.horizontal_top(|ui| {
ui.add_space(30.0);
egui::Grid::new("arrow_grid")
.spacing([20.0, 20.0])
.striped(false)
.min_col_width(80.0)
.show(ui, |ui| {
// Row 1
if ui.add_sized([80.0, 80.0], egui::Button::new("X")).clicked() {
std::process::exit(0);
}
if ui.add_sized([80.0, 80.0], egui::Button::new("")).clicked() {
if self.key_history.len() == MAX_KEYS {
self.key_history.pop_front();
}
self.key_history.push_back(Key::ArrowUp);
}
ui.label(""); // empty cell
ui.end_row();
// Row 2
if ui.add_sized([80.0, 80.0], egui::Button::new("")).clicked() {
if self.key_history.len() == MAX_KEYS {
self.key_history.pop_front();
}
self.key_history.push_back(Key::ArrowLeft);
}
if ui.add_sized([80.0, 80.0], egui::Button::new("")).clicked() {
if self.key_history.len() == MAX_KEYS
&& self
.key_history
.iter()
.copied()
.eq(SECRET_COMBO.iter().copied())
{
*self.should_run_admin_command.lock().unwrap() = true;
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
}
}
if ui.add_sized([80.0, 80.0], egui::Button::new("")).clicked() {
if self.key_history.len() == MAX_KEYS {
self.key_history.pop_front();
}
self.key_history.push_back(Key::ArrowRight);
}
ui.end_row();
// Row 3
ui.label(""); // empty cell
if ui.add_sized([80.0, 80.0], egui::Button::new("")).clicked() {
if self.key_history.len() == MAX_KEYS {
self.key_history.pop_front();
}
self.key_history.push_back(Key::ArrowDown);
}
ui.label(""); // empty cell
ui.end_row();
});
});
}); });
}); });
egui::TopBottomPanel::bottom("footer").show(ctx, |ui| { egui::TopBottomPanel::bottom("footer").show(ctx, |ui| {
ui.horizontal_centered(|ui| { ui.horizontal_centered(|ui| {
ui.label(egui::RichText::new("Press ESC to quit. Enter runs the admin command.").size(24.0).weak()); ui.label(
egui::RichText::new("Press ESC to quit. Enter runs the admin command.")
.size(24.0)
.weak(),
);
}); });
}); });
@@ -78,7 +137,7 @@ impl eframe::App for AdminGateApp {
key, key,
pressed: true, pressed: true,
modifiers: _, modifiers: _,
physical_key : _, physical_key: _,
repeat: _, repeat: _,
} = event } = event
{ {
@@ -116,7 +175,6 @@ impl eframe::App for AdminGateApp {
} }
} }
fn setup_custom_fonts(ctx: &egui::Context) { fn setup_custom_fonts(ctx: &egui::Context) {
let mut fonts = FontDefinitions::default(); let mut fonts = FontDefinitions::default();
@@ -124,10 +182,9 @@ fn setup_custom_fonts(ctx: &egui::Context) {
fonts.font_data.insert( fonts.font_data.insert(
"custom".to_string(), "custom".to_string(),
FontData::from_owned( FontData::from_owned(
std::fs::read("/usr/share/fonts/TTF/DejaVuSans.ttf").expect("Failed to load font"), std::fs::read("/usr/share/fonts/TTF/DejaVuSans.ttf").expect("Failed to load font"),
).into(), )
.into(),
); );
// Use the custom font for all proportional and monospace text // Use the custom font for all proportional and monospace text
@@ -146,11 +203,8 @@ fn setup_custom_fonts(ctx: &egui::Context) {
ctx.set_fonts(fonts); ctx.set_fonts(fonts);
} }
fn main() {
let mut args = env::args().skip(1); // Skip program name
fn main() {
let mut args = env::args().skip(1); // Skip program name
let admin_command = match args.next() { let admin_command = match args.next() {
Some(cmd) => cmd, Some(cmd) => cmd,
@@ -162,37 +216,38 @@ fn main() {
let admin_args: Vec<String> = args.collect(); let admin_args: Vec<String> = args.collect();
let should_run_admin_command = Arc::new(Mutex::new(false)); let should_run_admin_command = Arc::new(Mutex::new(false));
let shared_flag = should_run_admin_command.clone(); let shared_flag = should_run_admin_command.clone();
let app = AdminGateApp { let app = AdminGateApp {
key_history: VecDeque::with_capacity(6), key_history: VecDeque::with_capacity(6),
should_run_admin_command: shared_flag, should_run_admin_command: shared_flag,
}; };
{ {
let options = eframe::NativeOptions::default(); let options = eframe::NativeOptions::default();
let eventloop = EventLoop::<UserEvent>::with_user_event().build().unwrap(); let eventloop = EventLoop::<UserEvent>::with_user_event().build().unwrap();
let mut winit_app = eframe::create_native("Admin Gate", options, Box::new(|cc| { let mut winit_app = eframe::create_native(
// Setup fonts "Admin Gate",
setup_custom_fonts(&cc.egui_ctx); options,
Ok(Box::new(app)) Box::new(|cc| {
}), &eventloop); // Setup fonts
setup_custom_fonts(&cc.egui_ctx);
Ok(Box::new(app))
eventloop.run_app(&mut winit_app).unwrap(); }),
&eventloop,
);
eventloop.run_app(&mut winit_app).unwrap();
} }
{ {
// After GUI closes // After GUI closes
if *should_run_admin_command.lock().unwrap() { if *should_run_admin_command.lock().unwrap() {
println!("✅ Running admin command..."); println!("✅ Running admin command...");
match std::process::Command::new(admin_command) match std::process::Command::new(admin_command)
.args(admin_args) .args(admin_args)
.status() // ← wait until it finishes .status() // ← wait until it finishes
{ {
@@ -203,8 +258,6 @@ fn main() {
eprintln!("❌ Failed to run admin command: {}", e); eprintln!("❌ Failed to run admin command: {}", e);
} }
} }
}
} }
} }
}