SDL: Add protocol activation URI support for WinRT (#14309)

From 3e1cebed3c1e3e4529b0305bd65cc52bed4033da Mon Sep 17 00:00:00 2001
From: danprice142 <[EMAIL REDACTED]>
Date: Fri, 24 Oct 2025 18:34:44 +0100
Subject: [PATCH] Add protocol activation URI support for WinRT (#14309)

---
 include/SDL_system.h                     | 16 ++++++++++++++
 src/core/winrt/SDL_winrtapp_direct3d.cpp | 28 ++++++++++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/include/SDL_system.h b/include/SDL_system.h
index 7d11a5bf50253..fb0135a84a282 100644
--- a/include/SDL_system.h
+++ b/include/SDL_system.h
@@ -572,6 +572,22 @@ extern DECLSPEC const char * SDLCALL SDL_WinRTGetFSPathUTF8(SDL_WinRT_Path pathT
  */
 extern DECLSPEC SDL_WinRT_DeviceFamily SDLCALL SDL_WinRTGetDeviceFamily();
 
+/**
+ * Get the protocol activation URI if the app was launched via protocol activation.
+ *
+ * When a UWP/WinRT app is launched via a custom URI scheme (e.g., myapp://action?param=value),
+ * this function retrieves the full activation URI string.
+ * 
+ * The URI is only available once per activation - after the first successful call,
+ * subsequent calls will return NULL. This ensures the URI is processed only once.
+ *
+ * \returns the protocol activation URI as a UTF-8 string that must be freed with SDL_free(),
+ *          or NULL if the app was not activated via protocol or the URI was already retrieved.
+ *
+ * \since This function is available since SDL 2.33.0.
+ */
+extern DECLSPEC char * SDLCALL SDL_WinRTGetProtocolActivationURI(void);
+
 #endif /* __WINRT__ */
 
 /**
diff --git a/src/core/winrt/SDL_winrtapp_direct3d.cpp b/src/core/winrt/SDL_winrtapp_direct3d.cpp
index 04c655dda94f7..cd9118f2e4241 100644
--- a/src/core/winrt/SDL_winrtapp_direct3d.cpp
+++ b/src/core/winrt/SDL_winrtapp_direct3d.cpp
@@ -43,6 +43,7 @@ extern "C" {
 #include "SDL_hints.h"
 #include "SDL_main.h"
 #include "SDL_stdinc.h"
+#include "SDL_system.h"
 #include "SDL_render.h"
 #include "../../video/SDL_sysvideo.h"
 #include "../../events/SDL_events_c.h"
@@ -80,6 +81,9 @@ extern "C" void D3D11_Trim(SDL_Renderer *);
 // SDL_CreateWindow().
 SDL_WinRTApp ^ SDL_WinRTGlobalApp = nullptr;
 
+// Protocol activation URI storage
+static Platform::String^ WINRT_ProtocolActivationURI = nullptr;
+
 ref class SDLApplicationSource sealed : Windows::ApplicationModel::Core::IFrameworkViewSource
 {
   public:
@@ -107,6 +111,22 @@ int SDL_WinRTInitNonXAMLApp(int (*mainFunction)(int, char **))
     return 0;
 }
 
+extern "C" char* SDL_WinRTGetProtocolActivationURI(void)
+{
+    if (WINRT_ProtocolActivationURI == nullptr)
+        return nullptr;
+    Platform::String^ uriStr = WINRT_ProtocolActivationURI;
+    const wchar_t* uriWide = uriStr->Data();
+    int uriLen = WideCharToMultiByte(CP_UTF8, 0, uriWide, -1, nullptr, 0, nullptr, nullptr);
+    char* uriUtf8 = (char*)SDL_malloc(uriLen);
+    if (uriUtf8)
+        WideCharToMultiByte(CP_UTF8, 0, uriWide, -1, uriUtf8, uriLen, nullptr, nullptr);
+
+    // Clear after first successful read (only return it once)
+    WINRT_ProtocolActivationURI = nullptr;
+    return uriUtf8;
+}
+
 static void WINRT_ProcessWindowSizeChange() // TODO: Pass an SDL_Window-identifying thing into WINRT_ProcessWindowSizeChange()
 {
     CoreWindow ^ coreWindow = CoreWindow::GetForCurrentThread();
@@ -589,6 +609,14 @@ void SDL_WinRTApp::OnWindowClosed(CoreWindow ^ sender, CoreWindowEventArgs ^ arg
 
 void SDL_WinRTApp::OnAppActivated(CoreApplicationView ^ applicationView, IActivatedEventArgs ^ args)
 {
+    if (args->Kind == Windows::ApplicationModel::Activation::ActivationKind::Protocol)
+    {
+        auto protocolArgs = dynamic_cast<Windows::ApplicationModel::Activation::ProtocolActivatedEventArgs^>(args);
+        if (protocolArgs && protocolArgs->Uri)
+        {
+            WINRT_ProtocolActivationURI = protocolArgs->Uri->AbsoluteUri;
+        }
+    }
     CoreWindow::GetForCurrentThread()->Activate();
 }