Add part of a Windows event loop implementation
This commit is contained in:
parent
ce37ec288e
commit
0fb8ce6f7e
54
Cargo.lock
generated
54
Cargo.lock
generated
|
@ -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"
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -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
|
||||
|
|
|
@ -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
167
src/context/windows.rs
Normal 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)
|
||||
}
|
Loading…
Reference in a new issue