add logging

This commit is contained in:
Boof2015
2026-06-03 09:15:18 -04:00
parent 67f1c46862
commit 33b39626aa
3 changed files with 316 additions and 15 deletions
+41 -3
View File
@@ -44,9 +44,42 @@ endif()
set(PRISM_PLUGIN_FORMATS "${PRISM_DEFAULT_PLUGIN_FORMATS}" CACHE STRING "JUCE plugin formats to build")
option(PRISM_COPY_PLUGIN_AFTER_BUILD "Copy built plugins into user plugin folders after build" ON)
set(PRISM_LINUX_UI_MODE "webview" CACHE STRING "Linux-only UI diagnostic mode: webview, web_smoke, native_smoke, or floating_webview")
set_property(CACHE PRISM_LINUX_UI_MODE PROPERTY STRINGS webview web_smoke native_smoke floating_webview)
set(PRISM_VALID_LINUX_UI_MODES webview web_smoke native_smoke floating_webview)
if(UNIX AND NOT APPLE)
list(FIND PRISM_VALID_LINUX_UI_MODES "${PRISM_LINUX_UI_MODE}" PRISM_LINUX_UI_MODE_INDEX)
if(PRISM_LINUX_UI_MODE_INDEX EQUAL -1)
message(FATAL_ERROR "Invalid PRISM_LINUX_UI_MODE='${PRISM_LINUX_UI_MODE}'. Expected one of: ${PRISM_VALID_LINUX_UI_MODES}")
endif()
else()
if(NOT PRISM_LINUX_UI_MODE STREQUAL "webview")
message(FATAL_ERROR "PRISM_LINUX_UI_MODE is Linux-only; macOS/Windows builds must use 'webview'.")
endif()
endif()
set(PRISM_NEEDS_WEBUI OFF)
if(NOT PRISM_DEV_SERVER)
if(NOT UNIX OR APPLE OR PRISM_LINUX_UI_MODE STREQUAL "webview" OR PRISM_LINUX_UI_MODE STREQUAL "floating_webview")
set(PRISM_NEEDS_WEBUI ON)
endif()
endif()
if(PRISM_NEEDS_WEBUI)
set(PRISM_EMBED_WEBUI_DEFINITION PRISM_EMBED_WEBUI=1)
else()
set(PRISM_EMBED_WEBUI_DEFINITION PRISM_EMBED_WEBUI=0)
endif()
string(TOUPPER "${PRISM_LINUX_UI_MODE}" PRISM_LINUX_UI_MODE_UPPER)
set(PRISM_LINUX_UI_MODE_DEFINITIONS
PRISM_LINUX_UI_MODE_NAME="${PRISM_LINUX_UI_MODE}"
PRISM_LINUX_UI_MODE_${PRISM_LINUX_UI_MODE_UPPER}=1)
# Embed the built webview bundle once; every scope plugin shares it (the C++ tells
# the UI which scope to mount via initialisation data).
if(NOT PRISM_DEV_SERVER)
if(PRISM_NEEDS_WEBUI)
set(PRISM_WEBUI_DIST ${CMAKE_CURRENT_SOURCE_DIR}/webview-dist)
file(GLOB_RECURSE PRISM_WEBUI_FILES "${PRISM_WEBUI_DIST}/*")
if(NOT PRISM_WEBUI_FILES)
@@ -97,7 +130,9 @@ function(add_prism_scope TARGET PRODUCT PLUGIN_CODE SCOPE_DEFINE)
target_compile_definitions(${TARGET} PRIVATE
JUCE_WEB_BROWSER=1 # enables WebBrowserComponent (WKWebView on macOS)
JUCE_USE_CURL=0
JUCE_VST3_CAN_REPLACE_VST2=0)
JUCE_VST3_CAN_REPLACE_VST2=0
${PRISM_EMBED_WEBUI_DEFINITION}
${PRISM_LINUX_UI_MODE_DEFINITIONS})
if(WIN32)
# NEEDS_WEBVIEW2 links the WebView2 SDK but doesn't turn on the JUCE compile
# paths that gate withResourceProvider. JUCE_USE_WIN_WEBVIEW2=1 enables the
@@ -121,10 +156,13 @@ function(add_prism_scope TARGET PRODUCT PLUGIN_CODE SCOPE_DEFINE)
if(PRISM_DEV_SERVER)
target_compile_definitions(${TARGET} PRIVATE PRISM_USE_DEV_SERVER=1)
else()
target_link_libraries(${TARGET} PRIVATE PrismWebUI)
target_compile_definitions(${TARGET} PRIVATE PRISM_USE_DEV_SERVER=0)
endif()
if(PRISM_NEEDS_WEBUI)
target_link_libraries(${TARGET} PRIVATE PrismWebUI)
endif()
target_link_libraries(${TARGET} PRIVATE
juce::juce_audio_utils
juce::juce_gui_extra
+262 -11
View File
@@ -7,9 +7,18 @@
#include "SpectrogramEngine.h"
#include "WaveformEngine.h"
#include <cmath>
#include <cstdlib>
#include <cstring>
#if ! PRISM_USE_DEV_SERVER
#ifndef PRISM_EMBED_WEBUI
#define PRISM_EMBED_WEBUI 0
#endif
#ifndef PRISM_LINUX_UI_MODE_NAME
#define PRISM_LINUX_UI_MODE_NAME "webview"
#endif
#if PRISM_EMBED_WEBUI
#include "BinaryData.h"
#endif
@@ -41,6 +50,77 @@ namespace
#if JUCE_LINUX
constexpr int kLinuxFrameRateHz = 20;
juce::String envValue(const char* name)
{
if (const auto* value = std::getenv(name); value != nullptr && value[0] != '\0')
return value;
return "<unset>";
}
juce::File linuxDiagnosticLogFile()
{
juce::File baseDir;
if (const auto* xdgState = std::getenv("XDG_STATE_HOME"); xdgState != nullptr && xdgState[0] != '\0')
baseDir = juce::File(juce::String(xdgState));
else if (const auto* home = std::getenv("HOME"); home != nullptr && home[0] != '\0')
baseDir = juce::File(juce::String(home)).getChildFile(".local").getChildFile("state");
else
baseDir = juce::File::getSpecialLocation(juce::File::userApplicationDataDirectory);
return baseDir.getChildFile("prism").getChildFile("plugin-ui-diagnostics.log");
}
void logLinuxDiagnostic(const juce::String& message)
{
const auto file = linuxDiagnosticLogFile();
file.getParentDirectory().createDirectory();
file.appendText(juce::Time::getCurrentTime().toISO8601(true) + " " + message + "\n",
false,
false,
"\n");
}
juce::String wrapperTypeName()
{
switch (juce::PluginHostType::getPluginLoadedAs())
{
case juce::AudioProcessor::wrapperType_VST3: return "VST3";
case juce::AudioProcessor::wrapperType_VST: return "VST2";
case juce::AudioProcessor::wrapperType_Standalone: return "Standalone";
case juce::AudioProcessor::wrapperType_AudioUnit: return "AudioUnit";
case juce::AudioProcessor::wrapperType_AudioUnitv3:return "AudioUnitv3";
case juce::AudioProcessor::wrapperType_AAX: return "AAX";
case juce::AudioProcessor::wrapperType_LV2: return "LV2";
case juce::AudioProcessor::wrapperType_Unity: return "Unity";
default: return "Undefined";
}
}
juce::String linuxHostDisplayContext()
{
const juce::PluginHostType host;
juce::String context;
context << "mode=" << PRISM_LINUX_UI_MODE_NAME
<< " host=\"" << host.getHostDescription() << "\""
<< " wrapper=" << wrapperTypeName()
<< " display=" << envValue("DISPLAY")
<< " waylandDisplay=" << envValue("WAYLAND_DISPLAY")
<< " sessionType=" << envValue("XDG_SESSION_TYPE")
<< " gdkBackend=" << envValue("GDK_BACKEND")
<< " desktop=" << envValue("XDG_CURRENT_DESKTOP");
return context;
}
juce::String describeBounds(const juce::Rectangle<int>& bounds)
{
juce::String text;
text << bounds.getX() << "," << bounds.getY()
<< " " << bounds.getWidth() << "x" << bounds.getHeight();
return text;
}
#endif
std::unique_ptr<ScopeEngine> makeEngine()
@@ -64,7 +144,7 @@ namespace
#if PRISM_USE_DEV_SERVER
const juce::String kDevServerUrl { "http://localhost:5174" };
#else
#elif PRISM_EMBED_WEBUI
const char* getWebResourceData(const juce::String& name, int& dataSize)
{
dataSize = 0;
@@ -140,6 +220,47 @@ namespace
#endif
#endif
#if JUCE_LINUX
juce::String makeWebSmokeUrl()
{
const juce::String html =
"<!doctype html><html><head><meta charset=\"utf-8\">"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"
"<style>html,body{width:100%;height:100%;margin:0;background:#000;color:rgba(255,255,255,.82);"
"font:12px/1.5 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;}body{display:grid;place-items:center;}"
"#status{padding:20px;text-align:center;white-space:pre-wrap;}</style></head>"
"<body><div id=\"status\">Prism WebView smoke test\\nWaiting for JUCE bridge...</div>"
"<script>"
"function ready(){var s=document.getElementById('status');"
"try{if(window.__JUCE__&&window.__JUCE__.backend){window.__JUCE__.backend.emitEvent('prismReady',{});"
"if(s)s.textContent='Prism WebView smoke test\\nJUCE bridge emitted prismReady';return;}}"
"catch(e){if(s)s.textContent='Prism WebView smoke test\\n'+String(e&&e.message||e);return;}"
"setTimeout(ready,50);}ready();"
"</script></body></html>";
return "data:text/html;charset=utf-8," + juce::URL::addEscapeChars(html, false);
}
class FloatingWebViewWindow final : public juce::DocumentWindow
{
public:
FloatingWebViewWindow()
: juce::DocumentWindow("Prism WebView diagnostic",
juce::Colours::black,
juce::DocumentWindow::closeButton)
{
setUsingNativeTitleBar(true);
setResizable(true, true);
}
void closeButtonPressed() override
{
logLinuxDiagnostic("floating_webview closeButtonPressed");
setVisible(false);
}
};
#endif
juce::WebBrowserComponent::Options makeWebOptions(PrismSpectrumEditor& editor, const char* scopeId)
{
auto options = juce::WebBrowserComponent::Options{}
@@ -168,7 +289,7 @@ namespace
juce::WebBrowserComponent::Options::WinWebView2{}.withUserDataFolder(webView2DataDir));
#endif
#if ! PRISM_USE_DEV_SERVER
#if PRISM_EMBED_WEBUI
options = options.withResourceProvider(
[](const auto& url) { return provideResource(url); },
getResourceProviderOrigin());
@@ -185,7 +306,13 @@ PrismSpectrumEditor::PrismSpectrumEditor(PrismSpectrumProcessor& p)
drainLeft.assign((size_t) kDrainCapacity, 0.0f);
drainRight.assign((size_t) kDrainCapacity, 0.0f);
loadWebView();
#if JUCE_LINUX
logLinuxDiagnostic("editor constructed scope=" + juce::String(engine->scopeId())
+ " " + linuxHostDisplayContext()
+ " logFile=" + linuxDiagnosticLogFile().getFullPathName());
#endif
loadUi();
// Per-scope sizing: open at the scope's preferred default, with a per-scope min.
const auto pref = engine->preferredSize();
@@ -198,19 +325,50 @@ PrismSpectrumEditor::~PrismSpectrumEditor()
{
#if JUCE_WINDOWS || JUCE_LINUX
stopTimer();
#endif
#if JUCE_LINUX
logLinuxDiagnostic("editor destructed scope=" + juce::String(engine->scopeId())
+ " mode=" + juce::String(PRISM_LINUX_UI_MODE_NAME));
floatingWebViewWindow.reset();
#endif
webViewReady = false;
}
void PrismSpectrumEditor::resized()
{
if (webView != nullptr)
if (webView != nullptr && webView->getParentComponent() == this)
webView->setBounds(getLocalBounds());
webViewFallback.setBounds(getLocalBounds());
#if JUCE_LINUX
logLinuxDiagnostic("editor resized local=" + describeBounds(getLocalBounds())
+ " screen=" + describeBounds(getScreenBounds())
+ " mode=" + juce::String(PRISM_LINUX_UI_MODE_NAME));
#endif
}
void PrismSpectrumEditor::loadWebView()
void PrismSpectrumEditor::loadUi()
{
#if JUCE_LINUX
logLinuxDiagnostic("loadUi " + linuxHostDisplayContext());
#if defined(PRISM_LINUX_UI_MODE_NATIVE_SMOKE) && PRISM_LINUX_UI_MODE_NATIVE_SMOKE
loadNativeSmoke();
return;
#elif defined(PRISM_LINUX_UI_MODE_WEB_SMOKE) && PRISM_LINUX_UI_MODE_WEB_SMOKE
loadWebSmoke();
return;
#elif defined(PRISM_LINUX_UI_MODE_FLOATING_WEBVIEW) && PRISM_LINUX_UI_MODE_FLOATING_WEBVIEW
loadFloatingWebView();
return;
#endif
#endif
loadEmbeddedWebView();
}
bool PrismSpectrumEditor::createWebView()
{
auto options = makeWebOptions(*this, engine->scopeId());
@@ -218,29 +376,118 @@ void PrismSpectrumEditor::loadWebView()
if (! juce::WebBrowserComponent::areOptionsSupported(options))
{
showWebViewFallback();
return;
return false;
}
#endif
#if JUCE_LINUX
logLinuxDiagnostic("creating WebBrowserComponent mode=" + juce::String(PRISM_LINUX_UI_MODE_NAME));
#endif
webView = std::make_unique<juce::WebBrowserComponent>(options);
return true;
}
void PrismSpectrumEditor::loadEmbeddedWebView()
{
if (! createWebView())
return;
addAndMakeVisible(*webView);
#if PRISM_USE_DEV_SERVER
#if JUCE_LINUX
logLinuxDiagnostic("navigating embedded WebView url=" + kDevServerUrl);
#endif
webView->goToURL(kDevServerUrl);
#else
#if JUCE_LINUX
#elif PRISM_EMBED_WEBUI
#if JUCE_LINUX
const auto indexFile = writeLinuxWebViewIndexFile();
if (indexFile.existsAsFile())
{
webView->goToURL(juce::URL(indexFile).toString(false));
const auto url = juce::URL(indexFile).toString(false);
logLinuxDiagnostic("navigating embedded WebView url=" + url);
webView->goToURL(url);
return;
}
#endif
logLinuxDiagnostic("navigating embedded WebView url=" + juce::WebBrowserComponent::getResourceProviderRoot());
#endif
webView->goToURL(juce::WebBrowserComponent::getResourceProviderRoot());
#endif
}
#if JUCE_LINUX
void PrismSpectrumEditor::showLinuxDiagnosticPlaceholder(const juce::String& text)
{
webViewFallback.setText(text, juce::dontSendNotification);
webViewFallback.setJustificationType(juce::Justification::centred);
webViewFallback.setColour(juce::Label::backgroundColourId, juce::Colours::black);
webViewFallback.setColour(juce::Label::textColourId, juce::Colour(0xffe5e7eb));
webViewFallback.setMinimumHorizontalScale(0.7f);
addAndMakeVisible(webViewFallback);
}
void PrismSpectrumEditor::loadNativeSmoke()
{
logLinuxDiagnostic("native_smoke loaded");
webViewReady = true;
showLinuxDiagnosticPlaceholder("Prism native smoke test\n"
"Root JUCE editor rendered without WebView\n"
"Mode: native_smoke");
}
void PrismSpectrumEditor::loadWebSmoke()
{
if (! createWebView())
return;
addAndMakeVisible(*webView);
const auto url = makeWebSmokeUrl();
logLinuxDiagnostic("navigating web_smoke WebView url=data:text/html;charset=utf-8,<inline smoke page>");
webView->goToURL(url);
}
void PrismSpectrumEditor::loadFloatingWebView()
{
showLinuxDiagnosticPlaceholder("Prism floating WebView diagnostic\n"
"Embedded native placeholder is visible\n"
"The real Prism WebView should open in a separate window");
if (! createWebView())
return;
floatingWebViewWindow = std::make_unique<FloatingWebViewWindow>();
floatingWebViewWindow->setContentNonOwned(webView.get(), false);
const auto pref = engine->preferredSize();
floatingWebViewWindow->setSize(pref.defaultWidth, pref.defaultHeight);
floatingWebViewWindow->centreWithSize(pref.defaultWidth, pref.defaultHeight);
floatingWebViewWindow->setVisible(true);
floatingWebViewWindow->toFront(true);
#if PRISM_USE_DEV_SERVER
logLinuxDiagnostic("navigating floating WebView url=" + kDevServerUrl);
webView->goToURL(kDevServerUrl);
#elif PRISM_EMBED_WEBUI
const auto indexFile = writeLinuxWebViewIndexFile();
if (indexFile.existsAsFile())
{
const auto url = juce::URL(indexFile).toString(false);
logLinuxDiagnostic("navigating floating WebView url=" + url);
webView->goToURL(url);
return;
}
logLinuxDiagnostic("navigating floating WebView url=" + juce::WebBrowserComponent::getResourceProviderRoot());
webView->goToURL(juce::WebBrowserComponent::getResourceProviderRoot());
#endif
}
#endif
void PrismSpectrumEditor::showWebViewFallback()
{
webViewFallback.setText(
@@ -307,6 +554,10 @@ void PrismSpectrumEditor::onPrismConfig(juce::var payload)
void PrismSpectrumEditor::onPrismReady()
{
#if JUCE_LINUX
logLinuxDiagnostic("prismReady received mode=" + juce::String(PRISM_LINUX_UI_MODE_NAME)
+ " scope=" + juce::String(engine->scopeId()));
#endif
webViewReady = true;
pushRestoreSettings();
sendAppDefaults();
+13 -1
View File
@@ -50,11 +50,20 @@ public:
void sendAppDefaults();
private:
void loadWebView();
void loadUi();
bool createWebView();
void loadEmbeddedWebView();
void showWebViewFallback();
void startFrameDriver();
void renderFrame();
#if JUCE_LINUX
void loadNativeSmoke();
void loadWebSmoke();
void loadFloatingWebView();
void showLinuxDiagnosticPlaceholder(const juce::String& text);
#endif
PrismSpectrumProcessor& processorRef;
std::unique_ptr<ScopeEngine> engine;
@@ -71,6 +80,9 @@ private:
int uncapAttempts = 0;
std::unique_ptr<juce::WebBrowserComponent> webView;
#if JUCE_LINUX
std::unique_ptr<juce::DocumentWindow> floatingWebViewWindow;
#endif
juce::Label webViewFallback;
// Declared last so it is destroyed first; no vblank callback can fire into a