1
0

add icon to executable

This commit is contained in:
Rokas Puzonas 2023-02-05 14:39:17 +02:00
parent 510b28bde5
commit 143a96463c
11 changed files with 194 additions and 29 deletions

11
Cargo.lock generated
View File

@ -1310,6 +1310,8 @@ dependencies = [
"serde",
"toml",
"ureq",
"windows 0.43.0",
"winres",
]
[[package]]
@ -2928,6 +2930,15 @@ dependencies = [
"x11-dl",
]
[[package]]
name = "winres"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b68db261ef59e9e52806f688020631e987592bd83619edccda9c47d42cde4f6c"
dependencies = [
"toml",
]
[[package]]
name = "wio"
version = "0.2.2"

View File

@ -2,6 +2,10 @@
name = "ktu-timetable"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[build-dependencies]
winres = "0.1"
[profile.release]
opt-level = 'z' # Optimize for size
@ -22,4 +26,14 @@ lazy-regex = "2.4.1"
directories-next = "2.0.0"
toml = "0.5.11"
serde = { version = "1.0.152", features = ["derive"]}
lazy_static = "1.4.0"
lazy_static = "1.4.0"
winres = "0.1.12"
[dependencies.windows]
version = "0.43.0"
features = [
"Win32_UI_WindowsAndMessaging",
"Win32_Foundation",
"Win32_System_LibraryLoader",
"Win32_Graphics_Gdi"
]

BIN
assets/icon.ase Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

BIN
assets/small-icon.ase Normal file

Binary file not shown.

9
build.rs Normal file
View File

@ -0,0 +1,9 @@
extern crate winres;
fn main() {
if cfg!(target_os = "windows") {
let mut res = winres::WindowsResource::new();
res.set_icon_with_id("assets/icon.ico", "window-icon");
res.compile().unwrap();
}
}

15
src/environment.rs Normal file
View File

@ -0,0 +1,15 @@
use crate::{timetable::{TimetableGetter, BlockingTimetableGetter}, config::{ConfigStore, TomlConfigStore}};
pub struct Environment {
pub timetable_getter: Box<dyn TimetableGetter>,
pub config_store: Box<dyn ConfigStore>
}
impl Default for Environment {
fn default() -> Self {
Self {
config_store: Box::new(TomlConfigStore::default()),
timetable_getter: Box::new(BlockingTimetableGetter::default())
}
}
}

View File

@ -5,13 +5,14 @@ mod app;
mod config;
mod events_table;
mod utils;
mod platforms;
mod environment;
#[macro_use]
extern crate lazy_static;
use app::MainApp;
use config::TomlConfigStore;
use eframe::egui;
use environment::Environment;
use timetable::BlockingTimetableGetter;
// TODO: show errors when loading config
@ -19,7 +20,7 @@ use timetable::BlockingTimetableGetter;
// TODO: use "confy" for config loading?
// TODO: Setup pipeline
fn main() -> Result<(), ureq::Error> {
fn main() {
let config_store = TomlConfigStore::default();
// let config_store = MemoryConfigStore::new(Config {
// vidko: None//Some("E1810".into())
@ -41,29 +42,9 @@ fn main() -> Result<(), ureq::Error> {
// ]
// });
let mut native_options = eframe::NativeOptions::default();
native_options.decorated = true;
native_options.resizable = true;
native_options.min_window_size = Some(egui::vec2(480.0, 320.0));
native_options.initial_window_size = Some(egui::vec2(500.0, 320.0));
native_options.icon_data = Some(eframe::IconData {
rgba: image::load_from_memory(include_bytes!("../assets/icon.png"))
.expect("Failed to load icon")
.into_rgb8()
.into_raw(),
width: 32,
height: 32,
});
let mut app = MainApp::new(Box::new(config_store), Box::new(timetable_getter));
eframe::run_native(
"KTU timetable",
native_options,
Box::new(move |cc| {
app.init(cc);
Box::new(app)
})
);
Ok(())
platforms::run_windows_app(Environment {
timetable_getter: Box::new(timetable_getter),
config_store: Box::new(config_store)
})
}

3
src/platforms/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod win;
pub use win::run_windows_app;

132
src/platforms/win.rs Normal file
View File

@ -0,0 +1,132 @@
use eframe::IconData;
use windows::{
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,
},
},
};
use crate::{environment::Environment, app::MainApp};
// Yoinked from https://github.com/emilk/egui/issues/920#issuecomment-1364446538
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!("window-icon"),
IMAGE_ICON,
64,
64,
LR_DEFAULTCOLOR,
)
.expect("Failed to load icon");
let mut icon_info = ICONINFO::default();
let res = GetIconInfo(HICON(icon.0), &mut icon_info as *mut _).as_bool();
if !res {
panic!("Failed to load icon info");
}
let mut bitmap = BITMAP::default();
GetObjectA(
icon_info.hbmColor,
std::mem::size_of::<BITMAP>() 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::<u8>::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::<BITMAPINFOHEADER>() 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;
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 as *mut _,
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() as usize / 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,
}
}
pub fn run_windows_app(env: Environment) {
let mut native_options = eframe::NativeOptions::default();
native_options.decorated = true;
native_options.resizable = true;
native_options.min_window_size = Some(egui::vec2(480.0, 320.0));
native_options.initial_window_size = Some(egui::vec2(500.0, 320.0));
native_options.icon_data = Some(load_app_icon());
let mut app = MainApp::new(env.config_store, env.timetable_getter);
eframe::run_native(
"KTU timetable",
native_options,
Box::new(move |cc| {
app.init(cc);
Box::new(app)
})
);
}