From e3aef4a0e9d26e6e810cdef9d7e69b8b2f1d6517 Mon Sep 17 00:00:00 2001 From: Cherryleafroad <13651622+MolotovCherry@users.noreply.github.com> Date: Tue, 19 Sep 2023 22:29:35 -0700 Subject: [PATCH] Use icon in window title/taskbar --- virtual-display-driver-control/Cargo.toml | 3 + virtual-display-driver-control/build.rs | 2 +- .../src/load_icon.rs | 110 ++++++++++++++++++ virtual-display-driver-control/src/main.rs | 5 +- 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 virtual-display-driver-control/src/load_icon.rs diff --git a/virtual-display-driver-control/Cargo.toml b/virtual-display-driver-control/Cargo.toml index 0cf2d79b..b339f8ab 100644 --- a/virtual-display-driver-control/Cargo.toml +++ b/virtual-display-driver-control/Cargo.toml @@ -20,6 +20,9 @@ features = [ "Win32_UI_WindowsAndMessaging", "Win32_UI_Input_KeyboardAndMouse", "Win32_Foundation", + + "Win32_System_LibraryLoader", + "Win32_Graphics_Gdi", ] [build-dependencies] diff --git a/virtual-display-driver-control/build.rs b/virtual-display-driver-control/build.rs index d769c2ff..d1e6bc8a 100644 --- a/virtual-display-driver-control/build.rs +++ b/virtual-display-driver-control/build.rs @@ -2,7 +2,7 @@ fn main() { if cfg!(target_os = "windows") { let mut res = winres::WindowsResource::new(); - res.set_icon("icon.ico"); + res.set_icon_with_id("icon.ico", "virtual"); // allow high dpi scaling res.set_manifest(r#" diff --git a/virtual-display-driver-control/src/load_icon.rs b/virtual-display-driver-control/src/load_icon.rs new file mode 100644 index 00000000..5a085efe --- /dev/null +++ b/virtual-display-driver-control/src/load_icon.rs @@ -0,0 +1,110 @@ +use eframe::IconData; +use windows::{ + core::w, + Win32::{ + Graphics::Gdi::{ + CreateCompatibleDC, DeleteDC, GetDIBits, GetObjectA, SelectObject, BITMAP, BITMAPINFO, + BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, + }, + System::LibraryLoader::GetModuleHandleW, + UI::WindowsAndMessaging::{ + GetIconInfo, LoadImageW, HICON, ICONINFO, IMAGE_ICON, LR_DEFAULTCOLOR, + }, + }, +}; + +// Grab the icon from the exe and hand it over to egui +pub fn load_app_icon() -> IconData { + let (mut buffer, width, height) = unsafe { + let h_instance = GetModuleHandleW(None).expect("Failed to get HINSTANCE"); + let icon = LoadImageW( + h_instance, + w!("virtual"), + IMAGE_ICON, + 512, + 512, + LR_DEFAULTCOLOR, + ) + .expect("Failed to load icon"); + + let mut icon_info = ICONINFO::default(); + let res = GetIconInfo(HICON(icon.0), &mut icon_info as *mut _); + if res.is_err() { + panic!("Failed to load icon info"); + } + + let mut bitmap = BITMAP::default(); + GetObjectA( + icon_info.hbmColor, + std::mem::size_of::() as i32, + Some(&mut bitmap as *mut _ as *mut _), + ); + + let width = bitmap.bmWidth; + let height = bitmap.bmHeight; + + let b_size = (width * height * 4) as usize; + let mut buffer = Vec::::with_capacity(b_size); + + let h_dc = CreateCompatibleDC(None); + let h_bitmap = SelectObject(h_dc, icon_info.hbmColor); + + let mut bitmap_info = BITMAPINFO::default(); + bitmap_info.bmiHeader.biSize = std::mem::size_of::() as u32; + bitmap_info.bmiHeader.biWidth = width; + bitmap_info.bmiHeader.biHeight = height; + bitmap_info.bmiHeader.biPlanes = 1; + bitmap_info.bmiHeader.biBitCount = 32; + bitmap_info.bmiHeader.biCompression = BI_RGB.0; + bitmap_info.bmiHeader.biSizeImage = 0; + + let res = GetDIBits( + h_dc, + icon_info.hbmColor, + 0, + height as u32, + Some(buffer.spare_capacity_mut().as_mut_ptr() as *mut _), + &mut bitmap_info, + DIB_RGB_COLORS, + ); + if res == 0 { + panic!("Failed to get RGB DI bits"); + } + + SelectObject(h_dc, h_bitmap); + DeleteDC(h_dc); + + assert_eq!( + bitmap_info.bmiHeader.biSizeImage as usize, b_size, + "returned biSizeImage must equal to b_size" + ); + + // set the new size + buffer.set_len(bitmap_info.bmiHeader.biSizeImage as usize); + + (buffer, width as u32, height as u32) + }; + + // RGBA -> BGRA + for pixel in buffer.as_mut_slice().chunks_mut(4) { + pixel.swap(0, 2); + } + + // Flip the image vertically + let row_size = width as usize * 4; // number of pixels in each row + let row_count = buffer.len() / row_size; // number of rows in the image + for row in 0..row_count / 2 { + // loop through half of the rows + let start = row * row_size; // index of the start of the current row + let end = (row_count - row - 1) * row_size; // index of the end of the current row + for i in 0..row_size { + buffer.swap(start + i, end + i); + } + } + + IconData { + rgba: buffer, + width, + height, + } +} diff --git a/virtual-display-driver-control/src/main.rs b/virtual-display-driver-control/src/main.rs index af437f7b..8ca5eec5 100644 --- a/virtual-display-driver-control/src/main.rs +++ b/virtual-display-driver-control/src/main.rs @@ -4,6 +4,7 @@ mod app; #[cfg(debug_assertions)] mod backtrace; mod ipc; +mod load_icon; mod monitor; mod panic; mod popup; @@ -16,12 +17,14 @@ use std::error::Error; use eframe::{epaint::Vec2, NativeOptions}; use panic::set_hook; +use self::load_icon::load_app_icon; + fn main() -> Result<(), Box> { set_hook(); let options = NativeOptions { //min_window_size: Some(Vec2::new(500.0, 400.0)), - icon_data: None, + icon_data: Some(load_app_icon()), initial_window_size: Some(Vec2::new(1000.0, 800.0)), transparent: true, resizable: true,