mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-23 19:49:05 +08:00
windows portable loading ui (#8490)
Signed-off-by: 21pages <sunboeasy@gmail.com>
This commit is contained in:
parent
ef06b7d5d0
commit
12d3c59172
62
Cargo.lock
generated
62
Cargo.lock
generated
@ -2139,7 +2139,7 @@ version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad46a0e6c9bc688823a742aa969b5c08fdc56c2a436ee00d5c6fbcb5982c55c4"
|
||||
dependencies = [
|
||||
"libm",
|
||||
"libm 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3493,6 +3493,12 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
version = "0.2.8"
|
||||
@ -3813,6 +3819,22 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-windows-gui"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f7003a669f68deb6b7c57d74fff4f8e533c44a3f0b297492440ef4ff5a28454"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"lazy_static",
|
||||
"newline-converter",
|
||||
"plotters",
|
||||
"plotters-backend",
|
||||
"stretch",
|
||||
"winapi 0.3.9",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.7.0"
|
||||
@ -3891,6 +3913,15 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "newline-converter"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f71d09d5c87634207f894c6b31b6a2b2c64ea3bdcf71bd5599fdbbe1600c00f"
|
||||
dependencies = [
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.23.2"
|
||||
@ -4574,6 +4605,24 @@ dependencies = [
|
||||
"time 0.3.30",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a15b6eccb8484002195a3e44fe65a4ce8e93a625797a063735536fd59cb01cf3"
|
||||
dependencies = [
|
||||
"num-traits 0.2.17",
|
||||
"plotters-backend",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414cec62c6634ae900ea1c56128dfe87cf63e7caece0852ec76aba307cebadb7"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.10"
|
||||
@ -5390,6 +5439,7 @@ dependencies = [
|
||||
"brotli",
|
||||
"dirs 5.0.1",
|
||||
"md5",
|
||||
"native-windows-gui",
|
||||
"winapi 0.3.9",
|
||||
"winres",
|
||||
]
|
||||
@ -5948,6 +5998,16 @@ version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
|
||||
|
||||
[[package]]
|
||||
name = "stretch"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b0dc6d20ce137f302edf90f9cd3d278866fd7fb139efca6f246161222ad6d87"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libm 0.1.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
|
@ -14,6 +14,9 @@ dirs = "5.0"
|
||||
md5 = "0.7"
|
||||
winapi = { version = "0.3", features = ["winbase"] }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
native-windows-gui = "1.0"
|
||||
|
||||
[package.metadata.winres]
|
||||
LegalCopyright = "Copyright © 2024 Purslane Ltd. All rights reserved."
|
||||
ProductName = "RustDesk"
|
||||
|
@ -8,6 +8,8 @@ use std::{
|
||||
use bin_reader::BinaryReader;
|
||||
|
||||
pub mod bin_reader;
|
||||
#[cfg(windows)]
|
||||
mod ui;
|
||||
|
||||
#[cfg(windows)]
|
||||
const APP_METADATA: &[u8] = include_bytes!("../app_metadata.toml");
|
||||
@ -119,6 +121,11 @@ fn main() {
|
||||
let click_setup = args.is_empty() && arg_exe.to_lowercase().ends_with("install.exe");
|
||||
let quick_support = args.is_empty() && arg_exe.to_lowercase().ends_with("qs.exe");
|
||||
|
||||
#[cfg(windows)]
|
||||
if args.is_empty() {
|
||||
ui::setup();
|
||||
}
|
||||
|
||||
let reader = BinaryReader::default();
|
||||
if let Some(exe) = setup(
|
||||
reader,
|
||||
|
BIN
libs/portable/src/res/label.png
Normal file
BIN
libs/portable/src/res/label.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
BIN
libs/portable/src/res/spin.gif
Normal file
BIN
libs/portable/src/res/spin.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 58 KiB |
232
libs/portable/src/ui.rs
Normal file
232
libs/portable/src/ui.rs
Normal file
@ -0,0 +1,232 @@
|
||||
use native_windows_gui as nwg;
|
||||
use nwg::NativeUi;
|
||||
use std::cell::RefCell;
|
||||
|
||||
const GIF_DATA: &[u8] = include_bytes!("./res/spin.gif");
|
||||
const LABEL_DATA: &[u8] = include_bytes!("./res/label.png");
|
||||
const GIF_SIZE: i32 = 32;
|
||||
const BG_COLOR: [u8; 3] = [90, 90, 120];
|
||||
const BORDER_COLOR: [u8; 3] = [40, 40, 40];
|
||||
const GIF_DELAY: u64 = 30;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BasicApp {
|
||||
window: nwg::Window,
|
||||
|
||||
border_image: nwg::ImageFrame,
|
||||
bg_image: nwg::ImageFrame,
|
||||
gif_image: nwg::ImageFrame,
|
||||
label_image: nwg::ImageFrame,
|
||||
|
||||
border_layout: nwg::GridLayout,
|
||||
bg_layout: nwg::GridLayout,
|
||||
inner_layout: nwg::GridLayout,
|
||||
|
||||
timer: nwg::AnimationTimer,
|
||||
decoder: nwg::ImageDecoder,
|
||||
gif_index: RefCell<usize>,
|
||||
gif_images: RefCell<Vec<nwg::Bitmap>>,
|
||||
}
|
||||
|
||||
impl BasicApp {
|
||||
fn exit(&self) {
|
||||
self.timer.stop();
|
||||
nwg::stop_thread_dispatch();
|
||||
}
|
||||
|
||||
fn load_gif(&self) -> Result<(), nwg::NwgError> {
|
||||
let image_source = self.decoder.from_stream(GIF_DATA)?;
|
||||
for frame_index in 0..image_source.frame_count() {
|
||||
let image_data = image_source.frame(frame_index)?;
|
||||
let image_data = self
|
||||
.decoder
|
||||
.resize_image(&image_data, [GIF_SIZE as u32, GIF_SIZE as u32])?;
|
||||
let bmp = image_data.as_bitmap()?;
|
||||
self.gif_images.borrow_mut().push(bmp);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn update_gif(&self) -> Result<(), nwg::NwgError> {
|
||||
let images = self.gif_images.borrow();
|
||||
if images.len() == 0 {
|
||||
return Err(nwg::NwgError::ImageDecoderError(
|
||||
-1,
|
||||
"no gif images".to_string(),
|
||||
));
|
||||
}
|
||||
let image_index = *self.gif_index.borrow() % images.len();
|
||||
self.gif_image.set_bitmap(Some(&images[image_index]));
|
||||
*self.gif_index.borrow_mut() = (image_index + 1) % images.len();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_timer(&self) {
|
||||
self.timer.start();
|
||||
}
|
||||
}
|
||||
|
||||
mod basic_app_ui {
|
||||
use super::*;
|
||||
use native_windows_gui::{self as nwg, Bitmap};
|
||||
use nwg::{Event, GridLayoutItem};
|
||||
use std::cell::RefCell;
|
||||
use std::ops::Deref;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct BasicAppUi {
|
||||
inner: Rc<BasicApp>,
|
||||
default_handler: RefCell<Vec<nwg::EventHandler>>,
|
||||
}
|
||||
|
||||
impl nwg::NativeUi<BasicAppUi> for BasicApp {
|
||||
fn build_ui(mut data: BasicApp) -> Result<BasicAppUi, nwg::NwgError> {
|
||||
data.decoder = nwg::ImageDecoder::new()?;
|
||||
let col_cnt: i32 = 7;
|
||||
let row_cnt: i32 = 3;
|
||||
let border_width: i32 = 1;
|
||||
let window_size = (
|
||||
GIF_SIZE * col_cnt + 2 * border_width,
|
||||
GIF_SIZE * row_cnt + 2 * border_width,
|
||||
);
|
||||
|
||||
// Controls
|
||||
nwg::Window::builder()
|
||||
.flags(nwg::WindowFlags::POPUP | nwg::WindowFlags::VISIBLE)
|
||||
.size(window_size)
|
||||
.center(true)
|
||||
.build(&mut data.window)?;
|
||||
|
||||
nwg::ImageFrame::builder()
|
||||
.parent(&data.window)
|
||||
.size(window_size)
|
||||
.background_color(Some(BORDER_COLOR))
|
||||
.build(&mut data.border_image)?;
|
||||
|
||||
nwg::ImageFrame::builder()
|
||||
.parent(&data.border_image)
|
||||
.size((row_cnt * GIF_SIZE, col_cnt * GIF_SIZE))
|
||||
.background_color(Some(BG_COLOR))
|
||||
.build(&mut data.bg_image)?;
|
||||
|
||||
nwg::ImageFrame::builder()
|
||||
.parent(&data.bg_image)
|
||||
.size((GIF_SIZE, GIF_SIZE))
|
||||
.background_color(Some(BG_COLOR))
|
||||
.build(&mut data.gif_image)?;
|
||||
|
||||
nwg::ImageFrame::builder()
|
||||
.parent(&data.bg_image)
|
||||
.background_color(Some(BG_COLOR))
|
||||
.bitmap(Some(&Bitmap::from_bin(LABEL_DATA)?))
|
||||
.build(&mut data.label_image)?;
|
||||
|
||||
nwg::AnimationTimer::builder()
|
||||
.parent(&data.window)
|
||||
.interval(std::time::Duration::from_millis(GIF_DELAY))
|
||||
.build(&mut data.timer)?;
|
||||
|
||||
// Wrap-up
|
||||
let ui = BasicAppUi {
|
||||
inner: Rc::new(data),
|
||||
default_handler: Default::default(),
|
||||
};
|
||||
|
||||
// Layouts
|
||||
nwg::GridLayout::builder()
|
||||
.parent(&ui.window)
|
||||
.spacing(0)
|
||||
.margin([0, 0, 0, 0])
|
||||
.max_column(Some(1))
|
||||
.max_row(Some(1))
|
||||
.child_item(GridLayoutItem::new(&ui.border_image, 0, 0, 1, 1))
|
||||
.build(&ui.border_layout)?;
|
||||
|
||||
nwg::GridLayout::builder()
|
||||
.parent(&ui.border_image)
|
||||
.spacing(0)
|
||||
.margin([
|
||||
border_width as _,
|
||||
border_width as _,
|
||||
border_width as _,
|
||||
border_width as _,
|
||||
])
|
||||
.max_column(Some(1))
|
||||
.max_row(Some(1))
|
||||
.child_item(GridLayoutItem::new(&ui.bg_image, 0, 0, 1, 1))
|
||||
.build(&ui.bg_layout)?;
|
||||
|
||||
nwg::GridLayout::builder()
|
||||
.parent(&ui.bg_image)
|
||||
.spacing(0)
|
||||
.margin([0, 0, 0, 0])
|
||||
.max_column(Some(col_cnt as _))
|
||||
.max_row(Some(row_cnt as _))
|
||||
.child_item(GridLayoutItem::new(&ui.gif_image, 2, 1, 1, 1))
|
||||
.child_item(GridLayoutItem::new(&ui.label_image, 3, 1, 3, 1))
|
||||
.build(&ui.inner_layout)?;
|
||||
|
||||
// Events
|
||||
let evt_ui = Rc::downgrade(&ui.inner);
|
||||
let handle_events = move |evt, _evt_data, _handle| {
|
||||
if let Some(evt_ui) = evt_ui.upgrade().as_mut() {
|
||||
match evt {
|
||||
Event::OnWindowClose => {
|
||||
evt_ui.exit();
|
||||
}
|
||||
Event::OnTimerTick => {
|
||||
if let Err(e) = evt_ui.update_gif() {
|
||||
eprintln!("{:?}", e);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ui.default_handler
|
||||
.borrow_mut()
|
||||
.push(nwg::full_bind_event_handler(
|
||||
&ui.window.handle,
|
||||
handle_events,
|
||||
));
|
||||
|
||||
return Ok(ui);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for BasicAppUi {
|
||||
/// To make sure that everything is freed without issues, the default handler must be unbound.
|
||||
fn drop(&mut self) {
|
||||
let mut handlers = self.default_handler.borrow_mut();
|
||||
for handler in handlers.drain(0..) {
|
||||
nwg::unbind_event_handler(&handler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BasicAppUi {
|
||||
type Target = BasicApp;
|
||||
|
||||
fn deref(&self) -> &BasicApp {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ui() -> Result<(), nwg::NwgError> {
|
||||
nwg::init()?;
|
||||
let app = BasicApp::build_ui(Default::default())?;
|
||||
app.load_gif()?;
|
||||
app.start_timer();
|
||||
nwg::dispatch_thread_events();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn setup() {
|
||||
std::thread::spawn(move || {
|
||||
if let Err(e) = ui() {
|
||||
eprintln!("{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
Loading…
Reference in New Issue
Block a user