/*! Windowless Sciter. Windowless here means that Sciter does not use any `HWND`, `NSView*` or whatever OS uses for window designation. You just need to provide something of size `void*` that will be associated with the instance of the engine. Check out [this article](https://sciter.com/sciter-lite-is-published/) on sciter.com that explains the difference between the desktop and the windowless Sciter engine versions. */ use ::{_API}; use capi::scdef::{GFX_LAYER}; use capi::scdom::HELEMENT; use capi::sctypes::{HWINDOW, POINT, UINT, BOOL, RECT, LPCBYTE, LPVOID, INT}; use capi::scmsg::*; pub use capi::scmsg::key_codes; pub use capi::scbehavior::{MOUSE_BUTTONS, MOUSE_EVENTS, KEYBOARD_STATES, KEY_EVENTS}; /// Application-provided events to notify Sciter. #[derive(Debug)] pub enum Message { /// Creates an instance of Sciter assotiated with the given handle. Create { /// Graphics backend for rendering. backend: GFX_LAYER, /// Background transparency option. transparent: bool, }, /// Destroys the engine instance. Destroy, /// Window size changes. Size { /// Width of the rendering surface. width: u32, /// Height of the rendering surface. height: u32, }, /// Screen resolution changes. Resolution { /// Pixels per inch. ppi: u32, }, /// Window focus event. Focus { /// Whether the window has got or lost the input focus. enter: bool, }, /// Time changes in order to process animations, timers and other timed things. Heartbit { /// Absolute steady clock value, e.g. `GetTickCount()` or `glfwGetTime()`. milliseconds: u32, }, /// Redraw the whole document. Redraw, /// Redraw the specific layer. Paint(PaintLayer), /// Render to a bitmap. RenderTo(RenderEvent), #[cfg(any(windows, doc))] /// Render to a DXGI surface (Windows only, since 4.4.3.27). RenderToDxgiSurface(DxgiRenderEvent), /// Mouse input. Mouse(MouseEvent), /// Keyboard input. Keyboard(KeyboardEvent), } /// Events describing the mouse input. #[derive(Debug)] pub struct MouseEvent { /// A specific mouse event, like "mouse down" or "mouse move". pub event: MOUSE_EVENTS, /// Which mouse button is pressed. pub button: MOUSE_BUTTONS, /// Which keyboard modifier (e.g. Ctrl or Alt) is pressed. pub modifiers: KEYBOARD_STATES, /// Mouse cursor position. pub pos: POINT, } /// Events describing the keyboard input. #[derive(Debug)] pub struct KeyboardEvent { /// A specific key event, like "key down" or "key up". pub event: KEY_EVENTS, /// A key code: /// /// * a keyboard [scan-code](key_codes/index.html) /// for [`KEY_DOWN`](enum.KEY_EVENTS.html#variant.KEY_DOWN) /// and [`KEY_UP`](enum.KEY_EVENTS.html#variant.KEY_UP) events; /// * a Unicode code point for [`KEY_CHAR`](enum.KEY_EVENTS.html#variant.KEY_CHAR). pub code: UINT, /// Which keyboard modifier (e.g. Ctrl or Alt) is pressed. pub modifiers: KEYBOARD_STATES, } /// A specific UI layer to redraw. #[derive(Debug)] pub struct PaintLayer { /// A DOM element (layer) to render. pub element: HELEMENT, /// Whether the `element` is the topmost layer or a background one. pub is_foreground: bool, } /// Events for rendering UI to a bitmap. pub struct RenderEvent { /// Which layer to render (or the whole document if `None`). pub layer: Option, /// The callback that receives a rendered bitmap. /// /// The first argument contains a rectangle with the coordinates (position and size) of the rendered bitmap. /// /// The second ardument is the rendered bitmap in the `BGRA` form. The size of the bitmap equals to `width * height * 4`. pub callback: Box, } impl std::fmt::Debug for RenderEvent { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { fmt .debug_struct("RenderEvent") .field("layer", &self.layer) .field("callback", &"Box") .finish() } } #[cfg(any(windows, doc))] #[derive(Debug)] /// Events for rendering UI to a DXGI surface. /// /// Since 4.4.3.27. pub struct DxgiRenderEvent { /// Which layer to render (or the whole document if `None`). pub layer: Option, /// [`IDXGISurface`](https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nn-dxgi-idxgisurface) pointer. pub surface: LPVOID, } /// Notify Sciter about UI-specific events. /// /// `wnd` here is not a window handle but rather a window instance (pointer). pub fn handle_message(wnd: HWINDOW, event: Message) -> bool { let ok = match event { Message::Create { backend, transparent } => { let msg = SCITER_X_MSG_CREATE { header: SCITER_X_MSG_CODE::SXM_CREATE.into(), backend, transparent: transparent as BOOL, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Destroy => { let msg = SCITER_X_MSG_DESTROY { header: SCITER_X_MSG_CODE::SXM_DESTROY.into(), }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Size { width, height} => { let msg = SCITER_X_MSG_SIZE { header: SCITER_X_MSG_CODE::SXM_SIZE.into(), width, height, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Resolution { ppi } => { let msg = SCITER_X_MSG_RESOLUTION { header: SCITER_X_MSG_CODE::SXM_RESOLUTION.into(), ppi, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Focus { enter } => { let msg = SCITER_X_MSG_FOCUS { header: SCITER_X_MSG_CODE::SXM_FOCUS.into(), enter: enter as BOOL, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Heartbit { milliseconds } => { let msg = SCITER_X_MSG_HEARTBIT { header: SCITER_X_MSG_CODE::SXM_HEARTBIT.into(), time: milliseconds, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Mouse(params) => { let msg = SCITER_X_MSG_MOUSE { header: SCITER_X_MSG_CODE::SXM_MOUSE.into(), event: params.event, button: params.button, modifiers: params.modifiers as u32, pos: params.pos, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Keyboard(params) => { let msg = SCITER_X_MSG_KEY { header: SCITER_X_MSG_CODE::SXM_KEY.into(), event: params.event, code: params.code, modifiers: params.modifiers as u32, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Redraw => { use std::ptr; let msg = SCITER_X_MSG_PAINT { header: SCITER_X_MSG_CODE::SXM_PAINT.into(), element: ptr::null_mut(), isFore: true as BOOL, targetType: SCITER_PAINT_TARGET_TYPE::SPT_DEFAULT, context: ptr::null_mut(), callback: None, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::Paint(paint) => { let msg = SCITER_X_MSG_PAINT { header: SCITER_X_MSG_CODE::SXM_PAINT.into(), element: paint.element, isFore: paint.is_foreground as BOOL, targetType: SCITER_PAINT_TARGET_TYPE::SPT_DEFAULT, context: std::ptr::null_mut(), callback: None, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, #[cfg(windows)] Message::RenderToDxgiSurface(paint) => { let layer = paint.layer.unwrap_or(PaintLayer { element: std::ptr::null_mut(), is_foreground: false, }); let msg = SCITER_X_MSG_PAINT { header: SCITER_X_MSG_CODE::SXM_PAINT.into(), element: layer.element, isFore: layer.is_foreground as BOOL, targetType: SCITER_PAINT_TARGET_TYPE::SPT_SURFACE, context: paint.surface, callback: None, }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, Message::RenderTo(paint) => { struct Callback { callback: Box, } extern "system" fn inner(rgba: LPCBYTE, x: INT, y: INT, width: UINT, height: UINT, param: LPVOID) { assert!(!param.is_null()); assert!(!rgba.is_null()); if param.is_null() || rgba.is_null() { return; } let bitmap_area = RECT { left: x, top: y, right: x + width as INT, bottom: y + height as INT, }; let bitmap_size = width * height * 4; let bitmap_data = unsafe { std::slice::from_raw_parts(rgba, bitmap_size as usize) }; let param = param as *const Callback; let wrapper = unsafe { &*param }; (wrapper.callback)(&bitmap_area, bitmap_data); } let wrapper = Callback { callback: paint.callback, }; let param = &wrapper as *const _ as LPVOID; let layer = paint.layer.unwrap_or(PaintLayer { element: std::ptr::null_mut(), is_foreground: false, }); let msg = SCITER_X_MSG_PAINT { header: SCITER_X_MSG_CODE::SXM_PAINT.into(), element: layer.element, isFore: layer.is_foreground as BOOL, targetType: SCITER_PAINT_TARGET_TYPE::SPT_RECEIVER, context: param, callback: Some(inner), }; (_API.SciterProcX)(wnd, &msg.header as *const _) }, }; ok != 0 }