summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2022-01-27 19:26:22 +0100
committerDavid Lönnhager <david.l@mullvad.net>2022-02-02 17:21:29 +0100
commit3fbeb82c834abebf1a8852ea74ad59505c62c065 (patch)
tree9998d665558f4a8b32f3447b75b6965c3964a092
parent8e5a68a39e0aa2884f44e817d6c5e088b9edf6b3 (diff)
downloadmullvadvpn-3fbeb82c834abebf1a8852ea74ad59505c62c065.tar.xz
mullvadvpn-3fbeb82c834abebf1a8852ea74ad59505c62c065.zip
Add Windows windowing utilities module
-rw-r--r--talpid-core/src/offline/windows.rs137
-rw-r--r--talpid-core/src/windows/mod.rs (renamed from talpid-core/src/windows.rs)2
-rw-r--r--talpid-core/src/windows/window.rs128
3 files changed, 142 insertions, 125 deletions
diff --git a/talpid-core/src/offline/windows.rs b/talpid-core/src/offline/windows.rs
index c04277aea5..15b5d47169 100644
--- a/talpid-core/src/offline/windows.rs
+++ b/talpid-core/src/offline/windows.rs
@@ -1,41 +1,21 @@
-use crate::winnet;
+use crate::{
+ windows::window::{create_hidden_window, WindowCloseHandle},
+ winnet,
+};
use futures::channel::mpsc::UnboundedSender;
use parking_lot::Mutex;
use std::{
ffi::c_void,
io,
- mem::zeroed,
- os::windows::io::{IntoRawHandle, RawHandle},
- ptr,
sync::{Arc, Weak},
thread,
time::Duration,
};
use talpid_types::ErrorExt;
-use winapi::{
- shared::{
- basetsd::LONG_PTR,
- minwindef::{DWORD, LPARAM, LRESULT, UINT, WPARAM},
- windef::HWND,
- },
- um::{
- handleapi::CloseHandle,
- libloaderapi::GetModuleHandleW,
- processthreadsapi::GetThreadId,
- synchapi::WaitForSingleObject,
- winbase::INFINITE,
- winuser::{
- CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetMessageW,
- GetWindowLongPtrW, PostQuitMessage, PostThreadMessageW, SetWindowLongPtrW,
- GWLP_USERDATA, GWLP_WNDPROC, PBT_APMRESUMEAUTOMATIC, PBT_APMSUSPEND, WM_DESTROY,
- WM_POWERBROADCAST, WM_USER,
- },
- },
+use winapi::um::winuser::{
+ DefWindowProcW, PBT_APMRESUMEAUTOMATIC, PBT_APMSUSPEND, WM_POWERBROADCAST,
};
-const CLASS_NAME: &[u8] = b"S\0T\0A\0T\0I\0C\0\0\0";
-const REQUEST_THREAD_SHUTDOWN: UINT = WM_USER + 1;
-
#[derive(err_derive::Error, Debug)]
pub enum Error {
#[error(display = "Unable to create listener thread")]
@@ -45,8 +25,7 @@ pub enum Error {
}
pub struct BroadcastListener {
- thread_handle: RawHandle,
- thread_id: DWORD,
+ window: WindowCloseHandle,
system_state: Arc<Mutex<SystemState>>,
_callback_handle: winnet::WinNetCallbackHandle,
_notify_tx: Arc<UnboundedSender<bool>>,
@@ -67,7 +46,7 @@ impl BroadcastListener {
let power_broadcast_state_ref = system_state.clone();
- let power_broadcast_callback = move |message: UINT, wparam: WPARAM, _lparam: LPARAM| {
+ let power_broadcast_callback = move |window, message, wparam, lparam| {
let state = power_broadcast_state_ref.clone();
if message == WM_POWERBROADCAST {
if wparam == PBT_APMSUSPEND {
@@ -83,22 +62,16 @@ impl BroadcastListener {
});
}
}
+ unsafe { DefWindowProcW(window, message, wparam, lparam) }
};
- let join_handle = thread::Builder::new()
- .spawn(move || unsafe {
- Self::message_pump(power_broadcast_callback);
- })
- .map_err(Error::ThreadCreationError)?;
-
- let real_handle = join_handle.into_raw_handle();
+ let window = create_hidden_window(power_broadcast_callback);
let callback_handle =
unsafe { Self::setup_network_connectivity_listener(system_state.clone())? };
Ok(BroadcastListener {
- thread_handle: real_handle,
- thread_id: unsafe { GetThreadId(real_handle) },
+ window,
system_state,
_callback_handle: callback_handle,
_notify_tx: notify_tx,
@@ -131,88 +104,6 @@ impl BroadcastListener {
(v4_connectivity, v6_connectivity)
}
- unsafe fn message_pump<F>(client_callback: F)
- where
- F: Fn(UINT, WPARAM, LPARAM),
- {
- let dummy_window = CreateWindowExW(
- 0,
- CLASS_NAME.as_ptr() as *const u16,
- ptr::null_mut(),
- 0,
- 0,
- 0,
- 0,
- 0,
- ptr::null_mut(),
- ptr::null_mut(),
- GetModuleHandleW(ptr::null_mut()),
- ptr::null_mut(),
- );
-
- // Move callback information to the heap.
- // This enables us to reach the callback through a "thin pointer".
- let boxed_callback = Box::new(client_callback);
-
- // Detach callback from Box.
- let raw_callback = Box::into_raw(boxed_callback) as *mut c_void;
-
- SetWindowLongPtrW(dummy_window, GWLP_USERDATA, raw_callback as LONG_PTR);
- SetWindowLongPtrW(
- dummy_window,
- GWLP_WNDPROC,
- Self::window_procedure::<F> as LONG_PTR,
- );
-
- let mut msg = zeroed();
-
- loop {
- let status = GetMessageW(&mut msg, 0 as HWND, 0, 0);
-
- if status < 0 {
- continue;
- }
- if status == 0 {
- break;
- }
-
- if msg.hwnd.is_null() {
- if msg.message == REQUEST_THREAD_SHUTDOWN {
- DestroyWindow(dummy_window);
- }
- } else {
- DispatchMessageW(&mut msg);
- }
- }
-
- // Reattach callback to Box for proper clean-up.
- let _ = Box::from_raw(raw_callback as *mut F);
- }
-
- unsafe extern "system" fn window_procedure<F>(
- window: HWND,
- message: UINT,
- wparam: WPARAM,
- lparam: LPARAM,
- ) -> LRESULT
- where
- F: Fn(UINT, WPARAM, LPARAM),
- {
- let raw_callback = GetWindowLongPtrW(window, GWLP_USERDATA);
-
- if raw_callback != 0 {
- let typed_callback = &mut *(raw_callback as *mut F);
- typed_callback(message, wparam, lparam);
- }
-
- if message == WM_DESTROY {
- PostQuitMessage(0);
- return 0;
- }
-
- DefWindowProcW(window, message, wparam, lparam)
- }
-
/// The caller must make sure the `system_state` reference is valid
/// until after `WinNet_DeactivateConnectivityMonitor` has been called.
unsafe fn setup_network_connectivity_listener(
@@ -252,11 +143,7 @@ impl BroadcastListener {
impl Drop for BroadcastListener {
fn drop(&mut self) {
- unsafe {
- PostThreadMessageW(self.thread_id, REQUEST_THREAD_SHUTDOWN, 0, 0);
- WaitForSingleObject(self.thread_handle, INFINITE);
- CloseHandle(self.thread_handle);
- }
+ self.window.close();
}
}
diff --git a/talpid-core/src/windows.rs b/talpid-core/src/windows/mod.rs
index 1febc5bc21..4ec1fe19f3 100644
--- a/talpid-core/src/windows.rs
+++ b/talpid-core/src/windows/mod.rs
@@ -31,6 +31,8 @@ use winapi::shared::{
ws2ipdef::{SOCKADDR_IN6_LH as sockaddr_in6, SOCKADDR_INET},
};
+pub mod window;
+
/// Result type for this module.
pub type Result<T> = std::result::Result<T, Error>;
diff --git a/talpid-core/src/windows/window.rs b/talpid-core/src/windows/window.rs
new file mode 100644
index 0000000000..05e457e5d6
--- /dev/null
+++ b/talpid-core/src/windows/window.rs
@@ -0,0 +1,128 @@
+//! Utilities for windows.
+
+use std::{os::windows::io::AsRawHandle, ptr, thread};
+use winapi::{
+ shared::{
+ basetsd::LONG_PTR,
+ minwindef::{LPARAM, LRESULT, UINT, WPARAM},
+ windef::HWND,
+ },
+ um::{
+ libloaderapi::GetModuleHandleW,
+ processthreadsapi::GetThreadId,
+ winuser::{
+ CreateWindowExW, DefWindowProcW, DestroyWindow, DispatchMessageW, GetMessageW,
+ GetWindowLongPtrW, PostQuitMessage, PostThreadMessageW, SetWindowLongPtrW,
+ TranslateMessage, GWLP_USERDATA, GWLP_WNDPROC, WM_DESTROY, WM_USER,
+ },
+ },
+};
+
+const CLASS_NAME: &[u8] = b"S\0T\0A\0T\0I\0C\0\0\0";
+const REQUEST_THREAD_SHUTDOWN: UINT = WM_USER + 1;
+
+/// Handle for closing an associated window.
+/// The window is not destroyed when this is dropped.
+pub struct WindowCloseHandle {
+ thread: Option<std::thread::JoinHandle<()>>,
+}
+
+impl WindowCloseHandle {
+ /// Close the window and wait for the thread.
+ pub fn close(&mut self) {
+ if let Some(thread) = self.thread.take() {
+ let thread_id = unsafe { GetThreadId(thread.as_raw_handle()) };
+ unsafe { PostThreadMessageW(thread_id, REQUEST_THREAD_SHUTDOWN, 0, 0) };
+ let _ = thread.join();
+ }
+ }
+}
+
+/// Creates a dummy window whose messages are handled by `wnd_proc`.
+pub fn create_hidden_window<F: (Fn(HWND, UINT, WPARAM, LPARAM) -> LRESULT) + Send + 'static>(
+ wnd_proc: F,
+) -> WindowCloseHandle {
+ let join_handle = thread::spawn(move || {
+ let dummy_window = unsafe {
+ CreateWindowExW(
+ 0,
+ CLASS_NAME.as_ptr() as *const u16,
+ ptr::null_mut(),
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ GetModuleHandleW(ptr::null_mut()),
+ ptr::null_mut(),
+ )
+ };
+
+ // Move callback information to the heap.
+ // This enables us to reach the callback through a "thin pointer".
+ let raw_callback = Box::into_raw(Box::new(wnd_proc));
+
+ unsafe {
+ SetWindowLongPtrW(dummy_window, GWLP_USERDATA, raw_callback as LONG_PTR);
+ SetWindowLongPtrW(
+ dummy_window,
+ GWLP_WNDPROC,
+ window_procedure::<F> as LONG_PTR,
+ );
+ }
+
+ let mut msg = unsafe { std::mem::zeroed() };
+
+ loop {
+ let status = unsafe { GetMessageW(&mut msg, ptr::null_mut(), 0, 0) };
+
+ if status < 0 {
+ continue;
+ }
+ if status == 0 {
+ break;
+ }
+
+ if msg.hwnd.is_null() {
+ if msg.message == REQUEST_THREAD_SHUTDOWN {
+ unsafe { DestroyWindow(dummy_window) };
+ }
+ } else {
+ unsafe {
+ TranslateMessage(&mut msg);
+ DispatchMessageW(&mut msg);
+ }
+ }
+ }
+
+ // Free callback.
+ let _ = unsafe { Box::from_raw(raw_callback) };
+ });
+
+ WindowCloseHandle {
+ thread: Some(join_handle),
+ }
+}
+
+unsafe extern "system" fn window_procedure<F>(
+ window: HWND,
+ message: UINT,
+ wparam: WPARAM,
+ lparam: LPARAM,
+) -> LRESULT
+where
+ F: Fn(HWND, UINT, WPARAM, LPARAM) -> LRESULT,
+{
+ if message == WM_DESTROY {
+ PostQuitMessage(0);
+ return 0;
+ }
+ let raw_callback = GetWindowLongPtrW(window, GWLP_USERDATA);
+ if raw_callback != 0 {
+ let typed_callback = &mut *(raw_callback as *mut F);
+ return typed_callback(window, message, wparam, lparam);
+ }
+ DefWindowProcW(window, message, wparam, lparam)
+}