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

321 lines
9.0 KiB
Rust

//! Windowless mode example (for Sciter.Lite build).
extern crate sciter;
extern crate winit;
extern crate winapi;
extern crate raw_window_handle;
fn main() {
// "Windowless" Sciter builds are incompatible with the regular ones.
if !cfg!(feature = "windowless") {
panic!("This example requires \"windowless\" feature!");
}
// We need this to explicitly set path to the windowless sciter dll.
if !cfg!(feature = "dynamic") {
panic!("This example requires the \"dynamic\" feature enabled.")
}
if let Some(arg) = std::env::args().nth(1) {
println!("loading sciter from {:?}", arg);
if let Err(_) = sciter::set_options(sciter::RuntimeOptions::LibraryPath(&arg)) {
panic!("Invalid sciter-lite dll specified.");
}
}
// prepare and create a new window
println!("create window");
let mut events = winit::EventsLoop::new();
use raw_window_handle::HasRawWindowHandle;
let wnd = winit::WindowBuilder::new();
let wnd = wnd.build(&events).expect("Failed to create window");
let window_handle = wnd.raw_window_handle();
// configure Sciter
println!("create sciter instance");
sciter::set_options(sciter::RuntimeOptions::UxTheming(true)).unwrap();
sciter::set_options(sciter::RuntimeOptions::DebugMode(true)).unwrap();
sciter::set_options(sciter::RuntimeOptions::ScriptFeatures(0xFF)).unwrap();
// create an engine instance with an opaque pointer as an identifier
use sciter::windowless::{Message, handle_message};
let scwnd = { &wnd as *const _ as sciter::types::HWINDOW };
handle_message(scwnd, Message::Create { backend: sciter::types::GFX_LAYER::SKIA_OPENGL, transparent: false, });
#[cfg(windows)]
{
// Windows-specific: we need to redraw window in responce to the corresponding notification.
// winit 0.20 has an explicit `Window::request_redraw` method,
// here we use `winapi::InvalidateRect` for this.
struct WindowlessHandler {
hwnd: winapi::shared::windef::HWND,
}
impl sciter::HostHandler for WindowlessHandler {
fn on_invalidate(&mut self, pnm: &sciter::host::SCN_INVALIDATE_RECT) {
unsafe {
let rc = &pnm.invalid_rect;
let dst = winapi::shared::windef::RECT {
left: rc.left,
top: rc.top,
right: rc.right,
bottom: rc.bottom,
};
winapi::um::winuser::InvalidateRect(self.hwnd, &dst as *const _, 0);
// println!("- {} {}", rc.width(), rc.height());
}
}
}
let handler = WindowlessHandler {
hwnd: match window_handle {
raw_window_handle::RawWindowHandle::Windows(data) => data.hwnd as winapi::shared::windef::HWND,
_ => unreachable!(),
},
};
let instance = sciter::Host::attach_with(scwnd, handler);
let html = include_bytes!("minimal.htm");
instance.load_html(html, Some("example://minimal.htm"));
}
// events processing
use sciter::windowless::{MouseEvent, KeyboardEvent, RenderEvent};
use sciter::windowless::{MOUSE_BUTTONS, MOUSE_EVENTS, KEYBOARD_STATES, KEY_EVENTS};
let mut mouse_button = MOUSE_BUTTONS::NONE;
let mut mouse_pos = (0, 0);
let as_keys = |modifiers: winit::ModifiersState| {
let mut keys = 0;
if modifiers.ctrl {
keys |= 0x01;
}
if modifiers.shift {
keys |= 0x02;
}
if modifiers.alt {
keys |= 0x04;
}
KEYBOARD_STATES::from(keys)
};
println!("running...");
use winit::{Event, WindowEvent};
let skip = ();
let mut poll_break = false;
let startup = std::time::Instant::now();
loop {
// release CPU a bit, hackish
std::thread::sleep(std::time::Duration::from_millis(0));
// Sciter processes timers and fading effects here
handle_message(scwnd, Message::Heartbit {
milliseconds: std::time::Instant::now().duration_since(startup).as_millis() as u32,
});
// the actual event loop polling
events.poll_events(|event: winit::Event| {
match event {
Event::WindowEvent { event, window_id: _ } => {
match event {
WindowEvent::Destroyed => {
// never called due to loop break on close
println!("destroy");
handle_message(scwnd, Message::Destroy);
poll_break = true;
},
WindowEvent::CloseRequested => {
println!("close");
poll_break = true;
},
WindowEvent::Resized(size) => {
// println!("{:?}, size: {:?}", event, size);
let (width, height): (u32, u32) = size.into();
handle_message(scwnd, Message::Size { width, height });
skip
},
WindowEvent::Refresh => {
let on_render = move |bitmap_area: &sciter::types::RECT, bitmap_data: &[u8]|
{
#[cfg(unix)]
{
let _ = bitmap_area;
let _ = bitmap_data;
let _ = window_handle;
}
// Windows-specific bitmap rendering on the window
#[cfg(windows)]
{
use winapi::um::winuser::*;
use winapi::um::wingdi::*;
use winapi::shared::minwindef::LPVOID;
let hwnd = match window_handle {
raw_window_handle::RawWindowHandle::Windows(data) => data.hwnd as winapi::shared::windef::HWND,
_ => unreachable!(),
};
unsafe {
// NOTE: we use `GetDC` here instead of `BeginPaint`, because the way
// winit 0.19 processed the `WM_PAINT` message (it always calls `DefWindowProcW`).
// let mut ps = PAINTSTRUCT::default();
// let hdc = BeginPaint(hwnd, &mut ps as *mut _);
let hdc = GetDC(hwnd);
let (w, h) = (bitmap_area.width(), bitmap_area.height());
let mem_dc = CreateCompatibleDC(hdc);
let mem_bm = CreateCompatibleBitmap(hdc, w, h);
let mut bmi = BITMAPINFO::default();
{
let mut info = &mut bmi.bmiHeader;
info.biSize = std::mem::size_of::<BITMAPINFO>() as u32;
info.biWidth = w;
info.biHeight = -h;
info.biPlanes = 1;
info.biBitCount = 32;
}
let old_bm = SelectObject(mem_dc, mem_bm as LPVOID);
let _copied = StretchDIBits(mem_dc, 0, 0, w, h, 0, 0, w, h, bitmap_data.as_ptr() as *const _, &bmi as *const _, 0, SRCCOPY);
let _ok = BitBlt(hdc, 0, 0, w, h, mem_dc, 0, 0, SRCCOPY);
SelectObject(mem_dc, old_bm);
// EndPaint(hwnd, &ps as *const _);
ReleaseDC(hwnd, hdc);
// println!("+ {} {}", w, h);
}
}
};
let cb = RenderEvent {
layer: None,
callback: Box::new(on_render),
};
handle_message(scwnd, Message::RenderTo(cb));
skip
},
WindowEvent::Focused(enter) => {
println!("focus {}", enter);
handle_message(scwnd, Message::Focus { enter });
skip
},
WindowEvent::CursorEntered { device_id: _ } => {
println!("mouse enter");
let event = MouseEvent {
event: MOUSE_EVENTS::MOUSE_ENTER,
button: mouse_button,
modifiers: KEYBOARD_STATES::from(0),
pos: sciter::types::POINT {
x: mouse_pos.0,
y: mouse_pos.1,
},
};
handle_message(scwnd, Message::Mouse(event));
skip
},
WindowEvent::CursorLeft { device_id: _ } => {
println!("mouse leave");
let event = MouseEvent {
event: MOUSE_EVENTS::MOUSE_LEAVE,
button: mouse_button,
modifiers: KEYBOARD_STATES::from(0),
pos: sciter::types::POINT {
x: mouse_pos.0,
y: mouse_pos.1,
},
};
handle_message(scwnd, Message::Mouse(event));
skip
},
WindowEvent::CursorMoved { device_id: _, position, modifiers } => {
mouse_pos = position.into();
let event = MouseEvent {
event: MOUSE_EVENTS::MOUSE_MOVE,
button: mouse_button,
modifiers: as_keys(modifiers),
pos: sciter::types::POINT {
x: mouse_pos.0,
y: mouse_pos.1,
},
};
handle_message(scwnd, Message::Mouse(event));
skip
},
WindowEvent::MouseInput { device_id: _, state, button, modifiers } => {
mouse_button = match button {
winit::MouseButton::Left => MOUSE_BUTTONS::MAIN,
winit::MouseButton::Right => MOUSE_BUTTONS::PROP,
winit::MouseButton::Middle => MOUSE_BUTTONS::MIDDLE,
_ => MOUSE_BUTTONS::NONE,
};
println!("mouse {:?} as {:?}", mouse_button, mouse_pos);
let event = MouseEvent {
event: if state == winit::ElementState::Pressed { MOUSE_EVENTS::MOUSE_DOWN } else { MOUSE_EVENTS::MOUSE_UP },
button: mouse_button,
modifiers: as_keys(modifiers),
pos: sciter::types::POINT {
x: mouse_pos.0,
y: mouse_pos.1,
},
};
handle_message(scwnd, Message::Mouse(event));
skip
},
WindowEvent::KeyboardInput { device_id: _, input } => {
println!("key {} {}", input.scancode, if input.state == winit::ElementState::Pressed { "down" } else { "up" });
let event = KeyboardEvent {
event: if input.state == winit::ElementState::Pressed { KEY_EVENTS::KEY_DOWN } else { KEY_EVENTS::KEY_UP },
code: input.scancode,
modifiers: as_keys(input.modifiers),
};
handle_message(scwnd, Message::Keyboard(event));
skip
},
_ => (),
}
},
_ => (),
}
});
if poll_break {
break;
}
}
println!("done, quit");
}