//! 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::() 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"); }