sciter/
lib.rs

1// This component uses Sciter Engine,
2// copyright Terra Informatica Software, Inc.
3// (http://terrainformatica.com/).
4
5/*!
6# Rust bindings library for Sciter engine.
7
8[Sciter](http://sciter.com) is an embeddable [multiplatform](https://sciter.com/sciter/crossplatform/) HTML/CSS/script engine
9with GPU accelerated rendering designed to render modern desktop application UI.
10It's a compact, single dll/dylib/so file (4-8 mb) engine without any additional dependencies.
11
12Check the [screenshot gallery](https://github.com/oskca/sciter#sciter-desktop-ui-examples)
13of the desktop UI examples.
14
15Sciter supports all standard elements defined in HTML5 specification
16[with some additions](https://sciter.com/developers/for-web-programmers/).
17CSS is extended to better support the Desktop UI development,
18e.g. flow and flex units, vertical and horizontal alignment, OS theming.
19
20[Sciter SDK](https://sciter.com/download/) comes with a demo "browser" with builtin DOM inspector,
21script debugger and documentation viewer:
22
23![Sciter tools](https://sciter.com/images/sciter-tools.png)
24
25Check <https://sciter.com> website and its [documentation resources](https://sciter.com/developers/)
26for engine principles, architecture and more.
27
28
29## Brief look:
30
31Here is a minimal sciter app:
32
33```no_run
34extern crate sciter;
35
36fn main() {
37    let mut frame = sciter::Window::new();
38    frame.load_file("minimal.htm");
39    frame.run_app();
40}
41```
42
43It looks similar like this:
44
45![Minimal sciter sample](https://i.imgur.com/ojcM5JJ.png)
46
47Check [rust-sciter/examples](https://github.com/sciter-sdk/rust-sciter/tree/master/examples)
48folder for more complex usage and module-level sections for the guides about:
49
50* [Window](window/index.html) creation.
51* [Behaviors](dom/event/index.html) and event handling.
52* [DOM](dom/index.html) access methods.
53* Sciter [Value](value/index.html) interface.
54
55*/
56
57#![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// documentation test:
61// #![warn(missing_docs)]
62
63
64/* Clippy lints */
65
66#![allow(clippy::needless_return, clippy::let_and_return)] // past habits
67#![allow(clippy::redundant_field_names)] // since Rust 1.17 and less readable
68#![allow(clippy::unreadable_literal)] // C++ SDK constants
69#![allow(clippy::upper_case_acronyms)]// C++ SDK constants
70#![allow(clippy::deprecated_semver)]  // `#[deprecated(since="Sciter 4.4.3.24")]` is not a semver format.
71#![allow(clippy::result_unit_err)]		// Sciter returns BOOL, but `Result<(), ()>` is more strict even without error description.
72// #![allow(clippy::cast_ptr_alignment)] // 0.0.195 only
73
74
75/* Macros */
76
77#[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
90/* Rust interface */
91mod 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
113/// Builder pattern for window creation. See [`window::Builder`](window/struct.Builder.html) documentation.
114///
115/// For example,
116///
117/// ```rust,no_run
118/// let mut frame = sciter::WindowBuilder::main_window()
119///   .with_size((800,600))
120///   .glassy()
121///   .fixed()
122///   .create();
123/// ```
124pub type WindowBuilder = window::Builder;
125
126
127/* Loader */
128pub use capi::scapi::{ISciterAPI};
129use capi::scgraphics::SciterGraphicsAPI;
130use capi::screquest::SciterRequestAPI;
131
132#[cfg(windows)]
133mod ext {
134	// Note:
135	// Sciter 4.x shipped with universal "sciter.dll" library for different builds:
136	// bin/32, bin/64, bin/skia32, bin/skia64
137	// However it is quite inconvenient now (e.g. we can not put x64 and x86 builds in %PATH%)
138	//
139	#![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      // modern dll name
171      let mut dll = unsafe { LoadLibraryA(b"sciter.dll\0".as_ptr() as LPCSTR) };
172      if dll.is_null() {
173        // try to load with old names
174        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    // try specified path first (and only if present)
185    // and several paths to lookup then
186    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      // get the "SciterAPI" exported symbol
194      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	// "libsciter.dylib" since Sciter 4.4.6.3.
231  #[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    // Try to load the library from a specified absolute path.
248    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    // Try to find a library (by one of its names) in a specified path.
260    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    // Try to load from the current directory.
280    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            // "(bundle folder)/Contents/Frameworks/"
290            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    // Try to load indirectly via `dlopen("dll.so")`.
310    fn in_global() -> Option<LPVOID> {
311      try_load_from(None)
312    }
313
314    // Try to find in $PATH.
315    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    // try specified path first (and only if present)
328    // and several paths to lookup then
329    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      // get the "SciterAPI" exported symbol
337      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	// Note:
368	// Since 4.1.4 library name has been changed to "libsciter-gtk" (without 32/64 suffix).
369	// Since 3.3.1.6 library name was changed to "libsciter".
370	// However CC requires `-l sciter` form.
371	#[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/// Getting ISciterAPI reference, can be used for manual API calling.
382#[doc(hidden)]
383#[allow(non_snake_case)]
384pub fn SciterAPI<'a>() -> &'a ISciterAPI {
385	let ap = unsafe {
386		if cfg!(feature="extension") {
387			// TODO: it's not good to raise a panic inside `lazy_static!`,
388      // because it wents into recursive panicing.
389      //
390			// Somehow, `cargo test --all` tests all the features,
391      // also sometimes it comes even without `cfg!(test)`.
392      // Well, the culprit is "examples/extensions" which uses the "extension" feature,
393      // but how on earth it builds without `cfg(test)`?
394      //
395			if cfg!(test) {
396				&*ext::SciterAPI()
397			} else {
398				EXT_API
399					//.or_else(|| Some(&*ext::SciterAPI()))
400					.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/// Getting ISciterAPI reference, can be used for manual API calling.
420///
421/// Bypasses ABI compatability checks.
422#[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
448/// Set a custom path to the Sciter dynamic library.
449///
450/// Note: Must be called first before any other function.
451///
452/// Returns error if the specified library can not be loaded.
453///
454/// # Example
455///
456/// ```rust
457/// if sciter::set_library("~/lib/sciter/bin.gtk/x64/libsciter-gtk.so").is_ok() {
458///   println!("loaded Sciter version {}", sciter::version());
459/// }
460/// ```
461pub 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
480/// Set the Sciter API coming from `SciterLibraryInit`.
481///
482/// Note: Must be called first before any other function.
483pub fn set_host_api(api: &'static ISciterAPI) {
484	if cfg!(feature="extension") {
485		unsafe {
486			EXT_API.replace(api);
487		}
488	}
489}
490
491/// Sciter engine version number (e.g. `0x03030200`).
492///
493/// Note: does not return the `build` part because it doesn't fit in `0..255` byte range.
494/// Use [`sciter::version()`](fn.version.html) instead which returns the complete version string.
495pub 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	// let num = ((v1 >> 16) << 24) | ((v1 & 0xFFFF) << 16) | ((v2 >> 16) << 8) | (v2 & 0xFFFF);
502	return num;
503}
504
505/// Sciter engine version string (e.g. "`3.3.2.0`").
506pub 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
515/// Sciter API version.
516///
517/// Returns:
518///
519/// * `0x0000_0001` for regular builds, `0x0001_0001` for windowless builds.
520/// * `0x0000_0002` since 4.4.2.14 (a breaking change in assets with [SOM builds](https://sciter.com/native-code-exposure-to-script/))
521/// * `0x0000_0003` since 4.4.2.16
522/// * `0x0000_0004` since 4.4.2.17 (a breaking change in SOM passport)
523/// * `0x0000_0005` since 4.4.3.20 (a breaking change in `INITIALIZATION_PARAMS`, SOM in event handlers fix)
524/// * `0x0000_0006` since 4.4.3.24 (TIScript native API is gone, use SOM instead)
525/// * `0x0000_0007` since 4.4.5.4  (DOM-Value conversion functions were added, no breaking change)
526/// * `0x0000_0008` since 4.4.6.7 (Sciter and Sciter.Lite are unified in ISciterAPI)
527/// * `0x0000_0009` since 4.4.7.0 (ISciterAPI unification between all platforms)
528///
529/// Since 4.4.0.3.
530pub fn api_version() -> u32 {
531	_API.version
532}
533
534/// Returns true for windowless builds.
535pub fn is_windowless() -> bool {
536	api_version() >= 0x0001_0001
537}
538
539/// Various global Sciter engine options.
540///
541/// Used by [`sciter::set_options()`](fn.set_options.html).
542///
543/// See also [per-window options](window/enum.Options.html).
544#[derive(Copy, Clone)]
545pub enum RuntimeOptions<'a> {
546
547  /// global; value: the full path to the Sciter dynamic library (dll/dylib/so),
548  /// must be called before any other Sciter function.
549  LibraryPath(&'a str),
550  /// global; value: [`GFX_LAYER`](enum.GFX_LAYER.html), must be called before any window creation.
551  GfxLayer(GFX_LAYER),
552  /// global; value: `true` - the engine will use a "unisex" theme that is common for all platforms.
553  /// That UX theme is not using OS primitives for rendering input elements.
554  /// Use it if you want exactly the same (modulo fonts) look-n-feel on all platforms.
555  UxTheming(bool),
556  /// global or per-window; enables Sciter Inspector for all windows, must be called before any window creation.
557  DebugMode(bool),
558  /// global or per-window; value: combination of [`SCRIPT_RUNTIME_FEATURES`](enum.SCRIPT_RUNTIME_FEATURES.html) flags.
559  ///
560  /// Note that these features have been disabled by default
561  /// since [4.2.5.0](https://rawgit.com/c-smile/sciter-sdk/7036a9c7912ac30d9f369d9abb87b278d2d54c6d/logfile.htm).
562  ScriptFeatures(u8),
563	/// global; value: milliseconds, connection timeout of http client.
564	ConnectionTimeout(u32),
565	/// global; value: `0` - drop connection, `1` - use builtin dialog, `2` - accept connection silently.
566	OnHttpsError(u8),
567	// global; value: json with GPU black list, see the `gpu-blacklist.json` resource.
568	// Not used in Sciter 4, in fact: https://sciter.com/forums/topic/how-to-use-the-gpu-blacklist/#post-59338
569	// GpuBlacklist(&'a str),
570	/// global; value: script source to be loaded into each view before any other script execution.
571	InitScript(&'a str),
572	/// global; value - max request length in megabytes (1024*1024 bytes), since 4.3.0.15.
573	MaxHttpDataLength(usize),
574	/// global or per-window; value: `true` - `1px` in CSS is treated as `1dip`, otherwise `1px` is a physical pixel (by default).
575	///
576	/// since [4.4.5.0](https://rawgit.com/c-smile/sciter-sdk/aafb625bb0bc317d79c0a14d02b5730f6a02b48a/logfile.htm).
577	LogicalPixel(bool),
578}
579
580/// Set various global Sciter engine options, see the [`RuntimeOptions`](enum.RuntimeOptions.html).
581pub 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		// GpuBlacklist(json) => (SCITER_SET_GPU_BLACKLIST, json.as_bytes().as_ptr() as usize),
588		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
608/// Set a global variable by its path to all windows.
609///
610/// This variable will be accessible in all windows via `globalThis[path]` or just `path`.
611///
612/// Note that this call affects only _new_ windows,
613/// so it's preferred to call it before the main window creation.
614///
615/// See also per-window [`Window::set_variable`](window/struct.Window.html#method.set_variable)
616/// to assign a global to a single window only.
617pub 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/// Get a global variable by its path.
628///
629/// See the per-window [`Window::get_variable`](window/struct.Window.html#method.get_variable).
630#[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}