feat(gui): scaffold egui window behind the gui feature
Adds the opt-in graphical front-end (pixelpass --gui), default-off via the `gui` cargo feature so the headless build never pulls the toolkit tree. eframe 0.34 on the glow/OpenGL backend (no wgpu); 69 feature-gated crates, vetted. --gui on a headless build errors with a rebuild hint. This commit is just the shell: a window with a Host/View menu and back navigation. The shell-out child-spawning + JSON event parsing that drives real host/viewer controls come next. Window verified to open and render cleanly on Wayland (glow). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+106
@@ -0,0 +1,106 @@
|
||||
//! Graphical front-end (`pixelpass --gui`), compiled only with the `gui`
|
||||
//! feature.
|
||||
//!
|
||||
//! Architecture: this window is a thin **shell-out** driver. It never touches
|
||||
//! the capture / portal / gst / iroh machinery directly — instead it re-execs
|
||||
//! this same binary in headless mode (`pixelpass --host --output json …` or
|
||||
//! `pixelpass <ticket> --output json`) as a child process and parses the
|
||||
//! child's JSON event stream (see [`crate::common::output`]) to drive what it
|
||||
//! shows. That keeps the fragile capture stack sealed in a separate process:
|
||||
//! the GUI can be closed or crash without taking a live stream down.
|
||||
|
||||
use eframe::egui;
|
||||
|
||||
/// Launch the GUI event loop. Blocks until the window is closed. Runs on the
|
||||
/// main thread (a winit requirement), which is where `main` calls it from.
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
let options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([520.0, 420.0])
|
||||
.with_min_inner_size([420.0, 320.0])
|
||||
.with_title("PixelPass"),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
eframe::run_native(
|
||||
"PixelPass",
|
||||
options,
|
||||
Box::new(|_cc| Ok(Box::new(PixelPassApp::default()))),
|
||||
)
|
||||
.map_err(|e| anyhow::anyhow!("GUI failed to start: {e}"))
|
||||
}
|
||||
|
||||
/// Which screen the single window is currently showing.
|
||||
#[derive(Default, PartialEq)]
|
||||
enum Screen {
|
||||
#[default]
|
||||
Menu,
|
||||
Host,
|
||||
Viewer,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PixelPassApp {
|
||||
screen: Screen,
|
||||
}
|
||||
|
||||
impl eframe::App for PixelPassApp {
|
||||
// eframe 0.34 hands us the central-panel `ui` directly (it wraps this in a
|
||||
// CentralPanel for us).
|
||||
fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
|
||||
match self.screen {
|
||||
Screen::Menu => self.menu(ui),
|
||||
Screen::Host => self.host(ui),
|
||||
Screen::Viewer => self.viewer(ui),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelPassApp {
|
||||
fn menu(&mut self, ui: &mut egui::Ui) {
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.add_space(24.0);
|
||||
ui.heading("PixelPass");
|
||||
ui.label("P2P screen sharing");
|
||||
ui.add_space(32.0);
|
||||
if ui
|
||||
.add_sized([240.0, 40.0], egui::Button::new("Host — share my screen"))
|
||||
.clicked()
|
||||
{
|
||||
self.screen = Screen::Host;
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
if ui
|
||||
.add_sized(
|
||||
[240.0, 40.0],
|
||||
egui::Button::new("View — watch someone's screen"),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
self.screen = Screen::Viewer;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn host(&mut self, ui: &mut egui::Ui) {
|
||||
self.back_bar(ui, "Host");
|
||||
ui.separator();
|
||||
ui.label("Host controls land in the next step.");
|
||||
}
|
||||
|
||||
fn viewer(&mut self, ui: &mut egui::Ui) {
|
||||
self.back_bar(ui, "View");
|
||||
ui.separator();
|
||||
ui.label("Viewer controls land in a later step.");
|
||||
}
|
||||
|
||||
/// Title row with a back-to-menu button, shared by the host/viewer screens.
|
||||
fn back_bar(&mut self, ui: &mut egui::Ui, title: &str) {
|
||||
ui.horizontal(|ui| {
|
||||
if ui.button("← Menu").clicked() {
|
||||
self.screen = Screen::Menu;
|
||||
}
|
||||
ui.heading(title);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user