Skip to content

Instantly share code, notes, and snippets.

@kayabaNerve
Created May 22, 2021 11:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kayabaNerve/607992beb81f918d9362d8a470fdbddf to your computer and use it in GitHub Desktop.
Save kayabaNerve/607992beb81f918d9362d8a470fdbddf to your computer and use it in GitHub Desktop.
Linux-only mc_wry which allows handling the display server being accessible.
extern crate libc;
use std::{
ptr,
mem,
os::raw::c_char,
ffi::{CStr, CString}
};
use serde_json;
use gtk;
use wry::{
application::{
event::Event,
event_loop::{ControlFlow, EventLoop},
window::{WindowBuilder, Window},
},
webview::{self, WebViewBuilder, WebView}
};
pub struct Bundle {
event_loop: EventLoop<()>,
// Stored so the Window doesn't close immediately; allows closing later via dropping this
#[allow(dead_code)]
webview: WebView
}
#[repr(C)]
pub struct BundleOuter {
valid: bool,
bundle: Box<Bundle>
}
#[repr(C)]
pub struct RpcRequest {
rpc_method: *const c_char,
params: *const c_char
}
fn create_bundle<F: Fn(&Window, webview::RpcRequest) -> Option<webview::RpcResponse> + 'static>(
title: &str,
url: &str,
handler: F
) -> wry::Result<Bundle> {
//Make sure WebView is available.
webview::webview_version()?;
match gtk::init() {
Err(e) => Err(wry::Error::GlibBoolError(e)),
Ok(_) => {
let event_loop: EventLoop<()> = EventLoop::new();
let window: Window = WindowBuilder::new()
.with_title(title)
.build(&event_loop)?;
let webview: WebView = WebViewBuilder::new(window)?
.with_file_drop_handler(|_, _| true)
.with_rpc_handler(handler)
.with_url(url)?
.build()?;
Ok(
Bundle {
event_loop,
webview
}
)
}
}
}
#[no_mangle]
pub unsafe extern "C" fn new_webview(
title: *const c_char,
url: *const c_char,
handler: extern "C" fn(
request: RpcRequest
) -> *const c_char
) -> BundleOuter {
match create_bundle(
CStr::from_ptr(title).to_str().unwrap(),
CStr::from_ptr(url).to_str().unwrap(),
move |_, request| {
let method: CString = CString::new(request.method.as_str()).unwrap();
let params: CString = CString::new(request.params.unwrap().to_string()).unwrap();
Some(
webview::RpcResponse::new_result(
request.id,
Some(
serde_json::from_str(
CStr::from_ptr(
handler(
RpcRequest {
rpc_method: method.as_ptr(),
params: params.as_ptr()
}
)
).to_str().unwrap()
).unwrap()
)
)
)
}
) {
Ok(bundle) => BundleOuter {
valid: true,
bundle: Box::new(bundle)
},
Err(_) => BundleOuter {
valid: false,
bundle: Box::<Bundle>::from_raw(1 as *mut Bundle)
}
}
}
fn run(
event_loop: EventLoop<()>
) -> ! {
event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;
match event {
Event::UserEvent(_) => *control_flow = ControlFlow::Exit,
_ => {}
}
})
}
#[no_mangle]
pub unsafe extern "C" fn webview_run(
bundle: BundleOuter
) {
run(bundle.bundle.event_loop);
}
/*
fn terminate(
proxy: EventLoopProxy<()>
) -> ! {
// This SHOULD properly close the event loop and that will call exit on its own
// That said, Rust panics when we do this, claiming it hit unreachable code
// A pure Rust version of this model (foreign thread sent event to trigger quit) worked without issue though
// So there's some FFI issue. Because of that, terminate solely closes the window (see below), leaving the event loop running
proxy.send_event(()).unwrap();
}
*/
#[no_mangle]
pub unsafe extern "C" fn webview_terminate(
bundle: BundleOuter
) {
mem::drop(bundle.bundle);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment