use ::{_API};
use capi::sctypes::*;
use capi::screquest::HREQUEST;
use capi::schandler::NativeHandler;
use dom::event::EventHandler;
use eventhandler::*;
use value::{Value};
pub use capi::scdef::{SCITER_RT_OPTIONS, SCRIPT_RUNTIME_FEATURES, GFX_LAYER};
pub use capi::scdef::{LOAD_RESULT, SCN_LOAD_DATA, SCN_DATA_LOADED, SCN_ATTACH_BEHAVIOR, OUTPUT_SUBSYTEMS, OUTPUT_SEVERITY};
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) -> LOAD_RESULT { return LOAD_RESULT::LOAD_DEFAULT; }
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_debug_output(&mut self, subsystem: OUTPUT_SUBSYTEMS, severity: OUTPUT_SEVERITY, message: &str) {
if !message.is_empty() {
println!("{:?}:{:?}: {}", 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<Fn() -> Box<EventHandler>>)>;
type SharedBehaviorList = Rc<RefCell<BehaviorList>>;
#[repr(C)]
struct HostCallback<Callback> {
sig: u32,
behaviors: SharedBehaviorList,
handler: Callback,
}
pub struct Host {
hwnd: HWINDOW,
behaviors: SharedBehaviorList,
handler: RefCell<NativeHandler>,
}
impl Host {
pub fn attach(hwnd: HWINDOW) -> Host {
let host = Host { hwnd: hwnd, behaviors: Default::default(), handler: Default::default() };
host.setup_callback(DefaultHandler::default());
return host;
}
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) {
self.enable_debug(true);
let payload: HostCallback<Callback> = HostCallback {
sig: 17,
behaviors: Rc::clone(&self.behaviors),
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<EventHandler> + 'static
{
let make: Box<Fn() -> Box<EventHandler>> = Box::new(factory);
let pair = (name.to_owned(), make);
self.behaviors.borrow_mut().push(pair);
}
pub fn enable_debug(&self, enable: bool) {
let hwnd = 0 as HWINDOW;
(_API.SciterSetOption)(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) {
let (s,_) = s2w!(uri);
(_API.SciterLoadFile)(self.hwnd, s.as_ptr());
}
pub fn load_html(&self, html: &[u8], uri: Option<&str>) {
match uri {
Some(uri) => {
let (s,_) = s2w!(uri);
(_API.SciterLoadHtml)(self.hwnd, html.as_ptr(), html.len() as UINT, s.as_ptr())
},
None => {
(_API.SciterLoadHtml)(self.hwnd, html.as_ptr(), html.len() as UINT, 0 as LPCWSTR)
}
};
}
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) = s2w!(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)
}
#[deprecated(since="0.5.40", note="please use `Window::set_options()` instead.")]
pub fn set_option(&self, option: SCITER_RT_OPTIONS, value: usize) -> Result<()> {
let ok = (_API.SciterSetOption)(self.hwnd, option, value as UINT_PTR);
ok_or!(ok)
}
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 re = me.on_data_load(unsafe { &mut *scnm} );
re as UINT
},
SCITER_NOTIFICATION::SC_DATA_LOADED => {
let scnm = pnm as *mut SCN_DATA_LOADED;
me.on_data_loaded(unsafe { &mut *scnm} );
0 as UINT
},
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 as UINT
},
SCITER_NOTIFICATION::SC_GRAPHICS_CRITICAL_FAILURE => {
me.on_graphics_critical_failure();
0 as UINT
},
_ => 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_right());
}
pub struct Archive(HSARCHIVE);
impl Drop for Archive {
fn drop(&mut self) {
(_API.SciterCloseArchive)(self.0);
}
}
impl Archive {
pub fn open(archived: &[u8]) -> Self {
let p = (_API.SciterOpenArchive)(archived.as_ptr(), archived.len() as u32);
Archive(p)
}
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
}
}
}