mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-24 04:12:20 +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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ad46a0e6c9bc688823a742aa969b5c08fdc56c2a436ee00d5c6fbcb5982c55c4"
|
checksum = "ad46a0e6c9bc688823a742aa969b5c08fdc56c2a436ee00d5c6fbcb5982c55c4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libm",
|
"libm 0.2.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3493,6 +3493,12 @@ dependencies = [
|
|||||||
"windows-sys 0.48.0",
|
"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]]
|
[[package]]
|
||||||
name = "libm"
|
name = "libm"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
@ -3813,6 +3819,22 @@ dependencies = [
|
|||||||
"tempfile",
|
"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]]
|
[[package]]
|
||||||
name = "ndk"
|
name = "ndk"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -3891,6 +3913,15 @@ dependencies = [
|
|||||||
"log",
|
"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]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.23.2"
|
version = "0.23.2"
|
||||||
@ -4574,6 +4605,24 @@ dependencies = [
|
|||||||
"time 0.3.30",
|
"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]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.17.10"
|
version = "0.17.10"
|
||||||
@ -5390,6 +5439,7 @@ dependencies = [
|
|||||||
"brotli",
|
"brotli",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"md5",
|
"md5",
|
||||||
|
"native-windows-gui",
|
||||||
"winapi 0.3.9",
|
"winapi 0.3.9",
|
||||||
"winres",
|
"winres",
|
||||||
]
|
]
|
||||||
@ -5948,6 +5998,16 @@ version = "0.2.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82"
|
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]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
|
@ -14,6 +14,9 @@ dirs = "5.0"
|
|||||||
md5 = "0.7"
|
md5 = "0.7"
|
||||||
winapi = { version = "0.3", features = ["winbase"] }
|
winapi = { version = "0.3", features = ["winbase"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "windows")'.dependencies]
|
||||||
|
native-windows-gui = "1.0"
|
||||||
|
|
||||||
[package.metadata.winres]
|
[package.metadata.winres]
|
||||||
LegalCopyright = "Copyright © 2024 Purslane Ltd. All rights reserved."
|
LegalCopyright = "Copyright © 2024 Purslane Ltd. All rights reserved."
|
||||||
ProductName = "RustDesk"
|
ProductName = "RustDesk"
|
||||||
|
@ -8,6 +8,8 @@ use std::{
|
|||||||
use bin_reader::BinaryReader;
|
use bin_reader::BinaryReader;
|
||||||
|
|
||||||
pub mod bin_reader;
|
pub mod bin_reader;
|
||||||
|
#[cfg(windows)]
|
||||||
|
mod ui;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const APP_METADATA: &[u8] = include_bytes!("../app_metadata.toml");
|
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 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");
|
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();
|
let reader = BinaryReader::default();
|
||||||
if let Some(exe) = setup(
|
if let Some(exe) = setup(
|
||||||
reader,
|
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