use ::{_API};
use capi::scdef::SCITER_RT_OPTIONS;
use capi::sctypes::*;
use capi::screquest::HREQUEST;
use capi::schandler::NativeHandler;
use dom::{self, event::EventHandler};
use eventhandler::*;
use value::{Value};
pub use capi::scdef::{LOAD_RESULT, OUTPUT_SUBSYTEMS, OUTPUT_SEVERITY};
pub use capi::scdef::{SCN_LOAD_DATA, SCN_DATA_LOADED, SCN_ATTACH_BEHAVIOR, SCN_INVALIDATE_RECT};
pub type Result<T> = ::std::result::Result<T, ()>;
macro_rules! ok_or {
($ok:ident) => {
if $ok != 0 {
Ok(())
} else {
Err(())
}
};
($ok:ident, $rv:expr) => {
if $ok != 0 {
Ok($rv)
} else {
Err(())
}
};
($ok:ident, $rv:expr, $err:expr) => {
if $ok != 0 {
Ok($rv)
} else {
Err($err)
}
};
}
#[allow(unused_variables)]
pub trait HostHandler {
fn on_data_load(&mut self, pnm: &mut SCN_LOAD_DATA) -> Option<LOAD_RESULT> { return None; }
fn on_data_loaded(&mut self, pnm: &SCN_DATA_LOADED) { }
fn on_attach_behavior(&mut self, pnm: &mut SCN_ATTACH_BEHAVIOR) -> bool { return false; }
fn on_engine_destroyed(&mut self) { }
fn on_graphics_critical_failure(&mut self) { }
fn on_invalidate(&mut self, pnm: &SCN_INVALIDATE_RECT) {}
fn on_debug_output(&mut self, subsystem: OUTPUT_SUBSYTEMS, severity: OUTPUT_SEVERITY, message: &str) {
if !message.is_empty() {
if severity == OUTPUT_SEVERITY::INFO {
println!("{:?}:{:?}: {}", severity, subsystem, message);
} else {
eprintln!("{:?}:{:?}: {}", severity, subsystem, message);
}
}
}
fn data_ready(&self, hwnd: HWINDOW, uri: &str, data: &[u8], request_id: Option<HREQUEST>) {
let s = s2w!(uri);
match request_id {
Some(req) => {
(_API.SciterDataReadyAsync)(hwnd, s.as_ptr(), data.as_ptr(), data.len() as UINT, req)
},
None => {
(_API.SciterDataReady)(hwnd, s.as_ptr(), data.as_ptr(), data.len() as UINT)
},
};
}
fn attach_behavior<Handler: EventHandler>(&self, pnm: &mut SCN_ATTACH_BEHAVIOR, handler: Handler) {
let boxed = Box::new(handler);
let ptr = Box::into_raw(boxed); pnm.elementProc = ::eventhandler::_event_handler_proc::<Handler>;
pnm.elementTag = ptr as LPVOID;
}
}
#[derive(Default)]
struct DefaultHandler;
impl HostHandler for DefaultHandler {
}
use std::rc::Rc;
use std::cell::RefCell;
type BehaviorList = Vec<(String, Box<dyn Fn() -> Box<dyn EventHandler>>)>;
type SharedBehaviorList = Rc<RefCell<BehaviorList>>;
type SharedArchive = Rc<RefCell<Option<Archive>>>;
#[repr(C)]
struct HostCallback<Callback> {
sig: u32,
behaviors: SharedBehaviorList,
handler: Callback,
archive: SharedArchive,
}
pub struct Host {
hwnd: HWINDOW,
behaviors: SharedBehaviorList,
handler: RefCell<NativeHandler>,
archive: SharedArchive,
}
impl Host {
pub fn attach(hwnd: HWINDOW) -> Host {
let host = Host {
hwnd,
behaviors: Default::default(),
handler: Default::default(),
archive: Default::default(),
};
host.setup_callback(DefaultHandler::default());
return host;
}
pub fn attach_with<Handler: HostHandler>(hwnd: HWINDOW, handler: Handler) -> Host {
let host = Host {
hwnd,
behaviors: Default::default(),
handler: Default::default(),
archive: Default::default(),
};
host.setup_callback(handler);
return host;
}
pub fn event_handler<Handler: EventHandler>(&self, handler: Handler) {
self.attach_handler(handler)
}
#[doc(hidden)]
pub fn attach_handler<Handler: EventHandler>(&self, handler: Handler) {
let hwnd = self.get_hwnd();
let boxed = Box::new( WindowHandler { hwnd, handler } );
let ptr = Box::into_raw(boxed);
let func = _event_handler_window_proc::<Handler>;
let flags = dom::event::default_events();
(_API.SciterWindowAttachEventHandler)(hwnd, func, ptr as LPVOID, flags as UINT);
}
pub(crate) fn setup_callback<Callback: HostHandler>(&self, handler: Callback) {
let payload: HostCallback<Callback> = HostCallback {
sig: 17,
behaviors: Rc::clone(&self.behaviors),
archive: Rc::clone(&self.archive),
handler: handler,
};
*self.handler.borrow_mut() = NativeHandler::from(payload);
let ptr = self.handler.borrow().as_mut_ptr();
(_API.SciterSetCallback)(self.get_hwnd(), _on_handle_notification::<Callback>, ptr);
(_API.SciterSetupDebugOutput)(0 as HWINDOW, ptr, _on_debug_notification::<Callback>);
}
pub fn register_behavior<Factory>(&self, name: &str, factory: Factory)
where
Factory: Fn() -> Box<dyn EventHandler> + 'static
{
let make: Box<dyn Fn() -> Box<dyn EventHandler>> = Box::new(factory);
let pair = (name.to_owned(), make);
self.behaviors.borrow_mut().push(pair);
}
pub fn register_archive(&self, resource: &[u8]) -> Result<()> {
*self.archive.borrow_mut() = Some(Archive::open(resource)?);
Ok(())
}
pub fn enable_debug(&self, enable: bool) {
(_API.SciterSetOption)(self.hwnd, SCITER_RT_OPTIONS::SCITER_SET_DEBUG_MODE, enable as UINT_PTR);
}
pub fn get_hwnd(&self) -> HWINDOW {
self.hwnd
}
pub fn get_root(&self) -> Option<dom::Element> {
dom::Element::from_window(self.hwnd).ok()
}
pub fn load_file(&self, uri: &str) -> bool {
let s = s2w!(uri);
(_API.SciterLoadFile)(self.hwnd, s.as_ptr()) != 0
}
pub fn load_html(&self, html: &[u8], uri: Option<&str>) -> bool {
match uri {
Some(uri) => {
let s = s2w!(uri);
(_API.SciterLoadHtml)(self.hwnd, html.as_ptr(), html.len() as UINT, s.as_ptr()) != 0
},
None => {
(_API.SciterLoadHtml)(self.hwnd, html.as_ptr(), html.len() as UINT, 0 as LPCWSTR) != 0
}
}
}
pub fn data_ready(&self, uri: &str, data: &[u8]) {
let s = s2w!(uri);
(_API.SciterDataReady)(self.hwnd, s.as_ptr(), data.as_ptr(), data.len() as UINT);
}
pub fn data_ready_async(&self, uri: &str, data: &[u8], request_id: Option<HREQUEST>) {
let s = s2w!(uri);
let req = request_id.unwrap_or(::std::ptr::null_mut());
(_API.SciterDataReadyAsync)(self.hwnd, s.as_ptr(), data.as_ptr(), data.len() as UINT, req);
}
pub fn eval_script(&self, script: &str) -> ::std::result::Result<Value, Value> {
let (s,n) = s2wn!(script);
let mut rv = Value::new();
let ok = (_API.SciterEval)(self.hwnd, s.as_ptr(), n, rv.as_ptr());
ok_or!(ok, rv, rv)
}
pub fn call_function(&self, name: &str, args: &[Value]) -> ::std::result::Result<Value, Value> {
let mut rv = Value::new();
let s = s2u!(name);
let argv = Value::pack_args(args);
let ok = (_API.SciterCall)(self.hwnd, s.as_ptr(), argv.len() as UINT, argv.as_ptr(), rv.as_ptr());
ok_or!(ok, rv, rv)
}
pub fn set_home_url(&self, url: &str) -> Result<()> {
let s = s2w!(url);
let ok = (_API.SciterSetHomeURL)(self.hwnd, s.as_ptr());
ok_or!(ok)
}
pub fn set_media_type(&self, media_type: &str) -> Result<()> {
let s = s2w!(media_type);
let ok = (_API.SciterSetMediaType)(self.hwnd, s.as_ptr());
ok_or!(ok)
}
pub fn set_media_vars(&self, media: &Value) -> Result<()> {
let ok = (_API.SciterSetMediaVars)(self.hwnd, media.as_cptr());
ok_or!(ok)
}
pub fn set_master_css(&self, css: &str, append: bool) -> Result<()> {
let s = s2u!(css);
let b = s.as_bytes();
let n = b.len() as UINT;
let ok = if append {
(_API.SciterAppendMasterCSS)(b.as_ptr(), n)
} else {
(_API.SciterSetMasterCSS)(b.as_ptr(), n)
};
ok_or!(ok)
}
pub fn set_window_css(&self, css: &str, base_url: &str, media_type: &str) -> Result<()> {
let s = s2u!(css);
let url = s2w!(base_url);
let media = s2w!(media_type);
let b = s.as_bytes();
let n = b.len() as UINT;
let ok = (_API.SciterSetCSS)(self.hwnd, b.as_ptr(), n, url.as_ptr(), media.as_ptr());
ok_or!(ok)
}
}
extern "system" fn _on_handle_notification<T: HostHandler>(pnm: *mut ::capi::scdef::SCITER_CALLBACK_NOTIFICATION, param: LPVOID) -> UINT
{
use capi::scdef::{SCITER_NOTIFICATION, SCITER_CALLBACK_NOTIFICATION};
let callback = NativeHandler::get_data::<HostCallback<T>>(¶m);
let me: &mut T = &mut callback.handler;
let nm: &mut SCITER_CALLBACK_NOTIFICATION = unsafe { &mut *pnm };
let code: SCITER_NOTIFICATION = unsafe { ::std::mem::transmute(nm.code) };
let result: UINT = match code {
SCITER_NOTIFICATION::SC_LOAD_DATA => {
let scnm = pnm as *mut SCN_LOAD_DATA;
let scnm = unsafe { &mut *scnm };
let mut re = me.on_data_load(scnm);
if re.is_none() {
if let Some(archive) = callback.archive.borrow().as_ref() {
let uri = w2s!(scnm.uri);
if uri.starts_with("this://app/") {
if let Some(data) = archive.get(&uri) {
me.data_ready(scnm.hwnd, &uri, data, None);
} else {
eprintln!("[sciter] error: can't load {:?}", uri);
}
}
re = Some(LOAD_RESULT::LOAD_DEFAULT);
}
}
re.unwrap_or(LOAD_RESULT::LOAD_DEFAULT) as UINT
},
SCITER_NOTIFICATION::SC_DATA_LOADED => {
let scnm = pnm as *mut SCN_DATA_LOADED;
me.on_data_loaded(unsafe { &mut *scnm } );
0
},
SCITER_NOTIFICATION::SC_ATTACH_BEHAVIOR => {
let scnm = pnm as *mut SCN_ATTACH_BEHAVIOR;
let scnm = unsafe { &mut *scnm };
let mut re = me.on_attach_behavior(scnm);
if !re {
let name = u2s!(scnm.name);
let behavior = callback.behaviors
.borrow()
.iter()
.find(|x| x.0 == name)
.map(|x| x.1());
if let Some(behavior) = behavior {
let boxed = Box::new( BoxedHandler { handler: behavior } );
let ptr = Box::into_raw(boxed);
scnm.elementProc = ::eventhandler::_event_handler_behavior_proc;
scnm.elementTag = ptr as LPVOID;
re = true;
}
}
re as UINT
},
SCITER_NOTIFICATION::SC_ENGINE_DESTROYED => {
me.on_engine_destroyed();
0
},
SCITER_NOTIFICATION::SC_GRAPHICS_CRITICAL_FAILURE => {
me.on_graphics_critical_failure();
0
},
SCITER_NOTIFICATION::SC_INVALIDATE_RECT => {
let scnm = pnm as *const SCN_INVALIDATE_RECT;
me.on_invalidate(unsafe { &*scnm });
0
}
_ => 0,
};
return result;
}
extern "system" fn _on_debug_notification<T: HostHandler>(param: LPVOID, subsystem: OUTPUT_SUBSYTEMS, severity: OUTPUT_SEVERITY,
text: LPCWSTR, _text_length: UINT)
{
let me = NativeHandler::get_data::<HostCallback<T>>(¶m);
let message = ::utf::w2s(text).replace("\r", "\n");
me.handler.on_debug_output(subsystem, severity, message.trim_end());
}
pub struct Archive(HSARCHIVE);
impl Drop for Archive {
fn drop(&mut self) {
(_API.SciterCloseArchive)(self.0);
}
}
impl Archive {
pub fn open(archived: &[u8]) -> Result<Self> {
let p = (_API.SciterOpenArchive)(archived.as_ptr(), archived.len() as u32);
if !p.is_null() {
Ok(Archive(p))
} else {
Err(())
}
}
pub fn get(&self, path: &str) -> Option<&[u8]> {
let skip = if path.starts_with("this://app/") {
"this://app/".len()
} else if path.starts_with("//") {
"//".len()
} else {
0
};
let wname = s2w!(path);
let name = &wname[skip..];
let mut pb = ::std::ptr::null();
let mut cb = 0;
let ok = (_API.SciterGetArchiveItem)(self.0, name.as_ptr(), &mut pb, &mut cb);
if ok != 0 && !pb.is_null() {
let data = unsafe { ::std::slice::from_raw_parts(pb, cb as usize) };
Some(data)
} else {
None
}
}
}