11#include " deck_layout.h"
22#include " deck_gamepad.h"
33#include " polaris_game_fixture.h"
4+ #include " stream/deck_moonlight_handoff_preflight.h"
45
56#include < QClipboard>
67#include < QSocketNotifier>
@@ -246,6 +247,115 @@ QVariantMap toPreviewCopyActionModel(const nova::deck::DeckLaunchPreviewCopyActi
246247 model.insert (" executable" , copyAction.executable );
247248 return model;
248249}
250+
251+ QString moonlightHandoffVerdictLabel (const nova::deck::stream::DeckMoonlightHandoffVerdict verdict) {
252+ using nova::deck::stream::DeckMoonlightHandoffVerdict;
253+ switch (verdict) {
254+ case DeckMoonlightHandoffVerdict::ReadyForReview:
255+ return QStringLiteral (" ready_for_review" );
256+ case DeckMoonlightHandoffVerdict::BlockedStatic:
257+ return QStringLiteral (" blocked_static" );
258+ case DeckMoonlightHandoffVerdict::ForbiddenRuntimeBoundary:
259+ return QStringLiteral (" forbidden_runtime_boundary" );
260+ }
261+ return QStringLiteral (" unknown" );
262+ }
263+
264+ QString moonlightHandoffSurfaceLabel (const nova::deck::stream::DeckMoonlightHandoffSurface surface) {
265+ using nova::deck::stream::DeckMoonlightHandoffSurface;
266+ switch (surface) {
267+ case DeckMoonlightHandoffSurface::MoonlightQtCli:
268+ return QStringLiteral (" moonlight_qt_cli" );
269+ case DeckMoonlightHandoffSurface::HostAppSnapshot:
270+ return QStringLiteral (" host_app_snapshot" );
271+ case DeckMoonlightHandoffSurface::DesktopEntry:
272+ return QStringLiteral (" desktop_entry" );
273+ case DeckMoonlightHandoffSurface::FlatpakIdentity:
274+ return QStringLiteral (" flatpak_identity" );
275+ case DeckMoonlightHandoffSurface::SteamShortcut:
276+ return QStringLiteral (" steam_shortcut" );
277+ case DeckMoonlightHandoffSurface::CustomUri:
278+ return QStringLiteral (" custom_uri" );
279+ case DeckMoonlightHandoffSurface::NovaOwnedCommonCFuture:
280+ return QStringLiteral (" nova_owned_common_c_future" );
281+ case DeckMoonlightHandoffSurface::Unsupported:
282+ return QStringLiteral (" unsupported" );
283+ }
284+ return QStringLiteral (" unknown" );
285+ }
286+
287+ QVariantList toStringListModel (const std::vector<std::string>& values) {
288+ QVariantList model;
289+ for (const auto & value : values) {
290+ model.append (toQString (value));
291+ }
292+ return model;
293+ }
294+
295+ QString argvPreviewFor (const std::vector<std::string>& tokens) {
296+ if (tokens.size () == 4 ) {
297+ return QStringLiteral (" Typed argv plan: app token + stream action + redacted host selector + " )
298+ + toQString (tokens[3 ]);
299+ }
300+ return QStringLiteral (" Typed argv plan unavailable until the preflight is ready for review." );
301+ }
302+
303+ QVariantMap toMoonlightHandoffPreflightModel (
304+ const nova::deck::stream::DeckMoonlightHandoffPreflightResult& result) {
305+ QVariantMap model;
306+ model.insert (" verdict" , moonlightHandoffVerdictLabel (result.verdict ));
307+ model.insert (" candidateSurface" , moonlightHandoffSurfaceLabel (result.candidatePlan .surface ));
308+ model.insert (" publicPreviewCopy" , toQString (result.publicPreviewCopy ));
309+ model.insert (" publicSummary" , toQString (result.candidatePlan .publicSummary ));
310+ model.insert (" argvTokens" , toStringListModel (result.candidatePlan .argvTokens ));
311+ model.insert (" argvTokenCount" , static_cast <int >(result.candidatePlan .argvTokens .size ()));
312+ model.insert (" argvPreview" , argvPreviewFor (result.candidatePlan .argvTokens ));
313+ model.insert (" sourceSurface" , toQString (result.focusReturnPlan .sourceSurface ));
314+ model.insert (" intendedReturnTarget" , toQString (result.focusReturnPlan .intendedReturnTarget ));
315+ model.insert (" focusFallbackCopy" , toQString (result.focusReturnPlan .fallbackCopy ));
316+ model.insert (" focusConfidence" , toQString (result.focusReturnPlan .confidence ));
317+ model.insert (" safeToRender" , result.safeToRender );
318+ model.insert (" executable" , result.executable );
319+ model.insert (" allowsNetwork" , result.allowsNetwork );
320+ model.insert (" allowsProcessExecution" , result.allowsProcessExecution );
321+ model.insert (" allowsMoonlight" , result.allowsMoonlight );
322+ model.insert (" allowsHostMutation" , result.allowsHostMutation );
323+ return model;
324+ }
325+
326+ nova::deck::stream::DeckMoonlightHandoffPreflightResult resolveMoonlightHandoffPreflightFor (
327+ const QString& hostDisplayNamePublic,
328+ const QString& gameTitlePublic,
329+ const bool hasSafeSnapshot,
330+ const bool appPresentInSnapshot) {
331+ return nova::deck::stream::resolveDeckMoonlightHandoffPreflight (
332+ nova::deck::stream::DeckMoonlightHandoffPreflightRequest{
333+ .hostDisplayNamePublic = hostDisplayNamePublic.toStdString (),
334+ .gameTitlePublic = gameTitlePublic.toStdString (),
335+ .privateHostSelectorRedactedForDebug = " redacted-host-selector" ,
336+ .requestedSurface = nova::deck::stream::DeckMoonlightHandoffSurface::MoonlightQtCli,
337+ .hasSafeSnapshot = hasSafeSnapshot,
338+ .appPresentInSnapshot = appPresentInSnapshot,
339+ });
340+ }
341+
342+ class QtMoonlightHandoffPreflightBridge final : public QObject {
343+ Q_OBJECT
344+ public:
345+ using QObject::QObject;
346+
347+ Q_INVOKABLE QVariantMap resolve (
348+ const QString& hostDisplayNamePublic,
349+ const QString& gameTitlePublic,
350+ const bool hasSafeSnapshot,
351+ const bool appPresentInSnapshot) const {
352+ return toMoonlightHandoffPreflightModel (resolveMoonlightHandoffPreflightFor (
353+ hostDisplayNamePublic,
354+ gameTitlePublic,
355+ hasSafeSnapshot,
356+ appPresentInSnapshot));
357+ }
358+ };
249359} // namespace
250360
251361int main (int argc, char *argv[]) {
@@ -268,8 +378,15 @@ int main(int argc, char *argv[]) {
268378 const auto & launchPreviewCopyAction = selectedBinding.copyAction ;
269379
270380 QtLocalClipboardBridge localClipboard;
381+ QtMoonlightHandoffPreflightBridge moonlightHandoffBridge;
271382 QtDeckGamepadBridge gamepadBridge;
272383
384+ const auto initialMoonlightHandoffPreflight = resolveMoonlightHandoffPreflightFor (
385+ toQString (selectedBinding.selectedHostName ),
386+ toQString (selectedBinding.selectedGameTitle ),
387+ sampleLibrary.readOnly ,
388+ !sampleLibrary.games .empty ());
389+
273390 QQmlApplicationEngine engine;
274391 engine.rootContext ()->setContextProperty (" novaDeckShellName" , toQString (profile.shellName ));
275392 engine.rootContext ()->setContextProperty (" novaDeckWidth" , profile.width );
@@ -286,6 +403,8 @@ int main(int argc, char *argv[]) {
286403 engine.rootContext ()->setContextProperty (" novaLaunchIntentBoundary" , toLaunchIntentBoundaryModel (launchIntent.boundary ));
287404 engine.rootContext ()->setContextProperty (" novaLaunchIntentPreview" , toLaunchIntentPreviewModel (launchIntent, streamIntent));
288405 engine.rootContext ()->setContextProperty (" novaLaunchPreviewCopyAction" , toPreviewCopyActionModel (launchPreviewCopyAction));
406+ engine.rootContext ()->setContextProperty (" novaMoonlightHandoffPreflight" , toMoonlightHandoffPreflightModel (initialMoonlightHandoffPreflight));
407+ engine.rootContext ()->setContextProperty (" novaMoonlightHandoffPreflightBridge" , &moonlightHandoffBridge);
289408 engine.rootContext ()->setContextProperty (" novaLocalClipboard" , &localClipboard);
290409 engine.rootContext ()->setContextProperty (" novaGamepad" , &gamepadBridge);
291410 engine.rootContext ()->setContextProperty (" novaInitialHostFocusTarget" , toQString (nova::deck::initialHostFocusTarget (libraryHosts)));
0 commit comments