Skip to content

Commit

Permalink
Playtime: Full-screen support when in embedded mode
Browse files Browse the repository at this point in the history
  • Loading branch information
helgoboss committed Oct 25, 2023
1 parent d759224 commit 4343e5c
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 52 deletions.
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions main/src/infrastructure/plugin/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use crate::infrastructure::plugin::allocator::{
};
use crate::infrastructure::plugin::tracing_util::setup_tracing;
use crate::infrastructure::server::services::RealearnServices;
use anyhow::Context;
use anyhow::{anyhow, Context};
use once_cell::sync::Lazy;
use realearn_api::persistence::{
Envelope, FxChainDescriptor, FxDescriptor, TargetTouchCause, TrackDescriptor, TrackFxChain,
Expand Down Expand Up @@ -864,9 +864,9 @@ impl App {
&APP_LOGGER
}

pub fn get_or_load_app_library() -> Result<&'static AppLibrary, &'static anyhow::Error> {
pub fn get_or_load_app_library() -> anyhow::Result<&'static AppLibrary> {
static APP_LIBRARY: Lazy<anyhow::Result<AppLibrary>> = Lazy::new(load_app_library);
APP_LIBRARY.as_ref()
Ok(APP_LIBRARY.as_ref().map_err(|e| anyhow!(e.to_string()))?)
}

pub fn sessions_changed(&self) -> impl LocalObservable<'static, Item = (), Err = ()> + 'static {
Expand Down
53 changes: 35 additions & 18 deletions main/src/infrastructure/ui/app/app_library.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::infrastructure::plugin::App;
use crate::infrastructure::server::services::playtime_service::AppMatrixProvider;
use crate::infrastructure::ui::AppCallback;
use anyhow::{anyhow, bail, Result};
use crate::infrastructure::ui::{AppCallback, AppPanel, MainPanel};
use anyhow::{anyhow, bail, Context, Result};
use base::{tracing_error, Global};
use egui::Key::V;
use libloading::{Library, Symbol};
Expand All @@ -19,7 +19,7 @@ use std::ffi::{c_char, c_void, CString};
use std::future::Future;
use std::path::{Path, PathBuf};
use std::ptr::{null, NonNull};
use swell_ui::Window;
use swell_ui::{SharedView, Window};
use tonic::Status;

#[derive(Debug)]
Expand Down Expand Up @@ -104,6 +104,17 @@ impl AppLibrary {
})
}

pub fn toggle_full_screen(&self, parent_window: Window) -> Result<()> {
unsafe {
let symbol: Symbol<ToggleFullScreen> =
self.main_library
.get(b"toggle_full_screen\0")
.map_err(|_| anyhow!("failed to load toggle_full_screen function"))?;
symbol(parent_window.raw());
};
Ok(())
}

pub fn close(&self, parent_window: Window, app_handle: AppHandle) -> Result<()> {
unsafe {
let symbol: Symbol<CloseApp> = self
Expand Down Expand Up @@ -146,6 +157,9 @@ type RunAppInParent = unsafe extern "C" fn(
session_id: *const c_char,
) -> Option<AppHandle>;

/// Signature of the function that we use to toggle full-screen.
type ToggleFullScreen = unsafe extern "C" fn(parent_window: HWND);

/// Signature of the function that we use to close the App.
type CloseApp = unsafe extern "C" fn(parent_window: HWND, app_handle: AppHandle);

Expand Down Expand Up @@ -194,7 +208,7 @@ fn prepare_app_launch() {
}
}

fn process_request(req: proto::request::Value) -> Result<(), tonic::Status> {
fn process_request(req: proto::request::Value) -> Result<()> {
use proto::request::Value;
match req {
Value::CommandRequest(req) => process_command(
Expand All @@ -209,7 +223,7 @@ fn process_request(req: proto::request::Value) -> Result<(), tonic::Status> {
}
}

fn process_query(matrix_id: String, id: u32, query: proto::Query) -> Result<(), tonic::Status> {
fn process_query(matrix_id: String, id: u32, query: proto::Query) -> Result<()> {
use proto::query::Value::*;
let handler = ClipEngineRequestHandler::new(AppMatrixProvider);
match query
Expand All @@ -232,7 +246,7 @@ fn process_query(matrix_id: String, id: u32, query: proto::Query) -> Result<(),
Ok(())
}

fn process_command(req: proto::command_request::Value) -> Result<(), tonic::Status> {
fn process_command(req: proto::command_request::Value) -> Result<()> {
// TODO-low This should be a more generic command handler in future (not just clip engine)
let handler = ClipEngineRequestHandler::new(AppMatrixProvider);
use proto::command_request::Value::*;
Expand All @@ -242,10 +256,10 @@ fn process_command(req: proto::command_request::Value) -> Result<(), tonic::Stat
// App instance is started. Put the app instance callback at the correct position.
let ptr = req.app_callback_address as *const ();
let app_callback: AppCallback = unsafe { std::mem::transmute(ptr) };
let main_panel = App::get()
.find_main_panel_by_session_id(&req.matrix_id)
.ok_or(Status::not_found("instance not found"))?;
main_panel.notify_app_is_ready(app_callback);
find_app_panel(&req.matrix_id)?.notify_app_is_ready(app_callback);
}
TriggerApp(req) => {
find_app_panel(&req.session_id)?.toggle_full_screen()?;
}
// Event subscription commands
GetOccasionalMatrixUpdates(req) => {
Expand Down Expand Up @@ -340,7 +354,7 @@ fn process_command(req: proto::command_request::Value) -> Result<(), tonic::Stat
fn send_initial_events_to_app<T: Into<event_reply::Value>>(
matrix_id: &str,
create_reply: impl FnOnce(Option<&Matrix>) -> T + Copy,
) -> Result<(), tonic::Status> {
) -> Result<()> {
let event_reply_value = AppMatrixProvider
.with_matrix(matrix_id, |matrix| create_reply(Some(matrix)).into())
.unwrap_or_else(|_| create_reply(None).into());
Expand Down Expand Up @@ -369,15 +383,18 @@ fn send_query_reply_to_app(
});
}

fn send_to_app(session_id: &str, reply_value: reply::Value) -> Result<(), tonic::Status> {
let main_panel = App::get()
.find_main_panel_by_session_id(session_id)
.ok_or(Status::not_found("instance not found"))?;
fn send_to_app(session_id: &str, reply_value: reply::Value) -> Result<()> {
let app_panel = find_app_panel(session_id)?;
let reply = Reply {
value: Some(reply_value),
};
main_panel
.send_to_app(&reply)
.map_err(|e| Status::unknown(e.to_string()))?;
app_panel.send_to_app(&reply)?;
Ok(())
}

fn find_app_panel(session_id: &str) -> Result<SharedView<AppPanel>> {
App::get()
.find_main_panel_by_session_id(session_id)
.context("instance not found")?
.app_panel()
}
31 changes: 20 additions & 11 deletions main/src/infrastructure/ui/app/app_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ use crate::application::WeakSession;
use crate::infrastructure::plugin::App;
use crate::infrastructure::ui::bindings::root;
use crate::infrastructure::ui::AppHandle;
use anyhow::{anyhow, Result};
use anyhow::{anyhow, Context, Result};
use base::Global;
use c_str_macro::c_str;
use playtime_clip_engine::proto;
use playtime_clip_engine::proto::{reply, ClipEngineReceivers, EventReply, Reply};
use prost::Message;
use reaper_low::raw;
use reaper_low::{raw, Swell};
use std::cell::RefCell;
use std::ffi::c_void;
use std::ptr::NonNull;
use std::ptr::{null_mut, NonNull};
use std::time::Duration;
use swell_ui::{SharedView, View, ViewContext, Window};
use validator::HasLen;
Expand Down Expand Up @@ -39,14 +40,23 @@ impl AppPanel {
}
}

pub fn send_to_app(&self, reply: &Reply) -> Result<(), &'static str> {
pub fn send_to_app(&self, reply: &Reply) -> Result<()> {
self.open_state
.borrow()
.as_ref()
.ok_or("app not open")?
.context("app not open")?
.send_to_app(reply)
}

pub fn toggle_full_screen(&self) -> Result<()> {
// Because the full-screen windowing code is a mess and highly platform-specific, it's best
// to use a platform-specific language to do the job. In case of macOS, Swift is the best
// choice. The app itself has easy access to Swift, so let's just call into the app library
// so it takes care of handling its host window.
// TODO-low It's a bit weird to ask the app (a guest) to deal with a host window. Improve.
App::get_or_load_app_library()?.toggle_full_screen(self.view.require_window())
}

pub fn notify_app_is_ready(&self, callback: AppCallback) {
let mut open_state = self.open_state.borrow_mut();
let Some(open_state) = open_state.as_mut() else {
Expand All @@ -60,8 +70,8 @@ impl AppPanel {
.set_timer(TIMER_ID, Duration::from_millis(30));
}

fn open_internal(&self, window: Window) -> anyhow::Result<()> {
let app_library = App::get_or_load_app_library().map_err(|e| anyhow!(e.to_string()))?;
fn open_internal(&self, window: Window) -> Result<()> {
let app_library = App::get_or_load_app_library()?;
let session_id = self
.session
.upgrade()
Expand Down Expand Up @@ -90,8 +100,8 @@ impl AppPanel {
}

impl OpenState {
pub fn send_to_app(&self, reply: &Reply) -> Result<(), &'static str> {
let app_callback = self.app_callback.ok_or("app callback not known yet")?;
pub fn send_to_app(&self, reply: &Reply) -> Result<()> {
let app_callback = self.app_callback.context("app callback not known yet")?;
send_to_app(app_callback, reply);
Ok(())
}
Expand All @@ -110,8 +120,7 @@ impl OpenState {
}

pub fn close_app(&self, window: Window) -> Result<()> {
let app_library = App::get_or_load_app_library().map_err(|e| anyhow!(e.to_string()))?;
app_library.close(window, self.app_handle)
App::get_or_load_app_library()?.close(window, self.app_handle)
}
}

Expand Down
12 changes: 5 additions & 7 deletions main/src/infrastructure/ui/independent_panel_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,8 @@ impl IndependentPanelManager {
});
}

pub fn notify_app_is_ready(&self, callback: AppCallback) {
self.app_panel.notify_app_is_ready(callback);
}

pub fn send_to_app(&self, reply: &Reply) -> Result<(), &'static str> {
self.app_panel.send_to_app(reply)
pub fn app_panel(&self) -> &SharedView<AppPanel> {
&self.app_panel
}

pub fn handle_target_control_event(&self, event: TargetControlEvent) {
Expand Down Expand Up @@ -112,7 +108,9 @@ impl IndependentPanelManager {
}

fn open_app_panel_internal(&self) -> anyhow::Result<()> {
App::get_or_load_app_library().map_err(|e| anyhow!(e.to_string()))?;
// Fail fast if library not available
let _ = App::get_or_load_app_library()?;
// Then open
self.app_panel.clone().open(reaper_main_window());
Ok(())
}
Expand Down
20 changes: 7 additions & 13 deletions main/src/infrastructure/ui/main_panel.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::infrastructure::ui::{
bindings::root, util, AppCallback, HeaderPanel, IndependentPanelManager, MappingRowsPanel,
SharedIndependentPanelManager, SharedMainState,
bindings::root, util, AppCallback, AppPanel, HeaderPanel, IndependentPanelManager,
MappingRowsPanel, SharedIndependentPanelManager, SharedMainState,
};

use lazycell::LazyCell;
Expand All @@ -24,6 +24,7 @@ use crate::infrastructure::server::http::{
send_projection_feedback_to_subscribed_clients, send_updated_controller_routing,
};
use crate::infrastructure::ui::util::{header_panel_height, parse_tags_from_csv};
use anyhow::anyhow;
use base::SoundPlayer;
use playtime_clip_engine::proto::{EventReply, Reply};
use rxrust::prelude::*;
Expand Down Expand Up @@ -294,19 +295,12 @@ impl MainPanel {
}
}

pub fn notify_app_is_ready(&self, callback: AppCallback) {
if let Some(data) = self.active_data.borrow() {
data.panel_manager.borrow().notify_app_is_ready(callback);
}
}

pub fn send_to_app(&self, reply: &Reply) -> Result<(), &'static str> {
let data = self
pub fn app_panel(&self) -> anyhow::Result<SharedView<AppPanel>> {
let activate_data = self
.active_data
.borrow()
.ok_or("main panel not active yet")?;
data.panel_manager.borrow().send_to_app(reply)?;
Ok(())
.ok_or_else(|| anyhow!("main panel not active yet"))?;
Ok(activate_data.panel_manager.borrow().app_panel().clone())
}

fn handle_target_control_event(&self, event: TargetControlEvent) {
Expand Down

0 comments on commit 4343e5c

Please sign in to comment.