ltk_webkit/wpe/
main_loop.rsuse std::ffi::{ c_int, c_uint, c_void };
use std::sync::{ Arc, Condvar, Mutex, OnceLock };
use std::thread;
use glib_sys::*;
const EFD_NONBLOCK: c_int = 0o4000;
const EFD_CLOEXEC: c_int = 0o2000000;
extern "C"
{
fn eventfd( initval: c_uint, flags: c_int ) -> c_int;
fn read( fd: c_int, buf: *mut c_void, count: usize ) -> isize;
fn write( fd: c_int, buf: *const c_void, count: usize ) -> isize;
}
struct Arm
{
fds: Vec<GPollFD>,
timeout: c_int,
}
struct Watch
{
next: Mutex<Option<Arm>>,
armed: Condvar,
interrupt: c_int,
}
static WATCH: OnceLock<Arc<Watch>> = OnceLock::new();
pub fn wake_on_events( wake: impl Fn() + Send + 'static )
{
let interrupt = unsafe { eventfd( 0, EFD_NONBLOCK | EFD_CLOEXEC ) };
if interrupt < 0 { return; }
let watch = Arc::new( Watch { next: Mutex::new( None ), armed: Condvar::new(), interrupt } );
if WATCH.set( Arc::clone( &watch ) ).is_err() { return; }
unsafe { g_main_context_acquire( g_main_context_default() ); }
thread::Builder::new()
.name( "ltk-webkit-watch".into() )
.spawn( move || watch_loop( &watch, wake ) )
.ok();
rearm();
}
pub( crate ) fn rearm()
{
let Some( watch ) = WATCH.get() else { return };
let empty = GPollFD { fd: 0, events: 0, revents: 0 };
let mut fds = vec![ empty; 16 ];
let mut timeout = -1;
unsafe
{
let context = g_main_context_default();
let mut priority = 0;
let ready = g_main_context_prepare( context, &mut priority ) != 0;
loop
{
let needed = g_main_context_query( context, priority, &mut timeout, fds.as_mut_ptr(), fds.len() as c_int ).max( 0 ) as usize;
if needed <= fds.len()
{
fds.truncate( needed );
break;
}
fds.resize( needed, empty );
}
if ready { timeout = 0; }
}
if let Ok( mut next ) = watch.next.lock()
{
*next = Some( Arm { fds, timeout } );
watch.armed.notify_one();
}
let one: u64 = 1;
unsafe { write( watch.interrupt, &one as *const u64 as *const c_void, 8 ); }
}
fn watch_loop( watch: &Watch, wake: impl Fn() )
{
let mut current: Option<Arm> = None;
loop
{
{
let Ok( mut next ) = watch.next.lock() else { return };
while next.is_none() && current.is_none()
{
next = match watch.armed.wait( next ) { Ok( n ) => n, Err( _ ) => return };
}
if let Some( arm ) = next.take() { current = Some( arm ); }
}
let Some( arm ) = current.as_mut() else { continue };
let mut fds = arm.fds.clone();
fds.push( GPollFD { fd: watch.interrupt, events: G_IO_IN as u16, revents: 0 } );
let ready = unsafe { g_poll( fds.as_mut_ptr(), fds.len() as c_uint, arm.timeout ) };
if ready < 0 { continue; }
if fds.last().is_some_and( |f| f.revents != 0 )
{
let mut count: u64 = 0;
unsafe { read( watch.interrupt, &mut count as *mut u64 as *mut c_void, 8 ); }
continue;
}
current = None;
wake();
}
}