SDL: Implement bare minimum for SDL_FlashWindow

From 64724db0a1ffac7c3332df66a942d3ba27b2dc0b Mon Sep 17 00:00:00 2001
From: Jupeyy <[EMAIL REDACTED]>
Date: Fri, 4 Jun 2021 19:55:30 +0200
Subject: [PATCH] Implement bare minimum for SDL_FlashWindow

---
 include/SDL_video.h                   | 13 +++++++++++++
 src/dynapi/SDL_dynapi_overrides.h     |  1 +
 src/dynapi/SDL_dynapi_procs.h         |  1 +
 src/video/SDL_sysvideo.h              |  1 +
 src/video/SDL_video.c                 | 12 ++++++++++++
 src/video/windows/SDL_windowsvideo.c  |  1 +
 src/video/windows/SDL_windowswindow.c | 18 ++++++++++++++++++
 src/video/windows/SDL_windowswindow.h |  1 +
 src/video/x11/SDL_x11video.c          |  1 +
 src/video/x11/SDL_x11window.c         | 24 ++++++++++++++++++++++++
 src/video/x11/SDL_x11window.h         |  1 +
 11 files changed, 74 insertions(+)

diff --git a/include/SDL_video.h b/include/SDL_video.h
index 0c05a0937b..896f303219 100644
--- a/include/SDL_video.h
+++ b/include/SDL_video.h
@@ -65,6 +65,7 @@ typedef struct
  *  \sa SDL_CreateWindow()
  *  \sa SDL_CreateWindowFrom()
  *  \sa SDL_DestroyWindow()
+ *  \sa SDL_FlashWindow()
  *  \sa SDL_GetWindowData()
  *  \sa SDL_GetWindowFlags()
  *  \sa SDL_GetWindowGrab()
@@ -1509,6 +1510,18 @@ extern DECLSPEC int SDLCALL SDL_SetWindowHitTest(SDL_Window * window,
                                                  SDL_HitTest callback,
                                                  void *callback_data);
 
+/**
+ * Request a window to give a signal, e.g. a visual signal, to demand attention from the user.
+ *
+ * \returns 0 on success or a negative error code on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \param window the window to request the flashing for
+ * \param flash_count number of times the window gets flashed on systems that support flashing the
+ *                    window multiple times, like Windows, else it is ignored
+ */
+extern DECLSPEC int SDLCALL SDL_FlashWindow(SDL_Window * window, Uint32 flash_count);
+
 /**
  * Destroy a window.
  *
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index 157730806a..99662c0364 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -811,3 +811,4 @@
 #define SDL_GetAudioDeviceSpec SDL_GetAudioDeviceSpec_REAL
 #define SDL_TLSCleanup SDL_TLSCleanup_REAL
 #define SDL_SetWindowAlwaysOnTop SDL_SetWindowAlwaysOnTop_REAL
+#define SDL_FlashWindow SDL_FlashWindow_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index a3113d1da7..17e7eb317f 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -876,3 +876,4 @@ SDL_DYNAPI_PROC(int,SDL_AndroidShowToast,(const char *a, int b, int c, int d, in
 SDL_DYNAPI_PROC(int,SDL_GetAudioDeviceSpec,(int a, int b, SDL_AudioSpec *c),(a,b,c),return)
 SDL_DYNAPI_PROC(void,SDL_TLSCleanup,(void),(),)
 SDL_DYNAPI_PROC(void,SDL_SetWindowAlwaysOnTop,(SDL_Window *a, SDL_bool b),(a,b),)
+SDL_DYNAPI_PROC(int,SDL_FlashWindow,(SDL_Window *a, Uint32 b),(a, b),return)
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 8395e86804..4491a4e59e 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -240,6 +240,7 @@ struct SDL_VideoDevice
     int (*UpdateWindowFramebuffer) (_THIS, SDL_Window * window, const SDL_Rect * rects, int numrects);
     void (*DestroyWindowFramebuffer) (_THIS, SDL_Window * window);
     void (*OnWindowEnter) (_THIS, SDL_Window * window);
+    int (*FlashWindow) (_THIS, SDL_Window * window, Uint32 flash_count);
 
     /* * * */
     /*
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index a5cbe69629..d029f46731 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -2792,6 +2792,18 @@ SDL_GetGrabbedWindow(void)
     return _this->grabbed_window;
 }
 
+int
+SDL_FlashWindow(SDL_Window * window, Uint32 flash_count)
+{
+    CHECK_WINDOW_MAGIC(window, -1);
+
+    if (_this->FlashWindow) {
+        return _this->FlashWindow(_this, window, flash_count);
+    }
+
+    return SDL_Unsupported();
+}
+
 void
 SDL_OnWindowShown(SDL_Window * window)
 {
diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c
index 4956ced701..a80c20c7af 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/src/video/windows/SDL_windowsvideo.c
@@ -176,6 +176,7 @@ WIN_CreateDevice(int devindex)
     device->OnWindowEnter = WIN_OnWindowEnter;
     device->SetWindowHitTest = WIN_SetWindowHitTest;
     device->AcceptDragAndDrop = WIN_AcceptDragAndDrop;
+    device->FlashWindow = WIN_FlashWindow;
 
     device->shape_driver.CreateShaper = Win32_CreateShaper;
     device->shape_driver.SetWindowShape = Win32_SetWindowShape;
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index 51d0c11112..16401a57b8 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -1084,6 +1084,24 @@ WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
     DragAcceptFiles(data->hwnd, accept ? TRUE : FALSE);
 }
 
+int
+WIN_FlashWindow(_THIS, SDL_Window * window, Uint32 flash_count)
+{
+    HWND hwnd;
+    FLASHWINFO desc;
+
+    hwnd = ((SDL_WindowData *) window->driverdata)->hwnd;
+    desc.cbSize = sizeof(desc);
+    desc.hwnd = hwnd;
+    desc.dwFlags = FLASHW_TRAY;
+    desc.uCount = flash_count; /* flash x times */
+    desc.dwTimeout = 0;
+
+    FlashWindowEx(&desc);
+
+    return 0;
+}
+
 #endif /* SDL_VIDEO_DRIVER_WINDOWS */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h
index fe7f825ffe..1e8f3d3181 100644
--- a/src/video/windows/SDL_windowswindow.h
+++ b/src/video/windows/SDL_windowswindow.h
@@ -86,6 +86,7 @@ extern void WIN_OnWindowEnter(_THIS, SDL_Window * window);
 extern void WIN_UpdateClipCursor(SDL_Window *window);
 extern int WIN_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
 extern void WIN_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
+extern int WIN_FlashWindow(_THIS, SDL_Window * window, Uint32 flash_count);
 
 #endif /* SDL_windowswindow_h_ */
 
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 2dc7013da9..e8b9693112 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -254,6 +254,7 @@ X11_CreateDevice(int devindex)
     device->GetWindowWMInfo = X11_GetWindowWMInfo;
     device->SetWindowHitTest = X11_SetWindowHitTest;
     device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
+    device->FlashWindow = X11_FlashWindow;
 
     device->shape_driver.CreateShaper = X11_CreateShaper;
     device->shape_driver.SetWindowShape = X11_SetWindowShape;
diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c
index 8ed2a44370..a6a0b55b10 100644
--- a/src/video/x11/SDL_x11window.c
+++ b/src/video/x11/SDL_x11window.c
@@ -1747,6 +1747,30 @@ X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept)
     }
 }
 
+int
+X11_FlashWindow(_THIS, SDL_Window * window, Uint32 flash_count)
+{
+    SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+    SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(window)->driverdata;
+    Display *display = data->videodata->display;
+
+    Atom demands_attention = X11_XInternAtom(display, "_NET_WM_STATE_DEMANDS_ATTENTION", 1);
+    Atom wm_state = X11_XInternAtom(display, "_NET_WM_STATE", 1);
+
+    XEvent snd_ntfy_ev = {ClientMessage};
+    snd_ntfy_ev.xclient.window = data->xwindow;
+    snd_ntfy_ev.xclient.message_type = wm_state;
+    snd_ntfy_ev.xclient.format = 32;
+    snd_ntfy_ev.xclient.data.l[0] = 1; /* _NET_WM_STATE_ADD */
+    snd_ntfy_ev.xclient.data.l[1] = demands_attention;
+    snd_ntfy_ev.xclient.data.l[2] = 0;
+    snd_ntfy_ev.xclient.data.l[3] = 1; /* normal application */
+    snd_ntfy_ev.xclient.data.l[4] = 0;
+    X11_XSendEvent(display, RootWindow(display, displaydata->screen), False, SubstructureNotifyMask | SubstructureRedirectMask, &snd_ntfy_ev);
+
+    return 0;
+}
+
 #endif /* SDL_VIDEO_DRIVER_X11 */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h
index 98c1500889..4e9764c43e 100644
--- a/src/video/x11/SDL_x11window.h
+++ b/src/video/x11/SDL_x11window.h
@@ -107,6 +107,7 @@ extern SDL_bool X11_GetWindowWMInfo(_THIS, SDL_Window * window,
                                     struct SDL_SysWMinfo *info);
 extern int X11_SetWindowHitTest(SDL_Window *window, SDL_bool enabled);
 extern void X11_AcceptDragAndDrop(SDL_Window * window, SDL_bool accept);
+extern int X11_FlashWindow(_THIS, SDL_Window * window, Uint32 flash_count);
 
 #endif /* SDL_x11window_h_ */