From 31b60ae28e6aff6d23b378cd77e288c96c7db148 Mon Sep 17 00:00:00 2001 From: dyknon Date: Sun, 23 Feb 2025 08:33:02 +0900 Subject: abstruction --- src/gtk.rs | 171 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/gtk.rs (limited to 'src/gtk.rs') diff --git a/src/gtk.rs b/src/gtk.rs new file mode 100644 index 0000000..94fb5f1 --- /dev/null +++ b/src/gtk.rs @@ -0,0 +1,171 @@ +use anyhow::{anyhow, Result}; +use gtk4::{self as gtk, glib, cairo}; +use gtk4::prelude::*; +use glib::{clone, spawn_future_local}; +use std::thread; +use std::sync::{Arc, Mutex}; +use crate::sync::Signal; +use std::future::poll_fn; +use std::task::Poll; + +pub struct FbPool{ + size: usize, + pool: Vec, +} +impl FbPool{ + pub fn new(size: usize) -> Self{ + FbPool{ + size, + pool: Vec::with_capacity(size), + } + } + pub fn put(&mut self, buf: cairo::ImageSurfaceDataOwned){ + if self.pool.len() < self.size{ + self.pool.push(buf); + } + } + pub fn get(&mut self, w: usize, h: usize) -> Result{ + while let Some(i) = self.pool.pop(){ + let i = i.into_inner(); + if i.width() as usize == w && i.height() as usize == h{ + return Ok(i); + } + } + Ok(cairo::ImageSurface::create( + cairo::Format::Rgb24, + w.try_into()?, h.try_into()?)?) + } +} +pub trait FbSourceOnce{ + fn get(self, w: usize, h: usize) -> Result; +} +impl FbSourceOnce for &Mutex{ + fn get(self, w: usize, h: usize) -> Result{ + self.lock().map_err(|e| anyhow!("{}", e))?.get(w, h) + } +} + +pub struct Packet{ + pub image: cairo::ImageSurfaceDataOwned, + pub attr: T, +} +pub trait Overray: Send + 'static{} +pub trait Source: Send + 'static{ + type Attr: Overray; + fn next(&mut self, fbpool: impl FbSourceOnce) -> Result>; +} +impl Overray for (){} + +struct AppState{ + next: Mutex>>, + update: Mutex, + abort: Mutex, + fbpool: Mutex, +} + +fn sourcing_loop( + apps: &AppState, + src: &mut impl Source +) -> Result<()>{ + loop{ + let p = src.next(&apps.fbpool)?; + let old = apps.next.lock() + .map_err(|e| anyhow!("{}", e))? + .replace(p); + apps.update.lock().map_err(|e| anyhow!("{}", e))?.wake(); + if let Some(old) = old{ + apps.fbpool.lock() + .map_err(|e| anyhow!("{}", e))? + .put(old.image); + } + } +} + +fn activate(app: >k::Application, apps: Arc>){ + let draw = gtk::DrawingArea::new(); + let mut frame_cache: Option = None; + draw.set_draw_func(clone!{ + #[strong] apps, + move |_draw, ctx, canvas_w, canvas_h|{ + ctx.set_source_rgb(0., 0., 0.); + ctx.paint().unwrap(); + + if let Some(newfb) = apps.next.lock().unwrap().take(){ + if let Some(lastframe) = frame_cache.take(){ + apps.fbpool.lock().unwrap() + .put(lastframe.take_data().unwrap()); + } + frame_cache = Some(newfb.image.into_inner()); + } + if let Some(image) = frame_cache.clone(){ + let ipat = cairo::SurfacePattern::create(&image); + let scale = ((canvas_w as f64) / (image.width() as f64)).min( + (canvas_h as f64) / (image.height() as f64)); + ctx.scale(scale, scale); + ctx.set_source(&ipat).unwrap(); + ctx.paint().unwrap(); + } + } + }); + spawn_future_local(poll_fn(clone!{ + #[strong] apps, + #[strong] draw, + move |ctx|{ + loop{ + match apps.update.lock().unwrap().poll(ctx){ + Poll::Ready(_) => { + draw.queue_draw(); + }, + pending => return pending, + } + } + } + })); + spawn_future_local(poll_fn(clone!{ + #[strong] apps, + #[strong] app, + move |ctx|{ + loop{ + match apps.abort.lock().unwrap().poll(ctx){ + Poll::Ready(_) => { + app.quit(); + }, + pending => return pending, + } + } + } + })); + + let win = gtk::ApplicationWindow::builder() + .application(app) + .child(&draw) + .build(); + win.present(); +} + +pub fn main(src: impl Source + 'static) -> Result{ + let apps = Arc::new(AppState{ + next: Mutex::new(None), + update: Mutex::new(Signal::new()), + abort: Mutex::new(Signal::new()), + fbpool: Mutex::new(FbPool::new(4)), + }); + + thread::spawn(clone!{ + #[strong] apps, + move ||{ + let mut src = src; + let res = sourcing_loop(&apps, &mut src); + apps.abort.lock().unwrap().wake(); + res.unwrap(); + } + }); + + let app = gtk::Application::builder() + .build(); + app.connect_activate(clone!{ + #[strong] apps, + move |app| activate(app, apps.clone()) + }); + Ok(app.run()) +} -- cgit v1.2.3