mirror of
https://github.com/agersant/polaris
synced 2024-11-10 02:04:13 +00:00
Setup Windows UI with inert notification icon
This commit is contained in:
parent
b5dba126d6
commit
a4f118d9e6
10 changed files with 302 additions and 23 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
target
|
||||
release
|
||||
*.dll
|
||||
*.dll
|
||||
*.res
|
55
Cargo.lock
generated
55
Cargo.lock
generated
|
@ -4,15 +4,20 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"id3 0.1.10 (git+https://github.com/jameshurst/rust-id3)",
|
||||
"iron 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (git+https://github.com/retep998/winapi-rs)",
|
||||
"mount 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"oven 1.0.0 (git+https://github.com/agersant/oven?branch=remove_cookie_dep)",
|
||||
"params 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 0.1.77 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"router 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"shell32-sys 0.1.1 (git+https://github.com/retep998/winapi-rs)",
|
||||
"staticfile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"user32-sys 0.2.0 (git+https://github.com/retep998/winapi-rs)",
|
||||
"uuid 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.2.8 (git+https://github.com/retep998/winapi-rs)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -274,6 +279,15 @@ name = "itoa"
|
|||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "git+https://github.com/retep998/winapi-rs#77588710a683e1a4ec085b0f3d6c8939ecc7f030"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (git+https://github.com/retep998/winapi-rs)",
|
||||
"winapi-build 0.1.1 (git+https://github.com/retep998/winapi-rs)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
|
@ -671,6 +685,20 @@ dependencies = [
|
|||
"serde 0.8.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "shell32-sys"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/retep998/winapi-rs#77588710a683e1a4ec085b0f3d6c8939ecc7f030"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (git+https://github.com/retep998/winapi-rs)",
|
||||
"winapi-build 0.1.1 (git+https://github.com/retep998/winapi-rs)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "solicit"
|
||||
version = "0.4.4"
|
||||
|
@ -819,6 +847,15 @@ dependencies = [
|
|||
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "user32-sys"
|
||||
version = "0.2.0"
|
||||
source = "git+https://github.com/retep998/winapi-rs#77588710a683e1a4ec085b0f3d6c8939ecc7f030"
|
||||
dependencies = [
|
||||
"winapi 0.2.8 (git+https://github.com/retep998/winapi-rs)",
|
||||
"winapi-build 0.1.1 (git+https://github.com/retep998/winapi-rs)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "user32-sys"
|
||||
version = "0.2.0"
|
||||
|
@ -841,11 +878,29 @@ dependencies = [
|
|||
"rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "git+https://github.com/retep998/winapi-rs#77588710a683e1a4ec085b0f3d6c8939ecc7f030"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "git+https://github.com/retep998/winapi-rs#77588710a683e1a4ec085b0f3d6c8939ecc7f030"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
|
|
11
Cargo.toml
11
Cargo.toml
|
@ -3,6 +3,9 @@ name = "polaris"
|
|||
version = "0.1.0"
|
||||
authors = ["Antoine Gersant <antoine.gersant@lesforges.org>"]
|
||||
|
||||
[features]
|
||||
ui = []
|
||||
|
||||
[dependencies]
|
||||
id3 = { git = "https://github.com/jameshurst/rust-id3" }
|
||||
iron = "0.4.0"
|
||||
|
@ -14,4 +17,10 @@ router = "0.4.0"
|
|||
rustc-serialize = "0.3"
|
||||
staticfile = "0.3.0"
|
||||
toml = "0.2"
|
||||
url = "1.2.0"
|
||||
url = "1.2.0"
|
||||
|
||||
winapi = { git = "https://github.com/retep998/winapi-rs" }
|
||||
kernel32-sys = { git = "https://github.com/retep998/winapi-rs" }
|
||||
shell32-sys = { git = "https://github.com/retep998/winapi-rs" }
|
||||
user32-sys = { git = "https://github.com/retep998/winapi-rs" }
|
||||
uuid = { version = "0.3.0", features = ["v5"] }
|
17
build_release_windows.ps1
Normal file
17
build_release_windows.ps1
Normal file
|
@ -0,0 +1,17 @@
|
|||
"Compiling resource file"
|
||||
RC /fo res\application.res res\application.rc
|
||||
|
||||
"Compiling executable"
|
||||
cargo rustc --release --features "ui" -- -C link-args="/SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup res\application.res"
|
||||
|
||||
"Creating output directory"
|
||||
New-Item .\release\windows -type directory -Force
|
||||
Remove-Item -Recurse .\release\windows\*
|
||||
|
||||
"Copying to output directory"
|
||||
Copy-Item .\target\release\polaris.exe .\release\windows\
|
||||
Copy-Item .\res\libeay32.dll .\release\windows\
|
||||
Copy-Item .\res\libeay32md.dll .\release\windows\
|
||||
Copy-Item .\web\ .\release\windows\ -recurse
|
||||
|
||||
Read-Host -Prompt "All clear! Press Enter to exit"
|
15
res/application.manifest
Normal file
15
res/application.manifest
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
<asmv3:application>
|
||||
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
|
||||
<dpiAware>true</dpiAware>
|
||||
</asmv3:windowsSettings>
|
||||
</asmv3:application>
|
||||
</assembly>
|
5
res/application.rc
Normal file
5
res/application.rc
Normal file
|
@ -0,0 +1,5 @@
|
|||
#define IDI_POLARIS 0x101
|
||||
|
||||
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "application.manifest"
|
||||
|
||||
IDI_POLARIS ICON "icon_polaris_32.ico"
|
55
src/main.rs
55
src/main.rs
|
@ -10,6 +10,17 @@ extern crate staticfile;
|
|||
extern crate toml;
|
||||
extern crate url;
|
||||
|
||||
#[cfg(windows)]
|
||||
extern crate uuid;
|
||||
#[cfg(windows)]
|
||||
extern crate winapi;
|
||||
#[cfg(windows)]
|
||||
extern crate kernel32;
|
||||
#[cfg(windows)]
|
||||
extern crate shell32;
|
||||
#[cfg(windows)]
|
||||
extern crate user32;
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
@ -21,33 +32,35 @@ use staticfile::Static;
|
|||
mod api;
|
||||
mod collection;
|
||||
mod error;
|
||||
mod ui;
|
||||
mod vfs;
|
||||
|
||||
use api::*;
|
||||
use collection::*;
|
||||
|
||||
fn main() {
|
||||
|
||||
|
||||
let mut api_chain;
|
||||
{
|
||||
let api_handler;
|
||||
println!("Spawning server thread");
|
||||
std::thread::spawn(move || {
|
||||
let mut api_chain;
|
||||
{
|
||||
let mut collection = Collection::new();
|
||||
collection.load_config(Path::new("Polaris.toml")).unwrap();
|
||||
let collection = Arc::new(Mutex::new(collection));
|
||||
api_handler = get_api_handler(collection);
|
||||
let api_handler;
|
||||
{
|
||||
let mut collection = collection::Collection::new();
|
||||
collection.load_config(Path::new("Polaris.toml")).unwrap();
|
||||
let collection = Arc::new(Mutex::new(collection));
|
||||
api_handler = api::get_api_handler(collection);
|
||||
}
|
||||
api_chain = Chain::new(api_handler);
|
||||
|
||||
let auth_secret = std::env::var("POLARIS_SECRET")
|
||||
.expect("Environment variable POLARIS_SECRET must be set");
|
||||
let cookie_middleware = oven::new(auth_secret.into_bytes());
|
||||
api_chain.link(cookie_middleware);
|
||||
}
|
||||
api_chain = Chain::new(api_handler);
|
||||
|
||||
let auth_secret = std::env::var("POLARIS_SECRET").expect("Environment variable POLARIS_SECRET must be set");
|
||||
let cookie_middleware = oven::new(auth_secret.into_bytes());
|
||||
api_chain.link(cookie_middleware);
|
||||
}
|
||||
|
||||
let mut mount = Mount::new();
|
||||
mount.mount("/api/", api_chain);
|
||||
mount.mount("/", Static::new(Path::new("web")));
|
||||
let mut mount = Mount::new();
|
||||
mount.mount("/api/", api_chain);
|
||||
mount.mount("/", Static::new(Path::new("web")));
|
||||
Iron::new(mount).http("localhost:3000").unwrap();
|
||||
});
|
||||
|
||||
Iron::new(mount).http("localhost:3000").unwrap();
|
||||
ui::run();
|
||||
}
|
||||
|
|
9
src/ui/headless.rs
Normal file
9
src/ui/headless.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
use std::time;
|
||||
use std::thread;
|
||||
|
||||
pub fn run() {
|
||||
println!("Starting up UI (headless)");
|
||||
loop {
|
||||
thread::sleep(time::Duration::from_secs(10));
|
||||
}
|
||||
}
|
11
src/ui/mod.rs
Normal file
11
src/ui/mod.rs
Normal file
|
@ -0,0 +1,11 @@
|
|||
#[cfg(all(windows, feature = "ui"))]
|
||||
mod windows;
|
||||
|
||||
#[cfg(all(windows, feature = "ui"))]
|
||||
pub use self::windows::*;
|
||||
|
||||
#[cfg(not(all(windows, feature = "ui")))]
|
||||
mod headless;
|
||||
|
||||
#[cfg(not(all(windows, feature = "ui")))]
|
||||
pub use self::headless::*;
|
144
src/ui/windows.rs
Normal file
144
src/ui/windows.rs
Normal file
|
@ -0,0 +1,144 @@
|
|||
use kernel32;
|
||||
use shell32;
|
||||
use std;
|
||||
use std::ffi::OsStr;
|
||||
use std::os::windows::ffi::OsStrExt;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
use user32;
|
||||
use uuid;
|
||||
use winapi;
|
||||
|
||||
const IDI_POLARIS: isize = 0x101;
|
||||
|
||||
pub trait ToWin {
|
||||
type Out;
|
||||
fn to_win(&self) -> Self::Out;
|
||||
}
|
||||
|
||||
impl<'a> ToWin for &'a str {
|
||||
type Out = Vec<u16>;
|
||||
|
||||
fn to_win(&self) -> Self::Out {
|
||||
OsStr::new(self)
|
||||
.encode_wide()
|
||||
.chain(std::iter::once(0))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToWin for uuid::Uuid {
|
||||
type Out = winapi::GUID;
|
||||
|
||||
fn to_win(&self) -> Self::Out {
|
||||
let bytes = self.as_bytes();
|
||||
let end = [bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14],
|
||||
bytes[15]];
|
||||
|
||||
winapi::GUID {
|
||||
Data1: ((bytes[0] as u32) << 24 | (bytes[1] as u32) << 16 | (bytes[2] as u32) << 8 |
|
||||
(bytes[3] as u32)),
|
||||
Data2: ((bytes[4] as u16) << 8 | (bytes[5] as u16)),
|
||||
Data3: ((bytes[6] as u16) << 8 | (bytes[7] as u16)),
|
||||
Data4: end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_window() -> Option<winapi::HWND> {
|
||||
|
||||
let class_name = "Polaris-class".to_win();
|
||||
let window_name = "Polaris-window".to_win();
|
||||
|
||||
unsafe {
|
||||
let module_handle = kernel32::GetModuleHandleW(std::ptr::null());
|
||||
let wnd = winapi::WNDCLASSW {
|
||||
style: 0,
|
||||
lpfnWndProc: Some(window_proc),
|
||||
hInstance: module_handle,
|
||||
hIcon: std::ptr::null_mut(),
|
||||
hCursor: std::ptr::null_mut(),
|
||||
lpszClassName: class_name.as_ptr(),
|
||||
hbrBackground: winapi::COLOR_WINDOW as winapi::HBRUSH,
|
||||
lpszMenuName: std::ptr::null_mut(),
|
||||
cbClsExtra: 0,
|
||||
cbWndExtra: 0,
|
||||
};
|
||||
|
||||
let atom = user32::RegisterClassW(&wnd);
|
||||
if atom == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let window_handle = user32::CreateWindowExW(0,
|
||||
atom as winapi::LPCWSTR,
|
||||
window_name.as_ptr(),
|
||||
winapi::WS_DISABLED,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
user32::GetDesktopWindow(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut(),
|
||||
std::ptr::null_mut());
|
||||
|
||||
if window_handle.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
return Some(window_handle);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_notification_icon(window_handle: winapi::HWND, id: winapi::GUID) {
|
||||
unsafe {
|
||||
let module = kernel32::GetModuleHandleW(std::ptr::null());
|
||||
let icon = user32::LoadIconW(module, std::mem::transmute(IDI_POLARIS));
|
||||
assert!(!icon.is_null());
|
||||
|
||||
let mut icon_data = winapi::NOTIFYICONDATAW {
|
||||
cbSize: std::mem::size_of::<winapi::NOTIFYICONDATAW>() as u32,
|
||||
hWnd: window_handle,
|
||||
uFlags: winapi::NIF_ICON | winapi::NIF_GUID,
|
||||
guidItem: id,
|
||||
hIcon: icon,
|
||||
uID: 0,
|
||||
uCallbackMessage: 0,
|
||||
szTip: [0; 128],
|
||||
dwState: 0,
|
||||
dwStateMask: 0,
|
||||
szInfo: [0; 256],
|
||||
uTimeout: 0,
|
||||
szInfoTitle: [0; 64],
|
||||
dwInfoFlags: 0,
|
||||
hBalloonIcon: std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
shell32::Shell_NotifyIconW(winapi::NIM_ADD, &mut icon_data);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run() {
|
||||
println!("Starting up UI (Windows)");
|
||||
|
||||
let path = std::env::current_exe().expect("Could not get current path");
|
||||
let guid = uuid::Uuid::new_v5(&uuid::NAMESPACE_OID, path.to_str().unwrap()).to_win();
|
||||
let window_handle = create_window().expect("Could not initialize window");
|
||||
add_notification_icon(window_handle, guid);
|
||||
|
||||
loop {
|
||||
thread::sleep(time::Duration::from_secs(10));
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe extern "system" fn window_proc(h_wnd: winapi::HWND,
|
||||
msg: winapi::UINT,
|
||||
w_param: winapi::WPARAM,
|
||||
l_param: winapi::LPARAM)
|
||||
-> winapi::LRESULT {
|
||||
if msg == winapi::winuser::WM_DESTROY {
|
||||
user32::PostQuitMessage(0);
|
||||
}
|
||||
return user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
|
||||
}
|
Loading…
Reference in a new issue