From be15de6baab74d50767258112e785375eb661916 Mon Sep 17 00:00:00 2001
From: "Edgar San Martin, Jr." <[EMAIL REDACTED]>
Date: Thu, 20 Nov 2025 23:16:18 -0500
Subject: [PATCH] emscripten: Add SDL_SetWindowIcon implementation. (#14490)
---
src/video/emscripten/SDL_emscriptenvideo.c | 54 +++++++++++++++++++++-
1 file changed, 52 insertions(+), 2 deletions(-)
diff --git a/src/video/emscripten/SDL_emscriptenvideo.c b/src/video/emscripten/SDL_emscriptenvideo.c
index bb7f7ba8e4553..dc3a9680905d1 100644
--- a/src/video/emscripten/SDL_emscriptenvideo.c
+++ b/src/video/emscripten/SDL_emscriptenvideo.c
@@ -48,6 +48,7 @@ static void Emscripten_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window)
static SDL_FullscreenResult Emscripten_SetWindowFullscreen(SDL_VideoDevice *_this, SDL_Window *window, SDL_VideoDisplay *display, SDL_FullscreenOp fullscreen);
static void Emscripten_PumpEvents(SDL_VideoDevice *_this);
static void Emscripten_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window);
+static bool Emscripten_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon);
static bool pumpevents_has_run = false;
static int pending_swap_interval = -1;
@@ -157,8 +158,8 @@ static SDL_VideoDevice *Emscripten_CreateDevice(void)
device->CreateSDLWindow = Emscripten_CreateWindow;
device->SetWindowTitle = Emscripten_SetWindowTitle;
- /*device->SetWindowIcon = Emscripten_SetWindowIcon;
- device->SetWindowPosition = Emscripten_SetWindowPosition;*/
+ device->SetWindowIcon = Emscripten_SetWindowIcon;
+ /*device->SetWindowPosition = Emscripten_SetWindowPosition;*/
device->SetWindowSize = Emscripten_SetWindowSize;
device->SetWindowResizable = Emscripten_SetWindowResizable;
/*device->ShowWindow = Emscripten_ShowWindow;
@@ -716,4 +717,53 @@ static void Emscripten_SetWindowTitle(SDL_VideoDevice *_this, SDL_Window *window
emscripten_set_window_title(window->title);
}
+static bool Emscripten_SetWindowIcon(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *icon)
+{
+ // Create dynamic memory stream for PNG encoding
+ SDL_IOStream *stream = SDL_IOFromDynamicMem();
+ if (!stream) {
+ return false;
+ }
+
+ // Encode icon to PNG using SDL's native PNG encoder
+ if (!SDL_SavePNG_IO(icon, stream, false)) {
+ SDL_CloseIO(stream);
+ return false;
+ }
+
+ // Get the PNG data from the stream
+ void *png_data = SDL_GetPointerProperty(
+ SDL_GetIOProperties(stream),
+ SDL_PROP_IOSTREAM_DYNAMIC_MEMORY_POINTER,
+ NULL
+ );
+ size_t png_size = (size_t)SDL_GetIOSize(stream);
+
+ // Pass PNG data to JavaScript
+ MAIN_THREAD_EM_ASM({
+ var pngData = HEAPU8.subarray($0, $0 + $1);
+ var blob = new Blob([pngData], {type: 'image/png'});
+ var url = URL.createObjectURL(blob);
+
+ // Set as favicon (create link element if it doesn't exist)
+ var link = document.querySelector("link[rel~='icon']");
+ if (!link) {
+ link = document.createElement('link');
+ link.rel = 'icon';
+ link.type = 'image/png';
+ document.head.appendChild(link);
+ }
+
+ // Revoke old URL if it exists to prevent memory leaks
+ if (link.href && link.href.startsWith('blob:')) {
+ URL.revokeObjectURL(link.href);
+ }
+
+ link.href = url;
+ }, png_data, png_size);
+
+ SDL_CloseIO(stream);
+ return true;
+}
+
#endif // SDL_VIDEO_DRIVER_EMSCRIPTEN