mirror of
https://github.com/logos-co/logos-view-module-runtime.git
synced 2026-08-27 19:11:16 +00:00
* feat(windows): wire the view-module-runtime for x86_64-windows Routes through logos-nix.lib.forAllTargets, widens meta.platforms, and picks up Qt's host-tool cmake flags (empty natively). wrapQtAppsHook is GATED rather than removed: it fails to EVALUATE for a mingw host, and wrap-qt-apps-hook.sh would skip a PE anyway (`isELF || isMachO || continue`) -- but removing it outright would change native behaviour. dontWrapQtApps = true is the mandatory other half, or qtbase's own setup hook hard-errors with "depends on qtbase, but no wrapping behavior was specified". Windows and native (aarch64-darwin) both evaluate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(windows): quit ui-host with WM_QUIT instead of hard-killing it QProcess::terminate() cannot stop ui-host on Windows, so every UI-plugin teardown paid the full 3s grace period and then TerminateProcess. MEASURED, not inferred. terminate() on Windows is EnumWindows(WM_CLOSE) plus PostThreadMessage(tid, WM_CLOSE) (qprocess_win.cpp:648-653). ui-host owns ZERO windows -- it is a bare QCoreApplication linking no Qt6::Gui at all (ui-host/main.cpp:70, CMakeLists.txt:78-86); the QQuickWidget lives in the PARENT and ui-host only remotes the plugin object over QtRO. So the enumeration finds nothing. A controlled zero, not a broken command: the same enumeration in the same run found 7 windows for Basecamp, including the two never-shown QPA observer windows a GUI process must own. The thread message IS delivered, but Qt's dispatcher only branches on WM_QUIT (qeventdispatcher_win.cpp:545-548) -- WM_CLOSE falls through to a no-op for a thread message. terminate() returns void, so it "succeeds" having done nothing. A/B against the shipped ui-host.exe: replicating terminate() byte-exactly left it ALIVE past the full 3000ms; PostThreadMessage(WM_QUIT) exited it in 8ms with code 0. End to end through the real teardown path (graceful Basecamp quit with package_manager_ui loaded), same harness and trigger, only the payload differing: shipped ui-host exit 3905ms code 62097 (0xF291 Qt kill) "did not exit gracefully" present fixed ui-host exit 690ms code 0 warning absent (Basecamp total 4395ms -> 1186ms) REPLACES terminate() on Windows rather than preceding it. An earlier draft inserted WM_QUIT above the existing terminate()+3000+kill+1000, which would have made the worst case 6000ms -- worse than today while claiming to improve it. Since terminate() was measured to be dead time here, keeping it buys nothing: the ceiling stays 4000ms and the typical case is now sub-second. This also matches logos-container-subprocess, which REPLACES its equally no-op request_exit() rather than supplementing it. The POSIX path is byte-identical -- the original block is preserved verbatim inside #else; git diff shows zero removed lines. Getting the child's main THREAD id is the one subtle part. QProcess does not expose it, but CreateProcessArguments carries the PROCESS_INFORMATION pointer it hands to CreateProcess. The modifier runs BEFORE CreateProcess, so dwThreadId is not populated yet: capture the pointer there, read through it once started() has fired, and null it immediately -- QProcess owns that allocation and frees it in cleanup(). No CreateProcessW reimplementation and no Toolhelp32 snapshot needed. NOT fixed here, deliberately: orphan reaping is absent on Windows. ui-host's setsid + PR_SET_PDEATHSIG + getppid watchdog are wholly inside #ifndef _WIN32 (ui-host/main.cpp:40-68) and ViewModuleHost assigns no Job Object, so a Basecamp crash leaks ui-host.exe. That is a real second defect, but it is an untested process-lifetime change and does not belong bundled into a latency fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deps): re-pin the L1-L4 inputs to their merged revs logos-nix (L1), logos-protocol (L2), logos-cpp-sdk (L3) and logos-qt-sdk (L4) are all on their default branches now, so the lock can name the merged revs instead of the pre-merge branch tips it was resolving against while those PRs were open. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
45 lines
1.3 KiB
C++
45 lines
1.3 KiB
C++
#pragma once
|
|
|
|
#include <QObject>
|
|
#include <QProcess>
|
|
#include <QString>
|
|
|
|
class QLocalServer;
|
|
|
|
class ViewModuleHost : public QObject {
|
|
Q_OBJECT
|
|
public:
|
|
explicit ViewModuleHost(QObject* parent = nullptr);
|
|
~ViewModuleHost();
|
|
|
|
bool spawn(const QString& moduleName, const QString& pluginPath,
|
|
const QString& authToken);
|
|
void stop();
|
|
bool isRunning() const;
|
|
QString socketName() const;
|
|
|
|
signals:
|
|
void processExited(int exitCode);
|
|
void ready();
|
|
|
|
private:
|
|
QProcess* m_process = nullptr;
|
|
QLocalServer* m_tokenServer = nullptr;
|
|
QString m_moduleName;
|
|
QString m_socketName;
|
|
QByteArray m_stdoutBuffer;
|
|
bool m_readyEmitted = false;
|
|
#ifdef Q_OS_WIN
|
|
// The child's MAIN THREAD id, which is what stop() posts WM_QUIT to.
|
|
// QProcess does not expose it directly, but CreateProcessArguments carries
|
|
// the PROCESS_INFORMATION pointer it hands to CreateProcess -- capture the
|
|
// pointer in the modifier (which runs BEFORE CreateProcess, so dwThreadId is
|
|
// not filled in yet) and read it once started() has fired.
|
|
//
|
|
// m_procInfo is owned by QProcess and deleted in its cleanup(); it is nulled
|
|
// as soon as started() reads through it and is never dereferenced elsewhere.
|
|
void* m_procInfo = nullptr;
|
|
unsigned long m_mainThreadId = 0; // DWORD
|
|
#endif
|
|
};
|