rustdesk/libs/rust-sciter/src/windowless.rs
2021-05-23 10:55:19 +08:00

331 lines
8.5 KiB
Rust

/*! 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<PaintLayer>,
/// 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<dyn Fn(&RECT, &[u8])>,
}
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<dyn Fn>")
.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<PaintLayer>,
/// [`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<dyn Fn(&RECT, &[u8])>,
}
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
}