Selaa lähdekoodia

Add progress bar support for Windows (#12530)

Rémy Tassoux 3 viikkoa sitten
vanhempi
commit
7a10fcdccc

+ 1 - 0
CMakeLists.txt

@@ -1979,6 +1979,7 @@ elseif(WINDOWS)
   check_include_file(audioclient.h HAVE_AUDIOCLIENT_H)
   check_include_file(sensorsapi.h HAVE_SENSORSAPI_H)
   check_include_file(shellscalingapi.h HAVE_SHELLSCALINGAPI_H)
+  check_include_file(shobjidl_core.h HAVE_SHOBJIDL_CORE_H)
   check_c_source_compiles("
     #include <windows.h>
     #include <mfapi.h>

+ 1 - 0
cmake/PreseedMSVCCache.cmake

@@ -15,6 +15,7 @@ if(MSVC)
     set(HAVE_MMDEVICEAPI_H                               "1"   CACHE INTERNAL "Have include mmdeviceapi.h")
     set(HAVE_SENSORSAPI_H                                "1"   CACHE INTERNAL "Have include sensorsapi.h")
     set(HAVE_SHELLSCALINGAPI_H                           "1"   CACHE INTERNAL "Have include shellscalingapi.h")
+    set(HAVE_SHOBJIDL_CORE_H                             "1"   CACHE INTERNAL "Have include shobjidl_core.h")
     set(HAVE_TPCSHRD_H                                   "1"   CACHE INTERNAL "Have include tpcshrd.h")
     set(HAVE_WIN32_CC                                    "1"   CACHE INTERNAL "Test HAVE_WIN32_CC")
     set(HAVE_XINPUT_H                                    "1"   CACHE INTERNAL "Test HAVE_XINPUT_H")

+ 2 - 0
include/SDL3/SDL_test_common.h

@@ -87,6 +87,8 @@ typedef struct
     const char *window_icon;
     SDL_WindowFlags window_flags;
     bool flash_on_focus_loss;
+    SDL_ProgressState progress_state;
+    float progress_value;
     int window_x;
     int window_y;
     int window_w;

+ 42 - 0
include/SDL3/SDL_video.h

@@ -307,6 +307,20 @@ typedef enum SDL_FlashOperation
     SDL_FLASH_UNTIL_FOCUSED             /**< Flash the window until it gets focus */
 } SDL_FlashOperation;
 
+/**
+ * Window progress state
+ *
+ * \since This enum is available since SDL 3.2.8.
+ */
+typedef enum SDL_ProgressState
+{
+    SDL_PROGRESS_STATE_NONE,            /**< No progress bar is shown */
+    SDL_PROGRESS_STATE_INDETERMINATE,   /**< The progress bar is shown in a indeterminate state */
+    SDL_PROGRESS_STATE_NORMAL,          /**< The progress bar is shown in a normal state */
+    SDL_PROGRESS_STATE_PAUSED,          /**< The progress bar is shown in a paused state */
+    SDL_PROGRESS_STATE_ERROR            /**< The progress bar is shown in an error state */
+} SDL_ProgressState;
+
 /**
  * An opaque handle to an OpenGL context.
  *
@@ -2806,6 +2820,34 @@ extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowShape(SDL_Window *window, SDL_Surf
  */
 extern SDL_DECLSPEC bool SDLCALL SDL_FlashWindow(SDL_Window *window, SDL_FlashOperation operation);
 
+/**
+ * Sets the state of the progress bar for the given window’s taskbar icon.
+ *
+ * \param window the window whose progress state is to be modified.
+ * \param state the progress state.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \threadsafety This function should only be called on the main thread.
+ *
+ * \since This function is available since SDL 3.2.8.
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowProgressState(SDL_Window *window, SDL_ProgressState state);
+
+/**
+ * Sets the value of the progress bar for the given window’s taskbar icon.
+ *
+ * \param window the window whose progress value is to be modified.
+ * \param value the progress value (0.0f - start, 1.0f - end).
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \threadsafety This function should only be called on the main thread.
+ *
+ * \since This function is available since SDL 3.2.8.
+ */
+extern SDL_DECLSPEC bool SDLCALL SDL_SetWindowProgressValue(SDL_Window *window, float value);
+
 /**
  * Destroy a window.
  *

+ 1 - 0
include/build_config/SDL_build_config.h.cmake

@@ -225,6 +225,7 @@
 #cmakedefine HAVE_TPCSHRD_H 1
 #cmakedefine HAVE_ROAPI_H 1
 #cmakedefine HAVE_SHELLSCALINGAPI_H 1
+#cmakedefine HAVE_SHOBJIDL_CORE_H 1
 
 #cmakedefine USE_POSIX_SPAWN 1
 

+ 3 - 0
include/build_config/SDL_build_config_windows.h

@@ -95,6 +95,9 @@ typedef unsigned int uintptr_t;
 #if defined(_WIN32_MAXVER) && _WIN32_MAXVER >= 0x0603  /* Windows 8.1 SDK */
 #define HAVE_SHELLSCALINGAPI_H 1
 #endif
+#if defined(_WIN32_MAXVER) && _WIN32_MAXVER >= 0x0601  /* Windows 7 SDK */
+#define HAVE_SHOBJIDL_CORE_H 1
+#endif
 #define HAVE_MMDEVICEAPI_H 1
 #define HAVE_AUDIOCLIENT_H 1
 #define HAVE_TPCSHRD_H 1

+ 1 - 0
include/build_config/SDL_build_config_wingdk.h

@@ -43,6 +43,7 @@
 #define HAVE_D3D11_H 1
 #define HAVE_ROAPI_H 1
 #define HAVE_SHELLSCALINGAPI_H 1
+#define HAVE_SHOBJIDL_CORE_H 1
 #define HAVE_MMDEVICEAPI_H 1
 #define HAVE_AUDIOCLIENT_H 1
 #define HAVE_TPCSHRD_H 1

+ 1 - 0
include/build_config/SDL_build_config_xbox.h

@@ -41,6 +41,7 @@
 /*#define HAVE_WINDOWS_GAMING_INPUT_H 1*/
 /*#define HAVE_ROAPI_H 1*/
 /*#define HAVE_SHELLSCALINGAPI_H 1*/
+/*#define HAVE_SHOBJIDL_CORE_H 1*/
 #define HAVE_MMDEVICEAPI_H 1
 #define HAVE_AUDIOCLIENT_H 1
 /*#define HAVE_TPCSHRD_H  1*/

+ 2 - 0
src/dynapi/SDL_dynapi.sym

@@ -1242,6 +1242,8 @@ SDL3_0.0.0 {
     SDL_SetGPURenderStateFragmentUniforms;
     SDL_SetRenderGPUState;
     SDL_DestroyGPURenderState;
+    SDL_SetWindowProgressState;
+    SDL_SetWindowProgressValue;
     # extra symbols go here (don't modify this line)
   local: *;
 };

+ 2 - 0
src/dynapi/SDL_dynapi_overrides.h

@@ -1267,3 +1267,5 @@
 #define SDL_SetGPURenderStateFragmentUniforms SDL_SetGPURenderStateFragmentUniforms_REAL
 #define SDL_SetRenderGPUState SDL_SetRenderGPUState_REAL
 #define SDL_DestroyGPURenderState SDL_DestroyGPURenderState_REAL
+#define SDL_SetWindowProgressState SDL_SetWindowProgressState_REAL
+#define SDL_SetWindowProgressValue SDL_SetWindowProgressValue_REAL

+ 2 - 0
src/dynapi/SDL_dynapi_procs.h

@@ -1275,3 +1275,5 @@ SDL_DYNAPI_PROC(SDL_GPURenderState*,SDL_CreateGPURenderState,(SDL_Renderer *a,SD
 SDL_DYNAPI_PROC(bool,SDL_SetGPURenderStateFragmentUniforms,(SDL_GPURenderState *a,Uint32 b,const void *c,Uint32 d),(a,b,c,d),return)
 SDL_DYNAPI_PROC(bool,SDL_SetRenderGPUState,(SDL_Renderer *a,SDL_GPURenderState *b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_DestroyGPURenderState,(SDL_GPURenderState *a),(a),)
+SDL_DYNAPI_PROC(bool,SDL_SetWindowProgressState,(SDL_Window *a,SDL_ProgressState b),(a,b),return)
+SDL_DYNAPI_PROC(bool,SDL_SetWindowProgressValue,(SDL_Window *a,float b),(a,b),return)

+ 25 - 0
src/test/SDL_test_common.c

@@ -2455,6 +2455,31 @@ SDL_AppResult SDLTest_CommonEventMainCallbacks(SDLTest_CommonState *state, const
                 }
             }
             break;
+        case SDLK_P:
+            if (withAlt) {
+                /* Ctrl-P Cycle through progress states */
+                SDL_Window *window = SDL_GetWindowFromEvent(event);
+                if (window) {
+                    state->progress_state += 1;
+                    if (state->progress_state > SDL_PROGRESS_STATE_ERROR) {
+                        state->progress_state = SDL_PROGRESS_STATE_NONE;
+                    }
+                    SDL_SetWindowProgressState(window, state->progress_state);
+                }
+            }
+            else if (withControl)
+            {
+                /* Alt-P Increase progress value */
+                SDL_Window *window = SDL_GetWindowFromEvent(event);
+                if (window) {
+                    state->progress_value += 0.1f;
+                    if (state->progress_value > 1.f) {
+                        state->progress_value = 0.f;
+                    }
+                    SDL_SetWindowProgressValue(window, state->progress_value);
+                }
+            }
+            break;
         case SDLK_G:
             if (withControl) {
                 /* Ctrl-G toggle mouse grab */

+ 2 - 0
src/video/SDL_sysvideo.h

@@ -303,6 +303,8 @@ struct SDL_VideoDevice
     void (*OnWindowEnter)(SDL_VideoDevice *_this, SDL_Window *window);
     bool (*UpdateWindowShape)(SDL_VideoDevice *_this, SDL_Window *window, SDL_Surface *shape);
     bool (*FlashWindow)(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
+    bool (*SetWindowProgressState)(SDL_VideoDevice *_this, SDL_Window *window, SDL_ProgressState state);
+    bool (*SetWindowProgressValue)(SDL_VideoDevice *_this, SDL_Window *window, float value);
     bool (*SetWindowFocusable)(SDL_VideoDevice *_this, SDL_Window *window, bool focusable);
     bool (*SyncWindow)(SDL_VideoDevice *_this, SDL_Window *window);
 

+ 24 - 0
src/video/SDL_video.c

@@ -3921,6 +3921,30 @@ bool SDL_FlashWindow(SDL_Window *window, SDL_FlashOperation operation)
     return SDL_Unsupported();
 }
 
+bool SDL_SetWindowProgressState(SDL_Window *window, SDL_ProgressState state)
+{
+    CHECK_WINDOW_MAGIC(window, false);
+    CHECK_WINDOW_NOT_POPUP(window, false);
+
+    if (_this->SetWindowProgressState) {
+        return _this->SetWindowProgressState(_this, window, state);
+    }
+
+    return SDL_Unsupported();
+}
+
+bool SDL_SetWindowProgressValue(SDL_Window *window, float value)
+{
+    CHECK_WINDOW_MAGIC(window, false);
+    CHECK_WINDOW_NOT_POPUP(window, false);
+
+    if (_this->SetWindowProgressValue) {
+        return _this->SetWindowProgressValue(_this, window, value);
+    }
+
+    return SDL_Unsupported();
+}
+
 void SDL_OnWindowShown(SDL_Window *window)
 {
     // Set window state if we have pending window flags cached

+ 10 - 0
src/video/windows/SDL_windowsevents.c

@@ -51,6 +51,10 @@
 #include "wmmsg.h"
 #endif
 
+#ifdef HAVE_SHOBJIDL_CORE_H
+#include <shobjidl_core.h>
+#endif
+
 #ifdef SDL_PLATFORM_GDK
 #include "../../core/gdk/SDL_gdk.h"
 #endif
@@ -2431,6 +2435,12 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
 #endif // !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
     }
 
+#ifdef HAVE_SHOBJIDL_CORE_H
+    if (msg == data->videodata->WM_TASKBAR_BUTTON_CREATED) {
+        data->videodata->taskbar_button_created = true;
+    }
+#endif
+
     // If there's a window proc, assume it's going to handle messages
     if (data->wndproc) {
         return CallWindowProc(data->wndproc, hwnd, msg, wParam, lParam);

+ 17 - 0
src/video/windows/SDL_windowsvideo.c

@@ -36,6 +36,10 @@
 #include "SDL_windowsrawinput.h"
 #include "SDL_windowsvulkan.h"
 
+#ifdef HAVE_SHOBJIDL_CORE_H
+#include <shobjidl_core.h>
+#endif
+
 #ifdef SDL_GDK_TEXTINPUT
 #include "../gdk/SDL_gdktextinput.h"
 #endif
@@ -268,6 +272,8 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
     device->SetWindowHitTest = WIN_SetWindowHitTest;
     device->AcceptDragAndDrop = WIN_AcceptDragAndDrop;
     device->FlashWindow = WIN_FlashWindow;
+    device->SetWindowProgressState = WIN_SetWindowProgressState;
+    device->SetWindowProgressValue = WIN_SetWindowProgressValue;
     device->ShowWindowSystemMenu = WIN_ShowWindowSystemMenu;
     device->SetWindowFocusable = WIN_SetWindowFocusable;
     device->UpdateWindowShape = WIN_UpdateWindowShape;
@@ -552,6 +558,9 @@ static bool WIN_VideoInit(SDL_VideoDevice *_this)
 #if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
     data->_SDL_WAKEUP = RegisterWindowMessageA("_SDL_WAKEUP");
 #endif
+#if defined(HAVE_SHOBJIDL_CORE_H)
+    data->WM_TASKBAR_BUTTON_CREATED = RegisterWindowMessageA("TaskbarButtonCreated");
+#endif
 
     return true;
 }
@@ -581,6 +590,14 @@ void WIN_VideoQuit(SDL_VideoDevice *_this)
     }
 #endif // !(defined(SDL_PLATFORM_XBOXONE) || defined(SDL_PLATFORM_XBOXSERIES))
 
+#if defined(HAVE_SHOBJIDL_CORE_H)
+    data->taskbar_button_created = false;
+    if (data->taskbar_list) {
+        IUnknown_Release(data->taskbar_list);
+        data->taskbar_list = NULL;
+    }
+#endif
+
     if (data->coinitialized) {
         WIN_CoUninitialize();
         data->coinitialized = false;

+ 8 - 0
src/video/windows/SDL_windowsvideo.h

@@ -308,6 +308,8 @@ typedef enum PROCESS_DPI_AWARENESS
 #include <shellscalingapi.h>
 #endif
 
+typedef struct ITaskbarList3 ITaskbarList3;
+
 #ifndef _DPI_AWARENESS_CONTEXTS_
 
 typedef enum DPI_AWARENESS
@@ -536,6 +538,12 @@ struct SDL_VideoData
 
     BYTE pre_hook_key_state[256];
     UINT _SDL_WAKEUP;
+
+#ifdef HAVE_SHOBJIDL_CORE_H
+    UINT WM_TASKBAR_BUTTON_CREATED;
+    bool taskbar_button_created;
+    ITaskbarList3 *taskbar_list;
+#endif
 };
 
 extern bool g_WindowsEnableMessageLoop;

+ 93 - 2
src/video/windows/SDL_windowswindow.c

@@ -38,6 +38,10 @@
 // Dropfile support
 #include <shellapi.h>
 
+#ifdef HAVE_SHOBJIDL_CORE_H
+#include <shobjidl_core.h>
+#endif
+
 // Dark mode support
 typedef enum {
     UXTHEME_APPMODE_DEFAULT,
@@ -180,6 +184,33 @@ static DWORD GetWindowStyleEx(SDL_Window *window)
     return style;
 }
 
+#ifdef HAVE_SHOBJIDL_CORE_H
+static ITaskbarList3 *GetTaskbarList(SDL_Window* window)
+{
+    const SDL_WindowData *data = window->internal;
+    if (!data->videodata->taskbar_button_created) {
+        WIN_SetError("Missing taskbar button");
+        return NULL;
+    }
+    if (!data->videodata->taskbar_list) {
+        HRESULT ret = CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_ALL, &IID_ITaskbarList3, (LPVOID *)&data->videodata->taskbar_list);
+        if (FAILED(ret)) {
+            WIN_SetError("Unable to create taskbar list");
+            return NULL;
+        }
+        ITaskbarList3 *taskbarlist = data->videodata->taskbar_list;
+        ret = taskbarlist->lpVtbl->HrInit(taskbarlist);
+        if (FAILED(ret)) {
+            taskbarlist->lpVtbl->Release(taskbarlist);
+            data->videodata->taskbar_list = NULL;
+            WIN_SetError("Unable to initialize taskbar list");
+            return NULL;
+        }
+    }
+    return data->videodata->taskbar_list;
+}
+#endif
+
 /**
  * Returns arguments to pass to SetWindowPos - the window rect, including frame, in Windows coordinates.
  * Can be called before we have a HWND.
@@ -694,7 +725,7 @@ static void WIN_SetKeyboardFocus(SDL_Window *window, bool set_active_focus)
     toplevel->internal->keyboard_focus = window;
 
     if (set_active_focus && !window->is_hiding && !window->is_destroying) {
-    	SDL_SetKeyboardFocus(window);
+        SDL_SetKeyboardFocus(window);
     }
 }
 
@@ -1051,7 +1082,7 @@ void WIN_ShowWindow(SDL_VideoDevice *_this, SDL_Window *window)
     }
 
     if (window->flags & SDL_WINDOW_POPUP_MENU && bActivate) {
-	    WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
+        WIN_SetKeyboardFocus(window, window->parent == SDL_GetKeyboardFocus());
     }
     if (window->flags & SDL_WINDOW_MODAL) {
         WIN_SetWindowModal(_this, window, true);
@@ -2217,6 +2248,66 @@ bool WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperat
     return true;
 }
 
+bool WIN_SetWindowProgressState(SDL_VideoDevice *_this, SDL_Window *window, SDL_ProgressState state)
+{
+#ifndef HAVE_SHOBJIDL_CORE_H
+    return false;
+#else
+    ITaskbarList3 *taskbar_list = GetTaskbarList(window);
+    if (!taskbar_list) {
+        return false;
+    };
+
+    TBPFLAG tbpFlags;
+    switch (state) {
+    case SDL_PROGRESS_STATE_NONE:
+        tbpFlags = TBPF_NOPROGRESS;
+        break;
+    case SDL_PROGRESS_STATE_INDETERMINATE:
+        tbpFlags = TBPF_INDETERMINATE;
+        break;
+    case SDL_PROGRESS_STATE_NORMAL:
+        tbpFlags = TBPF_NORMAL;
+        break;
+    case SDL_PROGRESS_STATE_PAUSED:
+        tbpFlags = TBPF_PAUSED;
+        break;
+    case SDL_PROGRESS_STATE_ERROR:
+        tbpFlags = TBPF_ERROR;
+        break;
+    default:
+        return SDL_Unsupported();
+    }
+
+    HRESULT ret = taskbar_list->lpVtbl->SetProgressState(taskbar_list, window->internal->hwnd, tbpFlags);
+    if (FAILED(ret)) {
+        return WIN_SetErrorFromHRESULT("ITaskbarList3::SetProgressState()", ret);
+    }
+
+    return true;
+#endif // HAVE_SHOBJIDL_CORE_H
+}
+
+bool WIN_SetWindowProgressValue(SDL_VideoDevice *_this, SDL_Window *window, float value)
+{
+#ifndef HAVE_SHOBJIDL_CORE_H
+    return false;
+#else
+    ITaskbarList3 *taskbar_list = GetTaskbarList(window);
+    if (!taskbar_list) {
+        return false;
+    };
+
+    SDL_clamp(value, 0.0f, 1.f);
+    HRESULT ret = taskbar_list->lpVtbl->SetProgressValue(taskbar_list, window->internal->hwnd, (ULONGLONG)(value * 10000.f), 10000);
+    if (FAILED(ret)) {
+        return WIN_SetErrorFromHRESULT("ITaskbarList3::SetProgressValue()", ret);
+    }
+
+    return true;
+#endif  // HAVE_SHOBJIDL_CORE_H
+}
+
 void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y)
 {
     const SDL_WindowData *data = window->internal;

+ 2 - 0
src/video/windows/SDL_windowswindow.h

@@ -133,6 +133,8 @@ extern void WIN_UnclipCursorForWindow(SDL_Window *window);
 extern bool WIN_SetWindowHitTest(SDL_Window *window, bool enabled);
 extern void WIN_AcceptDragAndDrop(SDL_Window *window, bool accept);
 extern bool WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);
+extern bool WIN_SetWindowProgressState(SDL_VideoDevice *_this, SDL_Window *window, SDL_ProgressState state);
+extern bool WIN_SetWindowProgressValue(SDL_VideoDevice *_this, SDL_Window *window, float value);
 extern void WIN_UpdateDarkModeForHWND(HWND hwnd);
 extern bool WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRect rect_type);
 extern void WIN_ShowWindowSystemMenu(SDL_Window *window, int x, int y);