sciter/
windowless.rs

1/*! Windowless Sciter.
2
3Windowless here means that Sciter does not use any `HWND`, `NSView*` or whatever OS uses for window designation.
4You just need to provide something of size `void*` that will be associated with the instance of the engine.
5
6Check out [this article](https://sciter.com/sciter-lite-is-published/) on sciter.com that explains
7the difference between the desktop and the windowless Sciter engine versions.
8
9*/
10
11use ::{_API};
12use capi::scdef::{GFX_LAYER};
13use capi::scdom::HELEMENT;
14use capi::sctypes::{HWINDOW, POINT, UINT, BOOL, RECT, LPCBYTE, LPVOID, INT};
15use capi::scmsg::*;
16
17pub use capi::scmsg::key_codes;
18pub use capi::scbehavior::{MOUSE_BUTTONS, MOUSE_EVENTS, KEYBOARD_STATES, KEY_EVENTS};
19
20
21/// Application-provided events to notify Sciter.
22#[derive(Debug)]
23pub enum Message {
24	/// Creates an instance of Sciter assotiated with the given handle.
25	Create {
26		/// Graphics backend for rendering.
27		backend: GFX_LAYER,
28		/// Background transparency option.
29		transparent: bool,
30	},
31
32	/// Destroys the engine instance.
33	Destroy,
34
35	/// Window size changes.
36	Size {
37		/// Width of the rendering surface.
38		width: u32,
39		/// Height of the rendering surface.
40		height: u32,
41	},
42
43	/// Screen resolution changes.
44	Resolution {
45		/// Pixels per inch.
46		ppi: u32,
47	},
48
49	/// Window focus event.
50	Focus {
51		/// Whether the window has got or lost the input focus.
52		enter: bool,
53	},
54
55	/// Time changes in order to process animations, timers and other timed things.
56	Heartbit {
57		/// Absolute steady clock value, e.g. `GetTickCount()` or `glfwGetTime()`.
58		milliseconds: u32,
59	},
60
61	/// Redraw the whole document.
62	Redraw,
63
64	/// Redraw the specific layer.
65	Paint(PaintLayer),
66
67	/// Render to a bitmap.
68	RenderTo(RenderEvent),
69
70	#[cfg(any(windows, doc))]
71	/// Render to a DXGI surface (Windows only, since 4.4.3.27).
72	RenderToDxgiSurface(DxgiRenderEvent),
73
74	/// Mouse input.
75	Mouse(MouseEvent),
76
77	/// Keyboard input.
78	Keyboard(KeyboardEvent),
79}
80
81/// Events describing the mouse input.
82#[derive(Debug)]
83pub struct MouseEvent {
84	/// A specific mouse event, like "mouse down" or "mouse move".
85	pub event: MOUSE_EVENTS,
86	/// Which mouse button is pressed.
87	pub button: MOUSE_BUTTONS,
88	/// Which keyboard modifier (e.g. Ctrl or Alt) is pressed.
89	pub modifiers: KEYBOARD_STATES,
90	/// Mouse cursor position.
91	pub pos: POINT,
92}
93
94/// Events describing the keyboard input.
95#[derive(Debug)]
96pub struct KeyboardEvent {
97	/// A specific key event, like "key down" or "key up".
98	pub event: KEY_EVENTS,
99	/// A key code:
100	///
101	/// * a keyboard [scan-code](key_codes/index.html)
102	/// for [`KEY_DOWN`](enum.KEY_EVENTS.html#variant.KEY_DOWN)
103	/// and [`KEY_UP`](enum.KEY_EVENTS.html#variant.KEY_UP) events;
104	/// * a Unicode code point for [`KEY_CHAR`](enum.KEY_EVENTS.html#variant.KEY_CHAR).
105	pub code: UINT,
106	/// Which keyboard modifier (e.g. Ctrl or Alt) is pressed.
107	pub modifiers: KEYBOARD_STATES,
108}
109
110/// A specific UI layer to redraw.
111#[derive(Debug)]
112pub struct PaintLayer {
113	/// A DOM element (layer) to render.
114	pub element: HELEMENT,
115
116	/// Whether the `element` is the topmost layer or a background one.
117	pub is_foreground: bool,
118}
119
120/// Events for rendering UI to a bitmap.
121pub struct RenderEvent
122{
123	/// Which layer to render (or the whole document if `None`).
124	pub layer: Option<PaintLayer>,
125
126	/// The callback that receives a rendered bitmap.
127	///
128	/// The first argument contains a rectangle with the coordinates (position and size) of the rendered bitmap.
129	///
130	/// The second ardument is the rendered bitmap in the `BGRA` form. The size of the bitmap equals to `width * height * 4`.
131	pub callback: Box<dyn Fn(&RECT, &[u8])>,
132}
133
134impl std::fmt::Debug for RenderEvent {
135	fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
136		fmt
137			.debug_struct("RenderEvent")
138			.field("layer", &self.layer)
139			.field("callback", &"Box<dyn Fn>")
140			.finish()
141	}
142}
143
144#[cfg(any(windows, doc))]
145#[derive(Debug)]
146/// Events for rendering UI to a DXGI surface.
147///
148/// Since 4.4.3.27.
149pub struct DxgiRenderEvent {
150	/// Which layer to render (or the whole document if `None`).
151	pub layer: Option<PaintLayer>,
152
153	/// [`IDXGISurface`](https://docs.microsoft.com/en-us/windows/win32/api/dxgi/nn-dxgi-idxgisurface) pointer.
154	pub surface: LPVOID,
155}
156
157
158/// Notify Sciter about UI-specific events.
159///
160/// `wnd` here is not a window handle but rather a window instance (pointer).
161pub fn handle_message(wnd: HWINDOW, event: Message) -> bool
162{
163	let ok = match event {
164		Message::Create { backend, transparent } => {
165			let msg = SCITER_X_MSG_CREATE {
166				header: SCITER_X_MSG_CODE::SXM_CREATE.into(),
167				backend,
168				transparent: transparent as BOOL,
169			};
170			(_API.SciterProcX)(wnd, &msg.header as *const _)
171		},
172
173		Message::Destroy => {
174			let msg = SCITER_X_MSG_DESTROY {
175				header: SCITER_X_MSG_CODE::SXM_DESTROY.into(),
176			};
177			(_API.SciterProcX)(wnd, &msg.header as *const _)
178		},
179
180		Message::Size { width, height} => {
181			let msg = SCITER_X_MSG_SIZE {
182				header: SCITER_X_MSG_CODE::SXM_SIZE.into(),
183				width,
184				height,
185			};
186			(_API.SciterProcX)(wnd, &msg.header as *const _)
187		},
188
189		Message::Resolution { ppi } => {
190			let msg = SCITER_X_MSG_RESOLUTION {
191				header: SCITER_X_MSG_CODE::SXM_RESOLUTION.into(),
192				ppi,
193			};
194			(_API.SciterProcX)(wnd, &msg.header as *const _)
195		},
196
197		Message::Focus { enter } => {
198			let msg = SCITER_X_MSG_FOCUS {
199				header: SCITER_X_MSG_CODE::SXM_FOCUS.into(),
200				enter: enter as BOOL,
201			};
202			(_API.SciterProcX)(wnd, &msg.header as *const _)
203		},
204
205		Message::Heartbit { milliseconds } => {
206			let msg = SCITER_X_MSG_HEARTBIT {
207				header: SCITER_X_MSG_CODE::SXM_HEARTBIT.into(),
208				time: milliseconds,
209			};
210			(_API.SciterProcX)(wnd, &msg.header as *const _)
211		},
212
213		Message::Mouse(params) => {
214			let msg = SCITER_X_MSG_MOUSE {
215				header: SCITER_X_MSG_CODE::SXM_MOUSE.into(),
216
217				event: params.event,
218				button: params.button,
219				modifiers: params.modifiers,
220				pos: params.pos,
221			};
222			(_API.SciterProcX)(wnd, &msg.header as *const _)
223		},
224
225		Message::Keyboard(params) => {
226			let msg = SCITER_X_MSG_KEY {
227				header: SCITER_X_MSG_CODE::SXM_KEY.into(),
228
229				event: params.event,
230				code: params.code,
231				modifiers: params.modifiers,
232			};
233			(_API.SciterProcX)(wnd, &msg.header as *const _)
234		},
235
236		Message::Redraw => {
237			use std::ptr;
238			let msg = SCITER_X_MSG_PAINT {
239				header: SCITER_X_MSG_CODE::SXM_PAINT.into(),
240				element: ptr::null_mut(),
241				isFore: true as BOOL,
242				targetType: SCITER_PAINT_TARGET_TYPE::SPT_DEFAULT,
243				context: ptr::null_mut(),
244				callback: None,
245			};
246			(_API.SciterProcX)(wnd, &msg.header as *const _)
247		},
248
249		Message::Paint(paint) => {
250			let msg = SCITER_X_MSG_PAINT {
251				header: SCITER_X_MSG_CODE::SXM_PAINT.into(),
252				element: paint.element,
253				isFore: paint.is_foreground as BOOL,
254				targetType: SCITER_PAINT_TARGET_TYPE::SPT_DEFAULT,
255				context: std::ptr::null_mut(),
256				callback: None,
257			};
258			(_API.SciterProcX)(wnd, &msg.header as *const _)
259		},
260
261		#[cfg(windows)]
262		Message::RenderToDxgiSurface(paint) => {
263			let layer = paint.layer.unwrap_or(PaintLayer {
264				element: std::ptr::null_mut(),
265				is_foreground: false,
266			});
267
268			let msg = SCITER_X_MSG_PAINT {
269				header: SCITER_X_MSG_CODE::SXM_PAINT.into(),
270				element: layer.element,
271				isFore: layer.is_foreground as BOOL,
272				targetType: SCITER_PAINT_TARGET_TYPE::SPT_SURFACE,
273				context: paint.surface,
274				callback: None,
275			};
276			(_API.SciterProcX)(wnd, &msg.header as *const _)
277		},
278
279		Message::RenderTo(paint) => {
280
281			struct Callback {
282				callback: Box<dyn Fn(&RECT, &[u8])>,
283			}
284
285			extern "system" fn inner(rgba: LPCBYTE, x: INT, y: INT, width: UINT, height: UINT, param: LPVOID)
286			{
287				assert!(!param.is_null());
288				assert!(!rgba.is_null());
289				if param.is_null() || rgba.is_null() { return; }
290
291				let bitmap_area = RECT {
292					left: x,
293					top: y,
294					right: x + width as INT,
295					bottom: y + height as INT,
296				};
297
298				let bitmap_size = width * height * 4;
299				let bitmap_data = unsafe { std::slice::from_raw_parts(rgba, bitmap_size as usize) };
300
301				let param = param as *const Callback;
302				let wrapper = unsafe { &*param };
303				(wrapper.callback)(&bitmap_area, bitmap_data);
304			}
305
306			let wrapper = Callback {
307				callback: paint.callback,
308			};
309			let param = &wrapper as *const _ as LPVOID;
310
311			let layer = paint.layer.unwrap_or(PaintLayer {
312				element: std::ptr::null_mut(),
313				is_foreground: false,
314			});
315
316			let msg = SCITER_X_MSG_PAINT {
317				header: SCITER_X_MSG_CODE::SXM_PAINT.into(),
318				element: layer.element,
319				isFore: layer.is_foreground as BOOL,
320				targetType: SCITER_PAINT_TARGET_TYPE::SPT_RECEIVER,
321				context: param,
322				callback: Some(inner),
323			};
324			(_API.SciterProcX)(wnd, &msg.header as *const _)
325		},
326
327	};
328
329	ok != 0
330}