Reorganize top-level directories with clearer naming convention

app -> app-desktop-macos, presets -> app-presets, server -> remote-server,
daw-config-reaper -> osc-config-daw, plugin-reaper-realearn -> plugin-reaper-relearn.
Updated run.py and presets.py path references accordingly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Paul Lipscomb
2026-07-15 17:51:05 -04:00
parent e58f06d9fa
commit 7ecc718f5d
2256 changed files with 11 additions and 5 deletions
@@ -0,0 +1,347 @@
//! This file is supposed to encapsulate most of the (ugly) win32 API glue code
use crate::{
BrushCache, BrushDescriptor, DeviceContext, FontCache, FontDescriptor, Pixels, Point,
SharedView, View, WeakView, Window,
};
use std::cell::{Cell, RefCell};
use reaper_low::{raw, Swell};
use rxrust::prelude::*;
use std::os::raw::c_void;
use std::panic::catch_unwind;
use std::ptr::null_mut;
use base::hash_util::NonCryptoHashMap;
use fragile::Fragile;
use reaper_common_types::RgbColor;
use reaper_medium::{Hbrush, Hdc, Hfont, Hwnd};
use std::sync::OnceLock;
/// Creates a window according to the given dialog resource.
///
/// It's added as a child to the given parent window and attached to the specified view.
///
/// Internally, this creates a new win32 dialog using the given resource ID. Uses the methods in the
/// given view for all callbacks.
pub(crate) fn create_window(
view: SharedView<dyn View>,
resource_id: u32,
parent_window: Option<Window>,
) -> Option<Window> {
let swell = Swell::get();
let hwnd = unsafe {
// This will call the dialog procedure `view_dialog_proc`. In order to still know which
// of the many view objects we are dealing with, we make use of the lparam parameter of
// `CreateDialogParamA` by passing it an address which points to the concrete view.
// `view_dialog_proc` with message WM_INITDIALOG will be called immediately, not async.
// That's important because we must be sure that the given view Rc reference is still
// valid when it arrives in `view_dialog_proc`.
swell.CreateDialogParam(
swell.plugin_context().h_instance(),
resource_id as u16 as raw::ULONG_PTR as raw::LPSTR,
parent_window.map(|w| w.raw()).unwrap_or(null_mut()),
Some(view_dialog_proc),
convert_view_ref_to_address(&view),
)
};
Window::new(hwnd)
}
/// This struct manages the mapping from windows to views.
///
/// This is necessary to get from "global" win32 world into beloved "local" Rust struct world.
#[derive(Default)]
pub struct ViewManager {
/// Holds a mapping from window handles (HWND) to views
view_map: RefCell<NonCryptoHashMap<raw::HWND, WeakView<dyn View>>>,
brush_cache: BrushCache,
font_cache: FontCache,
}
impl ViewManager {
pub fn get_solid_brush(&'static self, color: RgbColor) -> Option<Hbrush> {
self.brush_cache.get_brush(BrushDescriptor::solid(color))
}
pub fn get_font(&'static self, descriptor: FontDescriptor) -> Option<Hfont> {
self.font_cache.get_font(descriptor)
}
/// If the given window is one of ours (one that drives our views) and the associated view
/// still exists, it returns that associated view.
pub fn get_associated_view(&self, window: Window) -> Option<SharedView<dyn View>> {
let view_map = self.view_map.borrow();
let view = view_map.get(&window.raw())?;
view.upgrade()
}
/// Returns the global window manager instance
pub fn get() -> &'static ViewManager {
static VIEW_MANAGER: OnceLock<Fragile<ViewManager>> = OnceLock::new();
// We need to initialize the manager lazily because it's impossible to do that using a const
// function (at least in Rust stable).
VIEW_MANAGER
.get_or_init(|| Fragile::new(ViewManager::default()))
.get()
}
/// Registers a new HWND-to-view mapping
fn register_view(&self, hwnd: raw::HWND, view: &SharedView<dyn View>) {
self.view_map
.borrow_mut()
.insert(hwnd, SharedView::downgrade(view));
}
/// Looks up a view by its corresponding HWND
fn lookup_view(&self, hwnd: raw::HWND) -> Option<SharedView<dyn View>> {
let view_map = self.view_map.borrow();
let weak_view = view_map.get(&hwnd)?;
weak_view.upgrade().or_else(|| {
// Not existing. The primary owner (most likely a parent view) dropped
// it already.
tracing::warn!("Requested ui is registered in ui map but has been dropped already");
None
})
}
/// Unregisters a HWND-to-View mapping
fn unregister_view(&self, hwnd: raw::HWND) {
self.view_map.borrow_mut().remove(&hwnd);
}
}
// Converts the given view Rc reference to an address which can be transmitted as LPARAM.
// `SharedView<dyn View>` is a so-called trait object, a *fat* pointer which is twice as large as a
// normal pointer (on 64-bit architectures 2 x 64 bit = 128 bit = 16 bytes). This is too big to
// encode within LPARAM. `&SharedView<dyn View>` is *not* a trait object but a reference to the
// trait object. Therefore it is a thin pointer already.
fn convert_view_ref_to_address(view_trait_object_ref: &SharedView<dyn View>) -> isize {
let view_trait_object_ptr = view_trait_object_ref as *const _ as *const c_void;
view_trait_object_ptr as isize
}
// Converts the given address back to the original view Rc reference.
fn interpret_address_as_view_ref<'a>(view_trait_object_address: isize) -> &'a SharedView<dyn View> {
let view_trait_object_ptr = view_trait_object_address as *const c_void;
unsafe { &*(view_trait_object_ptr as *const _) }
}
/// This is our dialog procedure.
///
/// It's called by Windows (or the emulation layer). It basically finds the particular `View`
/// instance which matches the HWND and then delegates to its methods. Please note that this is
/// a DialogProc, not a WindowProc. The difference is mainly the return value. A WindowProc
/// usually returns 0 if the message has been processed, or it delegates to `DefWindowProc()`.
/// If we do the latter in a DialogProc, non-child windows start to become always modal (not
/// returning focus) because it's wrong!
///
/// In DialogProc it's the opposite: It returns 1 if the message has been processed and 0 if not.
/// If we have a message where the return value has a special meaning (beyond processed or
/// unprocesssed), we need to "return" that via `SetWindowLong` instead, except for WM_INITDIALOG.
/// See https://docs.microsoft.com/en-us/windows/win32/api/winuser/nc-winuser-dlgproc.
unsafe extern "C" fn view_dialog_proc(
hwnd: raw::HWND,
msg: raw::UINT,
wparam: raw::WPARAM,
lparam: raw::LPARAM,
) -> raw::INT_PTR {
catch_unwind(|| {
DIALOG_PROC_ALREADY_ENTERED.with(|entered| {
// Detect reentrancy
let already_entered = entered.replace(true);
scopeguard::defer! {
if !already_entered {
entered.set(false);
}
}
// Obtain view
let view: SharedView<dyn View> = if msg == raw::WM_INITDIALOG {
// A view window is initializing. At this point lparam contains the value which we
// passed when calling CreateDialogParam. This contains the address of a
// view reference. At subsequent calls, this address is not passed anymore
// but only the HWND. So we need to save a HWND-to-view mapping now.
let view_ref = interpret_address_as_view_ref(lparam as _);
ViewManager::get().register_view(hwnd, view_ref);
view_ref.clone()
} else {
// Try to find view corresponding to given HWND
match ViewManager::get().lookup_view(hwnd) {
None => {
// View is not (yet) registered. Do default stuff.
return 0;
}
Some(v) => v,
}
};
// Found view.
// Delegate to view struct methods.
let window = Window::new(hwnd).expect("window was null");
if let Some(result) = view.process_raw(window, msg, wparam, lparam) {
return result;
}
const KEYBOARD_MSG_FOR_X_BRIDGE: u32 = raw::WM_USER + 100;
match msg {
raw::WM_INITDIALOG => {
view.view_context().window.replace(Some(window));
let show_window = view.show_window_on_init();
let keyboard_focus_desired = view.opened(window);
if show_window {
window.show();
// WM_INITDIALOG is special in a DialogProc in that we don't need to use
// `SetWindowLong()` for return values with special meaning.
keyboard_focus_desired.into()
} else {
0
}
}
raw::WM_SHOWWINDOW => view.shown_or_hidden(wparam == 1).into(),
raw::WM_DESTROY => {
let view_context = view.view_context();
view_context.closed_subject.borrow_mut().next(());
view_context.window.replace(None);
view.on_destroy(window);
ViewManager::get().unregister_view(hwnd);
1
}
// This is called on Linux when receiving a keyboard message via SendMessage.
// The only time we do this is when we want to forward keyboard interaction from
// the RealearnAccelerator to egui. egui runs in an XBridge window - a sort
// of child window of the SWELL window. Returning 0 here makes sure that the
// messages are passed through to the XBridge window. This is SWELL functionality
// (check the SWELL code).
KEYBOARD_MSG_FOR_X_BRIDGE => 0,
raw::WM_MOUSEMOVE => view
.mouse_moved(Point::new(
loword_signed(lparam as _),
hiword_signed(lparam as _),
))
.into(),
raw::WM_NCHITTEST => view
.mouse_test(Point::new(
loword_signed(lparam as _),
hiword_signed(lparam as _),
))
.into(),
raw::WM_SIZE => view.resized().into(),
raw::WM_SETFOCUS => view.focused().into(),
raw::WM_COMMAND => {
let resource_id = loword(wparam as _);
match hiword(wparam as _) as u32 {
0 => {
view.button_clicked(resource_id as _);
// We just say the click is handled. Don't know where this would not
// be the case.
1
}
raw::CBN_SELCHANGE => {
view.option_selected(resource_id as _);
// We just say the selection is handled. Don't know where this would not
// be the case.
1
}
raw::EN_SETFOCUS => view.edit_control_focus_set(resource_id as _).into(),
raw::EN_KILLFOCUS => {
view.edit_control_focus_killed(resource_id as _).into()
}
raw::EN_CHANGE => {
// Edit control change event is fired even if we change an edit control
// text programmatically. We don't want this. In general.
if already_entered {
return 0;
}
view.edit_control_changed(resource_id as _).into()
}
_ => 0,
}
}
raw::WM_VSCROLL => {
let code = loword(wparam as _);
view.scrolled_vertically(code as _).into()
}
raw::WM_HSCROLL => {
if lparam <= 0 {
// This is not a slider. Not interested.
return 0;
}
let raw_slider = Hwnd::new(lparam as raw::HWND).expect("slider hwnd is null");
view.slider_moved(Window::from_hwnd(raw_slider));
1
}
raw::WM_MOUSEWHEEL => {
let distance = hiword_signed(wparam);
view.mouse_wheel_turned(distance).into()
}
raw::WM_KEYDOWN => view.key_down(wparam as _).into(),
raw::WM_KEYUP => view.key_up(wparam as _).into(),
raw::WM_CLOSE => {
let processed = view.close_requested();
if !processed {
window.destroy();
}
1
}
raw::WM_CONTEXTMENU => {
let x = loword(lparam as _);
let y = hiword(lparam as _);
view.context_menu_wanted(Point::new(Pixels(x as _), Pixels(y as _)))
.into()
}
raw::WM_PAINT => isize::from(view.paint()),
raw::WM_ERASEBKGND => {
if let Some(hdc) = Hdc::new(wparam as raw::HDC) {
isize::from(view.erase_background(DeviceContext::new(hdc)))
} else {
0
}
}
raw::WM_CTLCOLORSTATIC => {
let brush = view.control_color_static(
DeviceContext::new(
Hdc::new(wparam as raw::HDC).expect("HDC in WM_CTLCOLORSTATIC is null"),
),
Window::new(lparam as raw::HWND)
.expect("WM_CTLCOLORSTATIC control is null"),
);
brush.map(|b| b.as_ptr()).unwrap_or(null_mut()) as _
}
raw::WM_CTLCOLORDLG => {
let brush = view.control_color_dialog(
DeviceContext::new(
Hdc::new(wparam as raw::HDC).expect("HDC in WM_CTLCOLORDLG is null"),
),
Window::new(lparam as raw::HWND).expect("WM_CTLCOLORDLG control is null"),
);
brush.map(|b| b.as_ptr()).unwrap_or(null_mut()) as _
}
raw::WM_TIMER => {
if view.timer(wparam) {
0
} else {
1
}
}
_ => 0,
}
})
})
.unwrap_or(0)
}
fn loword(wparam: usize) -> u16 {
(wparam & 0xffff) as _
}
fn loword_signed(wparam: usize) -> i32 {
loword(wparam) as i16 as i32
}
fn hiword(wparam: usize) -> u16 {
((wparam >> 16) & 0xffff) as _
}
fn hiword_signed(wparam: usize) -> i32 {
hiword(wparam) as i16 as i32
}
// Used for global dialog proc reentrancy check.
thread_local!(static DIALOG_PROC_ALREADY_ENTERED: Cell<bool> = const { Cell::new(false) });