1
0
Fork 0

Add part of a Windows event loop implementation

This commit is contained in:
Robbert van der Helm 2022-02-07 00:33:41 +01:00
parent ce37ec288e
commit 0fb8ce6f7e
4 changed files with 233 additions and 6 deletions

54
Cargo.lock generated
View file

@ -515,6 +515,7 @@ dependencies = [
"serde_json",
"vst3-sys",
"widestring",
"windows",
]
[[package]]
@ -983,17 +984,30 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbedf6db9096bc2364adce0ae0aa636dcd89f3c3f2cd67947062aaf0ca2a10ec"
dependencies = [
"windows_aarch64_msvc 0.32.0",
"windows_i686_gnu 0.32.0",
"windows_i686_msvc 0.32.0",
"windows_x86_64_gnu 0.32.0",
"windows_x86_64_msvc 0.32.0",
]
[[package]]
name = "windows-sys"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceb069ac8b2117d36924190469735767f0990833935ab430155e71a44bafe148"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
"windows_aarch64_msvc 0.29.0",
"windows_i686_gnu 0.29.0",
"windows_i686_msvc 0.29.0",
"windows_x86_64_gnu 0.29.0",
"windows_x86_64_msvc 0.29.0",
]
[[package]]
@ -1002,30 +1016,60 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d027175d00b01e0cbeb97d6ab6ebe03b12330a35786cbaca5252b1c4bf5d9b"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e92753b1c443191654ec532f14c199742964a061be25d77d7a96f09db20bf5"
[[package]]
name = "windows_i686_gnu"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8793f59f7b8e8b01eda1a652b2697d87b93097198ae85f823b969ca5b89bba58"
[[package]]
name = "windows_i686_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a711c68811799e017b6038e0922cb27a5e2f43a2ddb609fe0b6f3eeda9de615"
[[package]]
name = "windows_i686_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8602f6c418b67024be2996c512f5f995de3ba417f4c75af68401ab8756796ae4"
[[package]]
name = "windows_i686_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "146c11bb1a02615db74680b32a68e2d61f553cc24c4eb5b4ca10311740e44172"
[[package]]
name = "windows_x86_64_gnu"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3d615f419543e0bd7d2b3323af0d86ff19cbc4f816e6453f36a2c2ce889c354"
[[package]]
name = "windows_x86_64_gnu"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c912b12f7454c6620635bbff3450962753834be2a594819bd5e945af18ec64bc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d95421d9ed3672c280884da53201a5c46b7b2765ca6faf34b0d71cf34a3561"
[[package]]
name = "windows_x86_64_msvc"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "504a2476202769977a040c6364301a3f65d0cc9e3fb08600b2bda150a0488316"
[[package]]
name = "x11"
version = "2.19.1"

View file

@ -37,6 +37,16 @@ widestring = "1.0.0-beta.1"
assert_no_alloc = { version = "1.1", optional = true }
[target.'cfg(windows)'.dependencies.windows]
version = "0.32"
features = [
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_UI_WindowsAndMessaging",
"Win32_System_LibraryLoader",
"Win32_System_Performance",
]
[features]
default = []
# Enabling this feature will cause the plugin to terminate when allocations

View file

@ -20,9 +20,15 @@ use std::sync::Weak;
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
mod linux;
#[cfg(target_os = "windows")]
mod windows;
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
pub(crate) use linux::LinuxEventLoop as OsEventLoop;
pub(crate) use self::linux::LinuxEventLoop as OsEventLoop;
#[cfg(target_os = "windows")]
pub(crate) use self::windows::WindowsEventLoop as OsEventLoop;
#[cfg(target_os = "macos")]
compile_error!("The macOS event loop has not yet been implemented");
use crate::param::internals::ParamPtr;
use crate::param::Param;

167
src/context/windows.rs Normal file
View file

@ -0,0 +1,167 @@
// nih-plug: plugins, but rewritten in Rust
// Copyright (C) 2022 Robbert van der Helm
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! An event loop for windows, using an invisible window to hook into the host's message loop. This
//! has only been tested under Wine.
use crossbeam::queue::ArrayQueue;
use std::ffi::{c_void, CString};
use std::mem;
use std::ptr;
use std::sync::Arc;
use std::sync::Weak;
use std::thread::{self, ThreadId};
use windows::Win32::Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, PSTR, WPARAM};
use windows::Win32::System::{
LibraryLoader::GetModuleHandleA, Performance::QueryPerformanceCounter,
};
use windows::Win32::UI::WindowsAndMessaging::{
CloseWindow, CreateWindowExA, DefWindowProcA, RegisterClassExA, UnregisterClassA, HMENU,
WINDOW_EX_STYLE, WINDOW_STYLE, WNDCLASSEXA,
};
use super::{EventLoop, MainThreadExecutor};
use crate::nih_log;
compile_error!("The Windows event loop has not yet been fully implemented");
/// See [super::EventLoop].
pub(crate) struct WindowsEventLoop<T, E> {
/// The thing that ends up executing these tasks. The tasks are usually executed from the worker
/// thread, but if the current thread is the main thread then the task cna also be executed
/// directly.
executor: Weak<E>,
/// The ID of the main thread. In practice this is the ID of the thread that created this task
/// queue.
main_thread_id: ThreadId,
/// An invisible window that we can post a message to when we need to do something on the main
/// thread. The host's message loop will then cause our message to be proceded.
message_window: HWND,
/// The unique class for the message window, we'll clean this up together with the window.
message_window_class_name: CString,
/// A queue of tasks that still need to be performed. When something gets added to this queue
/// we'll wake up the window, which then continues to pop tasks off this queue until it is
/// empty.
tasks: Arc<ArrayQueue<T>>,
}
impl<T, E> EventLoop<T, E> for WindowsEventLoop<T, E>
where
T: Send + 'static,
E: MainThreadExecutor<T> + 'static,
{
fn new_and_spawn(executor: Weak<E>) -> Self {
// We'll pass one copy of the this to the window, and we'll keep the other copy here
let tasks = Arc::new(ArrayQueue::new(super::TASK_QUEUE_CAPACITY));
// Window classes need to have unique names or else multiple plugins loaded into the same
// process will end up calling the other plugin's callbacks
let mut ticks = 0i64;
assert!(unsafe { QueryPerformanceCounter(&mut ticks).as_bool() });
let class_name = CString::new(format!("nih-event-loop-{ticks}"))
.expect("Where did these null bytes come from?");
let class_name_ptr = PSTR(class_name.as_bytes_with_nul().as_ptr());
let class = WNDCLASSEXA {
cbSize: mem::size_of::<WNDCLASSEXA>() as u32,
lpfnWndProc: Some(window_proc),
hInstance: unsafe { GetModuleHandleA(PSTR(ptr::null())) },
lpszClassName: class_name_ptr,
..Default::default()
};
assert_ne!(unsafe { RegisterClassExA(&class) }, 0);
let window = unsafe {
CreateWindowExA(
WINDOW_EX_STYLE(0),
class_name_ptr,
PSTR(b"NIH-plug event loop\0".as_ptr()),
WINDOW_STYLE(0),
0,
0,
0,
0,
HWND(0),
HMENU(0),
HINSTANCE(0),
Arc::into_raw(tasks.clone()) as *const c_void,
)
};
assert!(!window.is_invalid());
Self {
executor,
main_thread_id: thread::current().id(),
message_window: window,
message_window_class_name: class_name,
tasks,
}
}
fn do_maybe_async(&self, task: T) -> bool {
if self.is_main_thread() {
match self.executor.upgrade() {
Some(e) => {
unsafe { e.execute(task) };
true
}
None => {
nih_log!("The executor doesn't exist but somehow it's still submitting tasks, this shouldn't be possible!");
false
}
}
} else {
let success = self.tasks.push(task).is_ok();
if success {
todo!("Wake up the window");
}
success
}
}
fn is_main_thread(&self) -> bool {
thread::current().id() == self.main_thread_id
}
}
impl<T, E> Drop for WindowsEventLoop<T, E> {
fn drop(&mut self) {
unsafe { CloseWindow(self.message_window) };
unsafe {
UnregisterClassA(
PSTR(self.message_window_class_name.as_bytes_with_nul().as_ptr()),
HINSTANCE(0),
)
};
}
}
unsafe extern "system" fn window_proc(
handle: HWND,
message: u32,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
eprintln!("Hello from the window proc!");
todo!("Clean up the Arc (that got turned into a raw pointe) with the window");
todo!("Handle messages");
DefWindowProcA(handle, message, wparam, lparam)
}