sdl2-compat: Merge pull request #73 from Kontrabant/sdl2_compat_popups

From 3a57fe39d3c01f88fa01635dd757c7c668d442e5 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 1 May 2023 11:49:02 -0400
Subject: [PATCH] Add basic popup support

Adds support for SDL3 popup windows in SDL2 applications.

Popup windows are positioned by translating global coordinates back to relative, except on Wayland, where this is not possible as Wayland windows don't expose their position in the global compositor space. As SDL3 requires popups to have a parent, first the window with mouse focus is tried, followed by keyboard focus if no application window currently has mouse focus.
---
 src/sdl2_compat.c | 49 +++++++++++++++++++++++++++++++++++++++++++++--
 src/sdl3_syms.h   |  1 +
 2 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/src/sdl2_compat.c b/src/sdl2_compat.c
index 0d41e05..54fdaeb 100644
--- a/src/sdl2_compat.c
+++ b/src/sdl2_compat.c
@@ -4593,11 +4593,14 @@ SDL_GetWindowFlags(SDL_Window *window)
     return flags;
 }
 
+#define POPUP_PARENT_PROP_STR "__SDL3_parentWnd"
+
 DECLSPEC SDL_Window * SDLCALL
 SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
 {
-    SDL_Window *window;
+    SDL_Window *window = NULL;
     int hidden = flags & SDL_WINDOW_HIDDEN;
+    const Uint32 is_popup = flags & (SDL_WINDOW_POPUP_MENU | SDL_WINDOW_TOOLTIP);
 
     flags &= ~SDL2_WINDOW_SHOWN;
     flags |= SDL_WINDOW_HIDDEN;
@@ -4606,7 +4609,19 @@ SDL_CreateWindow(const char *title, int x, int y, int w, int h, Uint32 flags)
         flags |= SDL_WINDOW_FULLSCREEN; /* This is fullscreen desktop for new windows */
     }
 
-    window = SDL3_CreateWindow(title, w, h, flags);
+    if (!is_popup) {
+        window = SDL3_CreateWindow(title, w, h, flags);
+    } else {
+        SDL_Window *parent = SDL3_GetMouseFocus();
+        if (!parent) {
+            parent = SDL3_GetKeyboardFocus();
+        }
+
+        if (parent) {
+            window = SDL3_CreatePopupWindow(parent, x, y, w, h, flags);
+            SDL3_SetWindowData(window, POPUP_PARENT_PROP_STR, parent);
+        }
+    }
     if (window) {
         if (!SDL_WINDOWPOS_ISUNDEFINED(x) || !SDL_WINDOWPOS_ISUNDEFINED(y)) {
             SDL3_SetWindowPosition(window, x, y);
@@ -4753,6 +4768,21 @@ SDL_DestroyRenderer(SDL_Renderer *renderer)
 DECLSPEC void SDLCALL
 SDL_SetWindowPosition(SDL_Window *window, int x, int y)
 {
+    /* Popup windows need to be transformed from global to relative coordinates. */
+    if (SDL3_GetWindowFlags(window) & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) {
+        SDL_Window *parent = SDL3_GetWindowData(window, POPUP_PARENT_PROP_STR);
+
+        while (parent) {
+            int x_off, y_off;
+            SDL3_GetWindowPosition(parent, &x_off, &y_off);
+
+            x -= x_off;
+            y -= y_off;
+
+            parent = SDL3_GetWindowData(parent, POPUP_PARENT_PROP_STR);
+        }
+    }
+
     SDL3_SetWindowPosition(window, x, y);
 }
 
@@ -4760,6 +4790,21 @@ DECLSPEC void SDLCALL
 SDL_GetWindowPosition(SDL_Window *window, int *x, int *y)
 {
     SDL3_GetWindowPosition(window, x, y);
+
+    /* Popup windows need to be transformed from relative to global coordinates. */
+    if (SDL3_GetWindowFlags(window) & (SDL_WINDOW_TOOLTIP | SDL_WINDOW_POPUP_MENU)) {
+        SDL_Window *parent = SDL3_GetWindowData(window, POPUP_PARENT_PROP_STR);
+
+        while (parent) {
+            int x_off, y_off;
+            SDL3_GetWindowPosition(parent, &x_off, &y_off);
+
+            *x += x_off;
+            *y += y_off;
+
+            parent = SDL3_GetWindowData(parent, POPUP_PARENT_PROP_STR);
+        }
+    }
 }
 
 DECLSPEC void SDLCALL
diff --git a/src/sdl3_syms.h b/src/sdl3_syms.h
index 69b5dd5..5d604c8 100644
--- a/src/sdl3_syms.h
+++ b/src/sdl3_syms.h
@@ -483,6 +483,7 @@ SDL3_SYM(const SDL_DisplayMode*,GetCurrentDisplayMode,(SDL_DisplayID a),(a),retu
 SDL3_SYM(int,SetWindowFullscreenMode,(SDL_Window *a, const SDL_DisplayMode *b),(a,b),return)
 SDL3_SYM_PASSTHROUGH(Uint32,GetWindowPixelFormat,(SDL_Window *a),(a),return)
 SDL3_SYM(SDL_Window*,CreateWindow,(const char *a, int b, int c, Uint32 d),(a,b,c,d),return)
+SDL3_SYM(SDL_Window*,CreatePopupWindow,(SDL_Window *a, int b, int c, int d, int e, Uint32 f),(a,b,c,d,e,f),return)
 SDL3_SYM_PASSTHROUGH(SDL_Window*,CreateWindowFrom,(const void *a),(a),return)
 SDL3_SYM_PASSTHROUGH(Uint32,GetWindowID,(SDL_Window *a),(a),return)
 SDL3_SYM_PASSTHROUGH(SDL_Window*,GetWindowFromID,(Uint32 a),(a),return)