cloud storage client
git clone https://git.lucas.co/cce-cloud.git
Migrate from winit to smithay-client-toolkit and calloop
Cargo.toml | 7 +-
src/main.rs | 685 +++++++++++++++++++++++++++++++++++++++++++++---------------
2 files changed, 524 insertions(+), 168 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
index 4eeaea8..256f31c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,7 +5,12 @@ edition = "2021"
[dependencies]
clear-ui = { path = "../clear-ui" }
-winit = "0.30"
+smithay-client-toolkit = "0.19.2"
+calloop = "0.13.0"
+calloop-wayland-source = "0.3.0"
+wayland-client = { version = "0.31", features = ["system"] }
+xkeysym = "0.2"
+raw-window-handle = "0.6"
wgpu = "24"
bytemuck = { version = "1", features = ["derive"] }
pollster = "0.4"
diff --git a/src/main.rs b/src/main.rs
index 03b988c..5983f26 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,14 +1,36 @@
use std::sync::{Arc, Mutex};
use std::io::{self, BufRead, IsTerminal};
-use winit::application::ApplicationHandler;
-use winit::event::{ElementState, WindowEvent};
-use winit::event_loop::{ActiveEventLoop, ControlFlow, EventLoop};
-use winit::window::{Window, WindowAttributes};
-use winit::keyboard::{Key, NamedKey};
-use winit::dpi::{PhysicalSize, LogicalSize, LogicalPosition};
+use std::path::Path;
use clear_ui::widget::{Widget, TextLabel};
+use smithay_client_toolkit::{
+ compositor::{CompositorHandler, CompositorState},
+ delegate_compositor, delegate_keyboard, delegate_pointer, delegate_registry,
+ delegate_seat, delegate_shm, delegate_layer, delegate_output,
+ registry::{ProvidesRegistryState, RegistryState},
+ output::{OutputHandler, OutputState},
+ seat::{
+ keyboard::KeyboardHandler,
+ pointer::PointerHandler,
+ Capability, SeatHandler, SeatState,
+ },
+ shell::{
+ wlr_layer::{
+ Anchor, KeyboardInteractivity, Layer, LayerShell, LayerShellHandler,
+ LayerSurface, LayerSurfaceConfigure,
+ },
+ WaylandSurface,
+ },
+ shm::{Shm, ShmHandler},
+};
+use wayland_client::{
+ globals::registry_queue_init,
+ protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+ Connection, QueueHandle, Proxy,
+};
+use calloop_wayland_source::WaylandSource;
+
use glyphon::{
Attrs, Buffer, Cache, FontSystem, Metrics, Resolution, SwashCache, TextArea, TextAtlas,
TextBounds, TextRenderer, Viewport,
@@ -324,8 +346,8 @@ impl Widget for FuzzelWidget {
labels
}
- fn mouse_input(&mut self, button: winit::event::MouseButton, state: ElementState, px: f32, py: f32) -> bool {
- if button == winit::event::MouseButton::Left && state == ElementState::Pressed {
+ fn mouse_input(&mut self, button: clear_ui::widget::MouseButton, state: clear_ui::widget::ElementState, px: f32, py: f32) -> bool {
+ if button == clear_ui::widget::MouseButton::Left && state == clear_ui::widget::ElementState::Pressed {
let pad = 15.0;
let search_h = 35.0;
let list_y = self.y + pad + search_h + 10.0;
@@ -353,7 +375,8 @@ struct StdinState {
}
struct State {
- window: Arc<Window>,
+ window: LayerSurface,
+ wl_surface: wl_surface::WlSurface,
surface: wgpu::Surface<'static>,
device: wgpu::Device,
queue: wgpu::Queue,
@@ -381,13 +404,38 @@ struct State {
}
impl State {
- async fn new(window: Arc<Window>, prompt: String) -> Self {
- let scale = window.scale_factor();
- let physical_size = window.inner_size();
- let pw = physical_size.width.max(1);
- let ph = physical_size.height.max(1);
- let lw = pw as f32 / scale as f32;
- let lh = ph as f32 / scale as f32;
+ async fn new(
+ conn: &Connection,
+ qh: &QueueHandle<AppState>,
+ compositor_state: &CompositorState,
+ layer_shell_state: &LayerShell,
+ prompt: String,
+ stdin_sender: calloop::channel::Sender<()>,
+ ) -> Self {
+ let scale = 2.0f64;
+ let (width, height) = (600, 400);
+ let pw = (width as f64 * scale) as u32;
+ let ph = (height as f64 * scale) as u32;
+ let lw = width as f32;
+ let lh = height as f32;
+
+ let wl_surface = compositor_state.create_surface(qh);
+ let window = layer_shell_state.create_layer_surface(
+ qh,
+ wl_surface.clone(),
+ Layer::Overlay,
+ Some("clear-cloud".to_string()),
+ None,
+ );
+ window.set_size(width, height);
+ window.set_keyboard_interactivity(KeyboardInteractivity::Exclusive);
+ window.set_anchor(Anchor::empty());
+ wl_surface.commit();
+
+ let wayland_handle = Box::leak(Box::new(clear_ui::wayland::WaylandSurfaceHandle {
+ display_ptr: conn.backend().display_id().as_ptr() as *mut std::ffi::c_void,
+ surface_ptr: wl_surface.id().as_ptr() as *mut std::ffi::c_void,
+ }));
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
backends: wgpu::Backends::VULKAN,
@@ -395,7 +443,7 @@ impl State {
});
let surface = instance
- .create_surface(window.clone())
+ .create_surface(wayland_handle)
.expect("Failed to create surface");
let adapter = instance
@@ -511,7 +559,6 @@ impl State {
if !io::stdin().is_terminal() {
let stdin_state_clone = stdin_state.clone();
- let window_clone = window.clone();
std::thread::spawn(move || {
let stdin = io::stdin();
for line in stdin.lock().lines() {
@@ -520,7 +567,7 @@ impl State {
lock_state.items.push(line);
lock_state.new_data = true;
}
- window_clone.request_redraw();
+ let _ = stdin_sender.send(());
}
}
});
@@ -534,6 +581,7 @@ impl State {
let mut state = Self {
window,
+ wl_surface,
surface,
device,
queue,
@@ -657,33 +705,33 @@ impl State {
.unwrap();
}
- fn resize(&mut self, new_size: PhysicalSize<u32>) {
- if new_size.width > 0 && new_size.height > 0 {
- self.physical_width = new_size.width;
- self.physical_height = new_size.height;
- self.width = new_size.width as f32 / self.scale as f32;
- self.height = new_size.height as f32 / self.scale as f32;
- self.config.width = new_size.width;
- self.config.height = new_size.height;
+ fn resize(&mut self, width: u32, height: u32) {
+ if width > 0 && height > 0 {
+ self.physical_width = width;
+ self.physical_height = height;
+ self.width = width as f32 / self.scale as f32;
+ self.height = height as f32 / self.scale as f32;
+ self.config.width = width;
+ self.config.height = height;
self.surface.configure(&self.device, &self.config);
self.apply_layout();
self.upload_vertices();
}
}
- fn render(&mut self) {
+ fn render(&mut self) -> bool {
self.prepare_text();
let output = match self.surface.get_current_texture() {
Ok(t) => t,
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
self.surface.configure(&self.device, &self.config);
- return;
+ return false;
}
- Err(wgpu::SurfaceError::Timeout) => return,
+ Err(wgpu::SurfaceError::Timeout) => return false,
Err(e) => {
eprintln!("Surface error: {e:?}");
- return;
+ return false;
}
};
@@ -721,165 +769,397 @@ impl State {
}
self.queue.submit(std::iter::once(encoder.finish()));
- self.window.pre_present_notify();
output.present();
+ false
}
}
-struct App {
+struct AppState {
+ registry_state: RegistryState,
+ compositor_state: CompositorState,
+ layer_shell_state: LayerShell,
+ shm_state: Shm,
+ seat_state: SeatState,
+ output_state: OutputState,
+
+ seats: Vec<wl_seat::WlSeat>,
+ pointer: Option<wl_pointer::WlPointer>,
+ keyboard: Option<wl_keyboard::WlKeyboard>,
+
+ window: LayerSurface,
+ surface: wl_surface::WlSurface,
+
state: Option<State>,
- prompt: String,
+ exit: bool,
+ redraw: bool,
}
-impl App {
- fn new(prompt: String) -> Self {
- Self { state: None, prompt }
+impl CompositorHandler for AppState {
+ fn scale_factor_changed(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ scale_factor: i32,
+ ) {
+ if let Some(state) = &mut self.state {
+ state.scale = scale_factor as f64;
+ let pw = (state.width as f64 * state.scale) as u32;
+ let ph = (state.height as f64 * state.scale) as u32;
+ state.resize(pw, ph);
+ self.redraw = true;
+ }
}
+
+ fn transform_changed(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _new_transform: wl_output::Transform,
+ ) {}
+
+ fn frame(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _time: u32,
+ ) {}
+
+ fn surface_enter(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _output: &wl_output::WlOutput,
+ ) {}
+
+ fn surface_leave(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _surface: &wl_surface::WlSurface,
+ _output: &wl_output::WlOutput,
+ ) {}
}
-impl ApplicationHandler for App {
- fn resumed(&mut self, event_loop: &ActiveEventLoop) {
- if self.state.is_some() { return; }
+impl OutputHandler for AppState {
+ fn output_state(&mut self) -> &mut OutputState {
+ &mut self.output_state
+ }
- let monitor = event_loop.primary_monitor().or_else(|| event_loop.available_monitors().next());
- let (width, height) = (600, 400);
- let mut window_attributes = WindowAttributes::default()
- .with_title("clear-cloud")
- .with_decorations(false)
- .with_inner_size(LogicalSize::new(width, height))
- .with_transparent(true);
-
- if let Some(monitor) = monitor {
- let monitor_size = monitor.size();
- let scale_factor = monitor.scale_factor();
- let monitor_w = monitor_size.width as f64 / scale_factor;
- let monitor_h = monitor_size.height as f64 / scale_factor;
- let x = (monitor_w - width as f64) / 2.0;
- let y = (monitor_h - height as f64) / 2.0;
- window_attributes = window_attributes.with_position(LogicalPosition::new(x, y));
- }
+ fn new_output(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _output: wl_output::WlOutput,
+ ) {}
- let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
- let state = pollster::block_on(State::new(window, self.prompt.clone()));
- self.state = Some(state);
- self.state.as_ref().unwrap().window.request_redraw();
+ fn update_output(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _output: wl_output::WlOutput,
+ ) {}
+
+ fn output_destroyed(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _output: wl_output::WlOutput,
+ ) {}
+}
+
+impl SeatHandler for AppState {
+ fn seat_state(&mut self) -> &mut SeatState {
+ &mut self.seat_state
}
- fn window_event(
+ fn new_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
+ self.seats.push(seat);
+ }
+
+ fn new_capability(
&mut self,
- event_loop: &ActiveEventLoop,
- _window_id: winit::window::WindowId,
- event: WindowEvent,
+ _conn: &Connection,
+ qh: &QueueHandle<Self>,
+ seat: wl_seat::WlSeat,
+ capability: Capability,
) {
- if self.state.is_none() { return; }
- let state = self.state.as_mut().unwrap();
+ if capability == Capability::Pointer && self.pointer.is_none() {
+ let pointer = self.seat_state.get_pointer(qh, &seat).unwrap();
+ self.pointer = Some(pointer);
+ }
+ if capability == Capability::Keyboard && self.keyboard.is_none() {
+ let keyboard = self
+ .seat_state
+ .get_keyboard(qh, &seat, None)
+ .unwrap();
+ self.keyboard = Some(keyboard);
+ }
+ }
- let needs_redraw = match event {
- WindowEvent::CloseRequested => {
- event_loop.exit();
- true
- }
- WindowEvent::Resized(size) => {
- state.resize(size);
- true
- }
- WindowEvent::RedrawRequested => {
- if state.check_stdin_updates() {
- state.apply_layout();
- state.upload_vertices();
- }
- state.render();
- false
- }
- WindowEvent::ScaleFactorChanged { scale_factor, mut inner_size_writer } => {
- let new_physical = winit::dpi::PhysicalSize::new(
- (state.width as f64 * scale_factor) as u32,
- (state.height as f64 * scale_factor) as u32,
- );
- let _ = inner_size_writer.request_inner_size(new_physical);
- state.scale = scale_factor;
- state.resize(new_physical);
- true
- }
- WindowEvent::CursorMoved { position, .. } => {
- state.cursor_x = position.x as f32 / state.scale as f32;
- state.cursor_y = position.y as f32 / state.scale as f32;
- false
- }
- WindowEvent::MouseInput { state: btn_state, button, .. } => {
- if button == winit::event::MouseButton::Left && btn_state == ElementState::Pressed {
- let prev_selected = state.fuzzel.selected;
- let changed = state.fuzzel.mouse_input(button, btn_state, state.cursor_x, state.cursor_y);
- if changed {
- if state.fuzzel.selected == prev_selected {
- if let Some(item) = state.fuzzel.filtered_items.get(state.fuzzel.selected) {
- println!("{}", item);
- std::process::exit(0);
- }
- }
- state.upload_vertices();
- true
- } else {
- false
+ fn remove_capability(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _seat: wl_seat::WlSeat,
+ capability: Capability,
+ ) {
+ if capability == Capability::Pointer {
+ self.pointer = None;
+ }
+ if capability == Capability::Keyboard {
+ self.keyboard = None;
+ }
+ }
+
+ fn remove_seat(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, seat: wl_seat::WlSeat) {
+ self.seats.retain(|s| s != &seat);
+ }
+}
+
+impl ShmHandler for AppState {
+ fn shm_state(&mut self) -> &mut Shm {
+ &mut self.shm_state
+ }
+}
+
+impl PointerHandler for AppState {
+ fn pointer_frame(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _pointer: &wl_pointer::WlPointer,
+ events: &[smithay_client_toolkit::seat::pointer::PointerEvent],
+ ) {
+ use smithay_client_toolkit::seat::pointer::PointerEventKind;
+ for event in events {
+ let (x, y) = event.position;
+ if let Some(st) = &mut self.state {
+ let scale = st.scale;
+ let cx = (x * scale) as f32;
+ let cy = (y * scale) as f32;
+ match &event.kind {
+ PointerEventKind::Motion { .. } => {
+ st.cursor_x = cx;
+ st.cursor_y = cy;
}
- } else {
- false
- }
- }
- WindowEvent::KeyboardInput { event, .. } => {
- if event.state == ElementState::Pressed {
- let mut handled = true;
- match &event.logical_key {
- Key::Named(NamedKey::Escape) => {
- std::process::exit(0);
- }
- Key::Named(NamedKey::Enter) => {
- if let Some(item) = state.fuzzel.filtered_items.get(state.fuzzel.selected) {
- println!("{}", item);
- std::process::exit(0);
- }
- }
- Key::Named(NamedKey::ArrowDown) => {
- if !state.fuzzel.filtered_items.is_empty() {
- state.fuzzel.selected = (state.fuzzel.selected + 1).min(state.fuzzel.filtered_items.len() - 1);
- state.fuzzel.update_scroll();
- state.upload_vertices();
- }
- }
- Key::Named(NamedKey::ArrowUp) => {
- if state.fuzzel.selected > 0 {
- state.fuzzel.selected -= 1;
- state.fuzzel.update_scroll();
- state.upload_vertices();
- }
- }
- Key::Named(NamedKey::Backspace) => {
- state.fuzzel.query.pop();
- state.fuzzel.filter();
- state.upload_vertices();
- }
- _ => {
- if let Some(text) = &event.text {
- for ch in text.chars().filter(|c| !c.is_control()) {
- state.fuzzel.query.push(ch);
+ PointerEventKind::Press { button, .. } => {
+ if *button == 272 {
+ st.cursor_x = cx;
+ st.cursor_y = cy;
+ let prev_selected = st.fuzzel.selected;
+ let changed = st.fuzzel.mouse_input(
+ clear_ui::widget::MouseButton::Left,
+ clear_ui::widget::ElementState::Pressed,
+ st.cursor_x,
+ st.cursor_y,
+ );
+ if changed {
+ if st.fuzzel.selected == prev_selected {
+ if let Some(item) = st.fuzzel.filtered_items.get(st.fuzzel.selected) {
+ println!("{}", item);
+ std::process::exit(0);
+ }
}
- state.fuzzel.filter();
- state.upload_vertices();
- } else {
- handled = false;
+ st.upload_vertices();
+ self.redraw = true;
}
}
}
- handled
+ _ => {}
+ }
+ }
+ }
+ }
+}
+
+impl KeyboardHandler for AppState {
+ fn enter(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _surface: &wl_surface::WlSurface,
+ _serial: u32,
+ _raw_modifiers: &[u32],
+ _keysyms: &[xkeysym::Keysym],
+ ) {}
+
+ fn leave(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _surface: &wl_surface::WlSurface,
+ _serial: u32,
+ ) {}
+
+ fn press_key(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _serial: u32,
+ event: smithay_client_toolkit::seat::keyboard::KeyEvent,
+ ) {
+ self.handle_key(event, clear_ui::widget::ElementState::Pressed);
+ }
+
+ fn release_key(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _serial: u32,
+ event: smithay_client_toolkit::seat::keyboard::KeyEvent,
+ ) {
+ self.handle_key(event, clear_ui::widget::ElementState::Released);
+ }
+
+ fn update_modifiers(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _keyboard: &wl_keyboard::WlKeyboard,
+ _serial: u32,
+ _modifiers: smithay_client_toolkit::seat::keyboard::Modifiers,
+ _layout: u32,
+ ) {}
+}
+
+impl LayerShellHandler for AppState {
+ fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _layer: &LayerSurface) {
+ self.exit = true;
+ }
+
+ fn configure(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _layer: &LayerSurface,
+ configure: LayerSurfaceConfigure,
+ _serial: u32,
+ ) {
+ let (width, height) = configure.new_size;
+ if let Some(state) = &mut self.state {
+ state.resize(width, height);
+ }
+ self.redraw = true;
+ }
+}
+
+impl ProvidesRegistryState for AppState {
+ fn registry(&mut self) -> &mut RegistryState {
+ &mut self.registry_state
+ }
+
+ fn runtime_add_global(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _name: u32,
+ _interface: &str,
+ _version: u32,
+ ) {}
+
+ fn runtime_remove_global(
+ &mut self,
+ _conn: &Connection,
+ _qh: &QueueHandle<Self>,
+ _name: u32,
+ _interface: &str,
+ ) {}
+}
+
+delegate_compositor!(AppState);
+delegate_layer!(AppState);
+delegate_shm!(AppState);
+delegate_seat!(AppState);
+delegate_pointer!(AppState);
+delegate_keyboard!(AppState);
+delegate_registry!(AppState);
+delegate_output!(AppState);
+
+impl AppState {
+ fn handle_key(&mut self, event: smithay_client_toolkit::seat::keyboard::KeyEvent, state: clear_ui::widget::ElementState) {
+ use clear_ui::widget::{Key, NamedKey};
+ if state != clear_ui::widget::ElementState::Pressed {
+ return;
+ }
+
+ let logical_key = match event.keysym {
+ xkeysym::Keysym::Escape => Key::Named(NamedKey::Escape),
+ xkeysym::Keysym::Return => Key::Named(NamedKey::Enter),
+ xkeysym::Keysym::BackSpace => Key::Named(NamedKey::Backspace),
+ xkeysym::Keysym::Down => Key::Named(NamedKey::ArrowDown),
+ xkeysym::Keysym::Up => Key::Named(NamedKey::ArrowUp),
+ xkeysym::Keysym::Left => Key::Named(NamedKey::ArrowLeft),
+ xkeysym::Keysym::Right => Key::Named(NamedKey::ArrowRight),
+ xkeysym::Keysym::Tab => Key::Named(NamedKey::Tab),
+ xkeysym::Keysym::Delete => Key::Named(NamedKey::Delete),
+ xkeysym::Keysym::space => Key::Named(NamedKey::Space),
+ _ => {
+ if let Some(ref text) = event.utf8 {
+ Key::Character(text.clone())
} else {
- false
+ return;
}
}
- _ => false,
};
- if needs_redraw {
- state.window.request_redraw();
+ if let Some(st) = &mut self.state {
+ let mut handled = true;
+ match &logical_key {
+ Key::Named(NamedKey::Escape) => {
+ std::process::exit(0);
+ }
+ Key::Named(NamedKey::Enter) => {
+ if let Some(item) = st.fuzzel.filtered_items.get(st.fuzzel.selected) {
+ println!("{}", item);
+ std::process::exit(0);
+ }
+ }
+ Key::Named(NamedKey::ArrowDown) => {
+ if !st.fuzzel.filtered_items.is_empty() {
+ st.fuzzel.selected = (st.fuzzel.selected + 1).min(st.fuzzel.filtered_items.len() - 1);
+ st.fuzzel.update_scroll();
+ st.upload_vertices();
+ self.redraw = true;
+ }
+ }
+ Key::Named(NamedKey::ArrowUp) => {
+ if st.fuzzel.selected > 0 {
+ st.fuzzel.selected -= 1;
+ st.fuzzel.update_scroll();
+ st.upload_vertices();
+ self.redraw = true;
+ }
+ }
+ Key::Named(NamedKey::Backspace) => {
+ st.fuzzel.query.pop();
+ st.fuzzel.filter();
+ st.upload_vertices();
+ self.redraw = true;
+ }
+ _ => {
+ if let Some(text) = &event.utf8 {
+ for ch in text.chars().filter(|c| !c.is_control()) {
+ st.fuzzel.query.push(ch);
+ }
+ st.fuzzel.filter();
+ st.upload_vertices();
+ self.redraw = true;
+ } else {
+ handled = false;
+ }
+ }
+ }
+ if handled {
+ self.redraw = true;
+ }
}
}
}
@@ -895,9 +1175,80 @@ fn main() {
}
}
- let event_loop = EventLoop::new().unwrap();
- event_loop.set_control_flow(ControlFlow::Wait);
+ let conn = Connection::connect_to_env().unwrap();
+ let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
+ let qh = event_queue.handle();
+
+ let compositor_state = CompositorState::bind(&globals, &qh).unwrap();
+ let layer_shell_state = LayerShell::bind(&globals, &qh).unwrap();
+ let shm_state = Shm::bind(&globals, &qh).unwrap();
+ let seat_state = SeatState::new(&globals, &qh);
+ let output_state = OutputState::new(&globals, &qh);
+
+ let (stdin_sender, stdin_channel) = calloop::channel::channel::<()>();
+
+ let state = pollster::block_on(State::new(
+ &conn,
+ &qh,
+ &compositor_state,
+ &layer_shell_state,
+ prompt,
+ stdin_sender,
+ ));
+
+ let mut app = AppState {
+ registry_state: RegistryState::new(&globals),
+ compositor_state,
+ layer_shell_state,
+ shm_state,
+ seat_state,
+ output_state,
+ seats: Vec::new(),
+ pointer: None,
+ keyboard: None,
+ window: state.window.clone(),
+ surface: state.wl_surface.clone(),
+ state: Some(state),
+ exit: false,
+ redraw: true,
+ };
+
+ let mut event_loop = calloop::EventLoop::try_new().unwrap();
+ let loop_handle = event_loop.handle();
+
+ WaylandSource::new(conn, event_queue).insert(loop_handle.clone()).unwrap();
+
+ loop_handle.insert_source(stdin_channel, |event, _metadata, app_state: &mut AppState| {
+ if let calloop::channel::Event::Msg(()) = event {
+ if let Some(st) = &mut app_state.state {
+ if st.check_stdin_updates() {
+ st.apply_layout();
+ st.upload_vertices();
+ app_state.redraw = true;
+ }
+ }
+ }
+ }).unwrap();
+
+ loop {
+ let timeout = if app.redraw {
+ std::time::Duration::from_millis(0)
+ } else {
+ std::time::Duration::from_millis(16)
+ };
+ event_loop.dispatch(timeout, &mut app).unwrap();
+
+ if app.exit {
+ break;
+ }
- let mut app = App::new(prompt);
- event_loop.run_app(&mut app).unwrap();
+ if app.redraw {
+ app.redraw = false;
+ if let Some(state) = &mut app.state {
+ if state.render() {
+ app.redraw = true;
+ }
+ }
+ }
+ }
}