1#![doc(html_logo_url = "https://sciter.com/screenshots/slide-sciter-osx.png",
58 html_favicon_url = "https://sciter.com/wp-content/themes/sciter/!images/favicon.ico")]
59
60#![allow(clippy::needless_return, clippy::let_and_return)] #![allow(clippy::redundant_field_names)] #![allow(clippy::unreadable_literal)] #![allow(clippy::upper_case_acronyms)]#![allow(clippy::deprecated_semver)] #![allow(clippy::result_unit_err)] #[cfg(target_os = "macos")]
78#[macro_use] extern crate objc;
79#[macro_use] extern crate lazy_static;
80
81
82#[macro_use] pub mod macros;
83
84mod capi;
85
86#[doc(hidden)]
87pub use capi::scdom::{HELEMENT};
88pub use capi::scdef::{GFX_LAYER, SCRIPT_RUNTIME_FEATURES};
89
90mod platform;
92mod eventhandler;
93
94pub mod dom;
95pub mod graphics;
96pub mod host;
97pub mod om;
98pub mod request;
99pub mod types;
100pub mod utf;
101pub mod value;
102pub mod video;
103pub mod window;
104pub mod windowless;
105
106pub use dom::Element;
107pub use dom::event::EventHandler;
108pub use host::{Archive, Host, HostHandler};
109pub use value::{Value, FromValue};
110pub use window::Window;
111
112
113pub type WindowBuilder = window::Builder;
125
126
127pub use capi::scapi::{ISciterAPI};
129use capi::scgraphics::SciterGraphicsAPI;
130use capi::screquest::SciterRequestAPI;
131
132#[cfg(windows)]
133mod ext {
134 #![allow(non_snake_case, non_camel_case_types)]
140 use capi::scapi::{ISciterAPI};
141 use capi::sctypes::{LPCSTR, LPCVOID, BOOL};
142
143 type ApiType = *const ISciterAPI;
144 type FuncType = extern "system" fn () -> *const ISciterAPI;
145
146 pub static mut CUSTOM_DLL_PATH: Option<String> = None;
147
148 extern "system"
149 {
150 fn LoadLibraryA(lpFileName: LPCSTR) -> LPCVOID;
151 fn FreeLibrary(dll: LPCVOID) -> BOOL;
152 fn GetProcAddress(hModule: LPCVOID, lpProcName: LPCSTR) -> LPCVOID;
153 }
154
155 pub fn try_load_library(permanent: bool) -> ::std::result::Result<ApiType, String> {
156 use std::ffi::CString;
157 use std::path::Path;
158
159 fn try_load(path: &Path) -> Option<LPCVOID> {
160 let path = CString::new(format!("{}", path.display())).expect("invalid library path");
161 let dll = unsafe { LoadLibraryA(path.as_ptr()) };
162 if !dll.is_null() {
163 Some(dll)
164 } else {
165 None
166 }
167 }
168
169 fn in_global() -> Option<LPCVOID> {
170 let mut dll = unsafe { LoadLibraryA(b"sciter.dll\0".as_ptr() as LPCSTR) };
172 if dll.is_null() {
173 let alternate = if cfg!(target_arch = "x86_64") { b"sciter64.dll\0" } else { b"sciter32.dll\0" };
175 dll = unsafe { LoadLibraryA(alternate.as_ptr() as LPCSTR) };
176 }
177 if !dll.is_null() {
178 Some(dll)
179 } else {
180 None
181 }
182 }
183
184 let dll = if let Some(path) = unsafe { CUSTOM_DLL_PATH.as_ref() } {
187 try_load(Path::new(path))
188 } else {
189 in_global()
190 };
191
192 if let Some(dll) = dll {
193 let sym = unsafe { GetProcAddress(dll, b"SciterAPI\0".as_ptr() as LPCSTR) };
195 if sym.is_null() {
196 return Err("\"SciterAPI\" function was expected in the loaded library.".to_owned());
197 }
198
199 if !permanent {
200 unsafe { FreeLibrary(dll) };
201 return Ok(0 as ApiType);
202 }
203
204 let get_api: FuncType = unsafe { std::mem::transmute(sym) };
205 return Ok(get_api());
206 }
207 let sdkbin = if cfg!(target_arch = "x86_64") { "bin/64" } else { "bin/32" };
208 let msg = format!("Please verify that Sciter SDK is installed and its binaries (from SDK/{}) are available in PATH.", sdkbin);
209 Err(format!("error: '{}' was not found neither in PATH nor near the current executable.\n {}", "sciter.dll", msg))
210 }
211
212 pub unsafe fn SciterAPI() -> *const ISciterAPI {
213 match try_load_library(true) {
214 Ok(api) => api,
215 Err(error) => panic!("{}", error),
216 }
217 }
218}
219
220#[cfg(all(feature = "dynamic", unix))]
221mod ext {
222 #![allow(non_snake_case, non_camel_case_types)]
223 extern crate libc;
224
225 pub static mut CUSTOM_DLL_PATH: Option<String> = None;
226
227 #[cfg(target_os = "linux")]
228 const DLL_NAMES: &[&str] = &[ "libsciter-gtk.so" ];
229
230 #[cfg(target_os = "macos")]
232 const DLL_NAMES: &[&str] = &[ "libsciter.dylib", "sciter-osx-64.dylib" ];
233
234 use capi::scapi::ISciterAPI;
235 use capi::sctypes::{LPVOID, LPCSTR};
236
237 type FuncType = extern "system" fn () -> *const ISciterAPI;
238 type ApiType = *const ISciterAPI;
239
240
241 pub fn try_load_library(permanent: bool) -> ::std::result::Result<ApiType, String> {
242 use std::ffi::CString;
243 use std::os::unix::ffi::OsStrExt;
244 use std::path::{Path};
245
246
247 fn try_load(path: &Path) -> Option<LPVOID> {
249 let bytes = path.as_os_str().as_bytes();
250 if let Ok(cstr) = CString::new(bytes) {
251 let dll = unsafe { libc::dlopen(cstr.as_ptr(), libc::RTLD_LOCAL | libc::RTLD_LAZY) };
252 if !dll.is_null() {
253 return Some(dll)
254 }
255 }
256 None
257 }
258
259 fn try_load_from(dir: Option<&Path>) -> Option<LPVOID> {
261
262 let dll = DLL_NAMES.iter()
263 .map(|name| {
264 let mut path = dir.map(Path::to_owned).unwrap_or_default();
265 path.push(name);
266 path
267 })
268 .map(|path| try_load(&path))
269 .find(|dll| dll.is_some())
270 .map(|o| o.unwrap());
271
272 if dll.is_some() {
273 return dll;
274 }
275
276 None
277 }
278
279 fn in_current_dir() -> Option<LPVOID> {
281 if let Ok(dir) = ::std::env::current_exe() {
282 if let Some(dir) = dir.parent() {
283 let dll = try_load_from(Some(dir));
284 if dll.is_some() {
285 return dll;
286 }
287
288 if cfg!(target_os = "macos") {
289 let mut path = dir.to_owned();
291 path.push("../Frameworks/libsciter.dylib");
292 let dll = try_load(&path);
293 if dll.is_some() {
294 return dll;
295 }
296
297 let mut path = dir.to_owned();
298 path.push("../Frameworks/sciter-osx-64.dylib");
299 let dll = try_load(&path);
300 if dll.is_some() {
301 return dll;
302 }
303 }
304 }
305 }
306 None
307 }
308
309 fn in_global() -> Option<LPVOID> {
311 try_load_from(None)
312 }
313
314 fn in_paths() -> Option<LPVOID> {
316 use std::env;
317 if let Some(paths) = env::var_os("PATH") {
318 for path in env::split_paths(&paths) {
319 if let Some(dll) = try_load_from(Some(&path)) {
320 return Some(dll);
321 }
322 }
323 }
324 None
325 }
326
327 let dll = if let Some(path) = unsafe { CUSTOM_DLL_PATH.as_ref() } {
330 try_load(Path::new(path))
331 } else {
332 in_current_dir().or_else(in_paths).or_else(in_global)
333 };
334
335 if let Some(dll) = dll {
336 let sym = unsafe { libc::dlsym(dll, b"SciterAPI\0".as_ptr() as LPCSTR) };
338 if sym.is_null() {
339 return Err("\"SciterAPI\" function was expected in the loaded library.".to_owned());
340 }
341
342 if !permanent {
343 unsafe { libc::dlclose(dll) };
344 return Ok(0 as ApiType);
345 }
346
347 let get_api: FuncType = unsafe { std::mem::transmute(sym) };
348 return Ok(get_api());
349 }
350
351 let sdkbin = if cfg!(target_os = "macos") { "bin.osx" } else { "bin.lnx" };
352 let msg = format!("Please verify that Sciter SDK is installed and its binaries (from {}) are available in PATH.", sdkbin);
353 Err(format!("error: '{}' was not found neither in PATH nor near the current executable.\n {}", DLL_NAMES[0], msg))
354 }
355
356 pub fn SciterAPI() -> *const ISciterAPI {
357 match try_load_library(true) {
358 Ok(api) => api,
359 Err(error) => panic!("{}", error),
360 }
361 }
362}
363
364
365#[cfg(all(target_os = "linux", not(feature = "dynamic")))]
366mod ext {
367 #[link(name = "sciter-gtk")]
372 extern "system" { pub fn SciterAPI() -> *const ::capi::scapi::ISciterAPI; }
373}
374
375#[cfg(all(target_os = "macos", target_arch = "x86_64", not(feature = "dynamic")))]
376mod ext {
377 #[link(name = "libsciter", kind = "dylib")]
378 extern "system" { pub fn SciterAPI() -> *const ::capi::scapi::ISciterAPI; }
379}
380
381#[doc(hidden)]
383#[allow(non_snake_case)]
384pub fn SciterAPI<'a>() -> &'a ISciterAPI {
385 let ap = unsafe {
386 if cfg!(feature="extension") {
387 if cfg!(test) {
396 &*ext::SciterAPI()
397 } else {
398 EXT_API
399 .expect("Sciter API is not available yet, call `sciter::set_api()` first.")
401 }
402 } else {
403 &*ext::SciterAPI()
404 }
405 };
406
407 let abi_version = ap.version;
408
409 if cfg!(feature = "windowless") {
410 assert!(abi_version >= 0x0001_0001, "Incompatible Sciter build and \"windowless\" feature");
411 }
412 if cfg!(not(feature = "windowless")) {
413 assert!(abi_version < 0x0001_0000, "Incompatible Sciter build and \"windowless\" feature");
414 }
415
416 return ap;
417}
418
419#[doc(hidden)]
423#[allow(non_snake_case)]
424pub fn SciterAPI_unchecked<'a>() -> &'a ISciterAPI {
425 let ap = unsafe {
426 if cfg!(feature="extension") {
427 EXT_API.expect("Sciter API is not available yet, call `sciter::set_api()` first.")
428 } else {
429 &*ext::SciterAPI()
430 }
431 };
432
433 return ap;
434}
435
436
437lazy_static! {
438 static ref _API: &'static ISciterAPI = SciterAPI();
439 static ref _GAPI: &'static SciterGraphicsAPI = {
440 if version_num() < 0x0401_0A00 {
441 panic!("Graphics API is incompatible since 4.1.10 (your version is {})", version());
442 }
443 unsafe { &*(SciterAPI().GetSciterGraphicsAPI)() }
444 };
445 static ref _RAPI: &'static SciterRequestAPI = unsafe { &*(SciterAPI().GetSciterRequestAPI)() };
446}
447
448pub fn set_library(custom_path: &str) -> ::std::result::Result<(), String> {
462 #[cfg(not(feature = "dynamic"))]
463 fn set_impl(_: &str) -> ::std::result::Result<(), String> {
464 Err("Don't use `sciter::set_library()` in static builds.\n Build with the feature \"dynamic\" instead.".to_owned())
465 }
466
467 #[cfg(feature = "dynamic")]
468 fn set_impl(path: &str) -> ::std::result::Result<(), String> {
469 unsafe {
470 ext::CUSTOM_DLL_PATH = Some(path.to_owned());
471 }
472 ext::try_load_library(false).map(|_| ())
473 }
474
475 set_impl(custom_path)
476}
477
478static mut EXT_API: Option<&'static ISciterAPI> = None;
479
480pub fn set_host_api(api: &'static ISciterAPI) {
484 if cfg!(feature="extension") {
485 unsafe {
486 EXT_API.replace(api);
487 }
488 }
489}
490
491pub fn version_num() -> u32 {
496 use types::BOOL;
497 let v1 = (_API.SciterVersion)(true as BOOL);
498 let v2 = (_API.SciterVersion)(false as BOOL);
499 let (major, minor, revision, _build) = (v1 >> 16 & 0xFF, v1 & 0xFF, v2 >> 16 & 0xFF, v2 & 0xFF);
500 let num = (major << 24) | (minor << 16) | (revision << 8);
501 return num;
503}
504
505pub fn version() -> String {
507 use types::BOOL;
508 let v1 = (_API.SciterVersion)(true as BOOL);
509 let v2 = (_API.SciterVersion)(false as BOOL);
510 let num = [v1 >> 16, v1 & 0xFFFF, v2 >> 16, v2 & 0xFFFF];
511 let version = format!("{}.{}.{}.{}", num[0], num[1], num[2], num[3]);
512 return version;
513}
514
515pub fn api_version() -> u32 {
531 _API.version
532}
533
534pub fn is_windowless() -> bool {
536 api_version() >= 0x0001_0001
537}
538
539#[derive(Copy, Clone)]
545pub enum RuntimeOptions<'a> {
546
547 LibraryPath(&'a str),
550 GfxLayer(GFX_LAYER),
552 UxTheming(bool),
556 DebugMode(bool),
558 ScriptFeatures(u8),
563 ConnectionTimeout(u32),
565 OnHttpsError(u8),
567 InitScript(&'a str),
572 MaxHttpDataLength(usize),
574 LogicalPixel(bool),
578}
579
580pub fn set_options(options: RuntimeOptions) -> std::result::Result<(), ()> {
582 use RuntimeOptions::*;
583 use capi::scdef::SCITER_RT_OPTIONS::*;
584 let (option, value) = match options {
585 ConnectionTimeout(ms) => (SCITER_CONNECTION_TIMEOUT, ms as usize),
586 OnHttpsError(behavior) => (SCITER_HTTPS_ERROR, behavior as usize),
587 InitScript(script) => (SCITER_SET_INIT_SCRIPT, script.as_bytes().as_ptr() as usize),
589 ScriptFeatures(mask) => (SCITER_SET_SCRIPT_RUNTIME_FEATURES, mask as usize),
590 GfxLayer(backend) => (SCITER_SET_GFX_LAYER, backend as usize),
591 DebugMode(enable) => (SCITER_SET_DEBUG_MODE, enable as usize),
592 UxTheming(enable) => (SCITER_SET_UX_THEMING, enable as usize),
593 MaxHttpDataLength(value) => (SCITER_SET_MAX_HTTP_DATA_LENGTH, value),
594 LogicalPixel(enable) => (SCITER_SET_PX_AS_DIP, enable as usize),
595
596 LibraryPath(path) => {
597 return set_library(path).map_err(|_|());
598 }
599 };
600 let ok = (_API.SciterSetOption)(std::ptr::null_mut(), option, value);
601 if ok != 0 {
602 Ok(())
603 } else {
604 Err(())
605 }
606}
607
608pub fn set_variable(path: &str, value: Value) -> dom::Result<()> {
618 let ws = s2u!(path);
619 let ok = (_API.SciterSetVariable)(std::ptr::null_mut(), ws.as_ptr(), value.as_cptr());
620 if ok == dom::SCDOM_RESULT::OK {
621 Ok(())
622 } else {
623 Err(ok)
624 }
625}
626
627#[doc(hidden)]
631pub fn get_variable(path: &str) -> dom::Result<Value> {
632 let ws = s2u!(path);
633 let mut value = Value::new();
634 let ok = (_API.SciterGetVariable)(std::ptr::null_mut(), ws.as_ptr(), value.as_mut_ptr());
635 if ok == dom::SCDOM_RESULT::OK {
636 Ok(value)
637 } else {
638 Err(ok)
639 }
640}