diff --git a/VisualC/SDL.sln b/VisualC/SDL.sln
index 98d8793073..a942d5fca9 100644
--- a/VisualC/SDL.sln
+++ b/VisualC/SDL.sln
@@ -54,6 +54,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsurround", "tests\tests
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testpen", "tests\testpen\testpen.vcxproj", "{C4E04D18-EF76-4B42-B4C2-16A1BACDC1A3}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testtray", "tests\testtray\testtray.vcxproj", "{E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{1498F0CD-F4DA-4847-9CB2-FB18D48061D5}"
ProjectSection(SolutionItems) = preProject
examples\Directory.Build.props = examples\Directory.Build.props
@@ -469,6 +471,18 @@ Global
{C4E04D18-EF76-4B42-B4C2-16A1BACDC1A3}.Release|Win32.Build.0 = Release|Win32
{C4E04D18-EF76-4B42-B4C2-16A1BACDC1A3}.Release|x64.ActiveCfg = Release|x64
{C4E04D18-EF76-4B42-B4C2-16A1BACDC1A3}.Release|x64.Build.0 = Release|x64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Debug|ARM64.ActiveCfg = Debug|ARM64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Debug|ARM64.Build.0 = Debug|ARM64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Debug|Win32.Build.0 = Debug|Win32
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Debug|x64.ActiveCfg = Debug|x64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Debug|x64.Build.0 = Debug|x64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Release|ARM64.ActiveCfg = Release|ARM64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Release|ARM64.Build.0 = Release|ARM64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Release|Win32.ActiveCfg = Release|Win32
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Release|Win32.Build.0 = Release|Win32
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Release|x64.ActiveCfg = Release|x64
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}.Release|x64.Build.0 = Release|x64
{EB448819-74BC-40C9-A61A-4D4ECD55F9D5}.Debug|ARM64.ActiveCfg = Debug|ARM64
{EB448819-74BC-40C9-A61A-4D4ECD55F9D5}.Debug|ARM64.Build.0 = Debug|ARM64
{EB448819-74BC-40C9-A61A-4D4ECD55F9D5}.Debug|Win32.ActiveCfg = Debug|Win32
@@ -930,6 +944,7 @@ Global
{C4E04D18-EF76-4B42-B4C2-16A1BACDC0A4} = {D69D5741-611F-4E14-8541-1FEE94F50B5A}
{70B894A9-E306-49E8-ABC2-932A952A5E5F} = {D69D5741-611F-4E14-8541-1FEE94F50B5A}
{C4E04D18-EF76-4B42-B4C2-16A1BACDC1A3} = {D69D5741-611F-4E14-8541-1FEE94F50B5A}
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1} = {D69D5741-611F-4E14-8541-1FEE94F50B5A}
{1B61A1B7-92DE-4C37-9151-D2928D6449AB} = {1498F0CD-F4DA-4847-9CB2-FB18D48061D5}
{EB448819-74BC-40C9-A61A-4D4ECD55F9D5} = {1B61A1B7-92DE-4C37-9151-D2928D6449AB}
{6B710DFF-8A4A-40A2-BF2D-88D266F3D4F0} = {1B61A1B7-92DE-4C37-9151-D2928D6449AB}
diff --git a/VisualC/tests/testtray/testtray.vcxproj b/VisualC/tests/testtray/testtray.vcxproj
new file mode 100644
index 0000000000..a4f22ad594
--- /dev/null
+++ b/VisualC/tests/testtray/testtray.vcxproj
@@ -0,0 +1,345 @@
+
+
+
+
+ Debug
+ ARM64
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ ARM64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {E2AE0C33-D9C6-4B8E-BB65-6F8AF2F9EBD1}
+ testtray
+ 10.0
+
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+ Application
+ $(DefaultPlatformToolset)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.40219.1
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+ AllRules.ruleset
+
+
+
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Debug/testtray.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ Disabled
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+ OldStyle
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ true
+ Windows
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ X64
+ .\Debug/testtray.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ Disabled
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+ Level3
+ OldStyle
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ true
+ Windows
+
+
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Debug/testtray.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ Disabled
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDebugDLL
+ Level3
+ OldStyle
+
+
+ _DEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ true
+ Windows
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ Win32
+ .\Release/testtray.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ Windows
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ X64
+ .\Release/testtray.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ Windows
+
+
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ true
+ true
+ .\Release/testtray.tlb
+
+
+ %(AdditionalOptions) /utf-8
+ $(SolutionDir)/../include;%(AdditionalIncludeDirectories)
+ %(AdditionalUsingDirectories)
+ WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ Level3
+
+
+ NDEBUG;%(PreprocessorDefinitions)
+ 0x0409
+
+
+ Windows
+
+
+
+
+ $(TreatWarningsAsError)
+
+
+
+
+ {81ce8daf-ebb2-4761-8e45-b71abcca8c68}
+ false
+ false
+ true
+
+
+ {da956fd3-e143-46f2-9fe5-c77bebc56b1a}
+ false
+ false
+ true
+
+
+
+
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ Copying %(Filename)%(Extension)
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ Copying %(Filename)%(Extension)
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+
+
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ Copying %(Filename)%(Extension)
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ Copying %(Filename)%(Extension)
+ Copying %(Filename)%(Extension)
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ copy "%(FullPath)" "$(ProjectDir)\"
+
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+ $(ProjectDir)\%(Filename)%(Extension);%(Outputs)
+
+
+
+
+
+
+
+
+
+
diff --git a/test/testtray.c b/test/testtray.c
index e4c3fd02bf..3a4fabfc4d 100644
--- a/test/testtray.c
+++ b/test/testtray.c
@@ -3,6 +3,35 @@
#include
#include
+/*
+ * testtray - SDL system tray API test application
+ *
+ * This program creates two system tray icons to demonstrate and test the
+ * SDL tray API:
+ *
+ * 1. Control tray (sdl-test_round.png) - Provides a menu to:
+ * - Quit: Exit the application
+ * - Destroy trays: Remove both tray icons and show the window
+ * - Hide/Show window: Toggle the window visibility
+ * - Change icon: Change the example tray's icon via file dialog
+ * - Create button/checkbox/submenu/separator: Add menu items to the
+ * example tray, demonstrating dynamic menu construction
+ *
+ * 2. Example tray (speaker.png) - A target tray that can be manipulated
+ * through the control tray's menu. Menu items created here can be
+ * enabled, disabled, checked, unchecked, or removed via submenus that
+ * appear in the control tray. This tray is created with
+ * SDL_CreateTrayWithProperties to demonstrate click callbacks:
+ * - Left click: Logs a message and shows the menu (returns true)
+ * - Right click: Logs a message and suppresses the menu (returns false)
+ *
+ * Window behavior:
+ * - Closing the window (X button) hides it to the tray rather than exiting
+ * - The "Hide/Show window" menu item toggles visibility and updates its label
+ * - If trays are destroyed while the window is hidden, it is shown first
+ * - If trays are destroyed, closing the window exits the application
+ */
+
static void SDLCALL tray_quit(void *ptr, SDL_TrayEntry *entry)
{
SDL_Event e;
@@ -10,7 +39,21 @@ static void SDLCALL tray_quit(void *ptr, SDL_TrayEntry *entry)
SDL_PushEvent(&e);
}
+static bool SDLCALL tray2_leftclick(void *userdata, SDL_Tray *tray)
+{
+ SDL_Log("Left click on example tray - menu shown");
+ return true;
+}
+
+static bool SDLCALL tray2_rightclick(void *userdata, SDL_Tray *tray)
+{
+ SDL_Log("Right click on example tray - menu suppressed");
+ return false;
+}
+
static bool trays_destroyed = false;
+static SDL_Window *window = NULL;
+static SDL_TrayEntry *entry_toggle = NULL;
static void SDLCALL tray_close(void *ptr, SDL_TrayEntry *entry)
{
@@ -18,10 +61,25 @@ static void SDLCALL tray_close(void *ptr, SDL_TrayEntry *entry)
trays_destroyed = true;
+ if (window) {
+ SDL_ShowWindow(window);
+ }
+
SDL_DestroyTray(trays[0]);
SDL_DestroyTray(trays[1]);
}
+static void SDLCALL toggle_window(void *ptr, SDL_TrayEntry *entry)
+{
+ if (SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN) {
+ SDL_ShowWindow(window);
+ SDL_SetTrayEntryLabel(entry, "Hide window");
+ } else {
+ SDL_HideWindow(window);
+ SDL_SetTrayEntryLabel(entry, "Show window");
+ }
+}
+
static void SDLCALL apply_icon(void *ptr, const char * const *filelist, int filter)
{
if (!*filelist) {
@@ -514,9 +572,9 @@ int main(int argc, char **argv)
return 1;
}
- SDL_Window *w = SDL_CreateWindow("testtray", 640, 480, 0);
+ window = SDL_CreateWindow("testtray", 640, 480, 0);
- if (!w) {
+ if (!window) {
SDL_Log("Couldn't create window: %s", SDL_GetError());
goto quit;
}
@@ -544,7 +602,13 @@ int main(int argc, char **argv)
goto clean_window;
}
- SDL_Tray *tray2 = SDL_CreateTray(icon2, "SDL Tray example");
+ SDL_PropertiesID tray2_props = SDL_CreateProperties();
+ SDL_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_ICON_POINTER, icon2);
+ SDL_SetStringProperty(tray2_props, SDL_PROP_TRAY_CREATE_TOOLTIP_STRING, "SDL Tray example");
+ SDL_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_LEFTCLICK_CALLBACK_POINTER, tray2_leftclick);
+ SDL_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_RIGHTCLICK_CALLBACK_POINTER, tray2_rightclick);
+ SDL_Tray *tray2 = SDL_CreateTrayWithProperties(tray2_props);
+ SDL_DestroyProperties(tray2_props);
if (!tray2) {
SDL_Log("Couldn't create example tray: %s", SDL_GetError());
@@ -569,7 +633,7 @@ int main(int argc, char **argv)
SDL_TrayEntry *entry_quit = SDL_InsertTrayEntryAt(menu, -1, "Quit", SDL_TRAYENTRY_BUTTON);
CHECK(entry_quit);
- SDL_TrayEntry *entry_close = SDL_InsertTrayEntryAt(menu, -1, "Close", SDL_TRAYENTRY_BUTTON);
+ SDL_TrayEntry *entry_close = SDL_InsertTrayEntryAt(menu, -1, "Destroy trays", SDL_TRAYENTRY_BUTTON);
CHECK(entry_close);
/* TODO: Track memory! */
@@ -586,6 +650,13 @@ int main(int argc, char **argv)
SDL_InsertTrayEntryAt(menu, -1, NULL, 0);
+ entry_toggle = SDL_InsertTrayEntryAt(menu, -1, "Hide window", SDL_TRAYENTRY_BUTTON);
+ CHECK(entry_toggle);
+
+ SDL_SetTrayEntryCallback(entry_toggle, toggle_window, NULL);
+
+ SDL_InsertTrayEntryAt(menu, -1, NULL, 0);
+
SDL_TrayEntry *entry_icon = SDL_InsertTrayEntryAt(menu, -1, "Change icon", SDL_TRAYENTRY_BUTTON);
CHECK(entry_icon);
@@ -620,8 +691,10 @@ int main(int argc, char **argv)
if (e.type == SDL_EVENT_QUIT) {
break;
} else if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) {
- SDL_DestroyWindow(w);
- w = NULL;
+ if (trays_destroyed) {
+ break;
+ }
+ toggle_window(NULL, entry_toggle);
}
}
@@ -637,8 +710,8 @@ clean_tray1:
SDL_free(trays);
clean_window:
- if (w) {
- SDL_DestroyWindow(w);
+ if (window) {
+ SDL_DestroyWindow(window);
}
quit: