mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-12-24 12:24:07 +08:00
321 lines
9.0 KiB
Rust
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");
|
||
|
}
|