Skip to content

Commit

Permalink
Playtime: Support standalone window mode on Windows (not enabled though)
Browse files Browse the repository at this point in the history
  • Loading branch information
helgoboss committed Nov 12, 2023
1 parent fb0ea07 commit 6a69e7f
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 11 deletions.
39 changes: 28 additions & 11 deletions main/src/infrastructure/ui/app/app_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,40 @@ pub trait AppInstance: Debug {
}

pub fn create_app_instance(session: WeakSession) -> impl AppInstance {
// I was experimenting with 2 different ways of embedding the app GUI into REAPER:
//
// - Parented mode: We create a new SWELL window on ReaLearn side (HWND on Windows, NSView on
// macOS) and the app renders its GUI *within* it.
// - Standalone mode: The app fires up its own window.
//
// Embedding the app in "parented" mode is in theory preferable because:
//
// 1. Only "parented" mode makes it possible to dock the app GUI (in case we want to do that
// one day).
// 2. ReaLearn has full control over the window and can listen to its events.
// 3. We stop sending events when the app window is hidden, not wasting resources when the
// app is not shown anyway. (However, with just a bit more effort, we could implement this
// for standalone mode as well.)
#[cfg(target_os = "windows")]
{
// On Windows, parented mode works wonderfully. It needs some tricks (see View
// implementation) but it's worth it. That's why we use parented mode on Windows.
let app_panel = AppPanel::new(session);
ParentedAppInstance {
panel: SharedView::new(app_panel),
}
}
#[cfg(target_os = "macos")]
{
// On macOS, parented mode is possible only by using Cocoa child windows (see app side
// embedding docs). This means that the app doesn't really render itself in the NSView
// provided by ReaLearn but places an NSWindow on top of the NSWindow provided by ReaLearn.
// It works but it needs a few keyboard tricks and - most importantly - it doesn't work
// well if the app itself wants to control its window (e.g. going full screen or changing
// the window opacity). It will try to control the child window, not the outer window.
// This could be solved on app side by navigating up the child/parent window chain, but
// it's not something I want to do now as long as we don't support docking anyway.
// Therefore: Standalone mode on macOS!
StandaloneAppInstance {
session,
running_state: None,
Expand Down Expand Up @@ -355,19 +380,11 @@ impl View for AppPanel {
true
}

fn button_clicked(self: SharedView<Self>, resource_id: u32) {
match resource_id {
// Escape key
raw::IDCANCEL => self.close(),
_ => {}
}
}

/// On Windows, this is necessary to resize contained app.
///
/// On macOS, this has no effect because the app window is not a child view (NSView) but a
/// child window (NSWindow). Resizing according to the parent window (the SWELL window) is done
/// on app side.
/// On macOS, this would have no effect because the app window is not a child view (NSView) but
/// a child window (NSWindow). Resizing according to the parent window (the SWELL window) is
/// done on app side.
#[cfg(target_os = "windows")]
fn resized(self: SharedView<Self>) -> bool {
crate::infrastructure::ui::egui_views::on_parent_window_resize(self.view.require_window())
Expand Down
12 changes: 12 additions & 0 deletions main/src/infrastructure/ui/app/app_library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use playtime_clip_engine::proto::{
MatrixProvider, Notification, NotificationKind, QueryReply, QueryResult, Reply, Request,
};
use prost::Message;
use reaper_high::Reaper;
use reaper_low::raw::HWND;
use std::env;
use std::ffi::{c_char, c_void, CString};
Expand Down Expand Up @@ -120,6 +121,7 @@ impl AppLibrary {
app_base_dir_c_string.as_ptr(),
invoke_host,
session_id_c_string.as_ptr(),
Reaper::get().main_window().as_ptr(),
)
};
let Some(app_handle) = app_handle else {
Expand Down Expand Up @@ -222,11 +224,21 @@ fn process_request(matrix_id: String, request_value: request::Value) -> Result<(
pub type AppHandle = NonNull<c_void>;

/// Signature of the function that we use to start an app instance.
///
/// # Arguments
///
/// * `parent_window` - Optional parent window handle. If you pass this, the app (if supported for
/// the OS) will render itself *within* that parent window. On macOS, this is should be an NSView.
/// * `app_base_dir_utf8_c_str`- Directory where the app is located
/// * `host_callback` - Pointer to host callback function
/// * `session_id` - Session ID of the ReaLearn instance associated with this new app instance.
/// * `main_window` - Handle to REAPER's main window
type StartAppInstance = unsafe extern "C" fn(
parent_window: HWND,
app_base_dir_utf8_c_str: *const c_char,
host_callback: HostCallback,
session_id: *const c_char,
main_window: HWND,
) -> Option<AppHandle>;

/// Signature of the function that we use to show an app instance.
Expand Down

0 comments on commit 6a69e7f

Please sign in to comment.