| /* |
| Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely. |
| */ |
| |
| #include "testutils.h" |
| #include <SDL3/SDL.h> |
| #include <SDL3/SDL_main.h> |
| #include <SDL3/SDL_test.h> |
| |
| /* |
| * 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) |
| * - Middle click: Logs a message (menu never shows for middle click) |
| * |
| * 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; |
| e.type = SDL_EVENT_QUIT; |
| 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 SDLCALL tray2_middleclick(void *userdata, SDL_Tray *tray) |
| { |
| SDL_Log("Middle click on example tray - menu doesn't show for middle click"); |
| return false; |
| } |
| |
| static bool trays_destroyed = false; |
| static SDL_Window *window = NULL; |
| static SDL_Renderer *renderer = NULL; |
| static SDL_TrayEntry *entry_toggle = NULL; |
| |
| static void SDLCALL tray_close(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_Tray **trays = (SDL_Tray **)ptr; |
| |
| 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) { |
| return; |
| } |
| |
| SDL_Surface *icon = SDL_LoadPNG(*filelist); |
| |
| if (!icon) { |
| SDL_Log("Couldn't load icon '%s': %s", *filelist, SDL_GetError()); |
| return; |
| } |
| |
| SDL_Tray *tray = (SDL_Tray *)ptr; |
| SDL_SetTrayIcon(tray, icon); |
| |
| SDL_DestroySurface(icon); |
| } |
| |
| static void SDLCALL change_icon(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_DialogFileFilter filters[] = { |
| { "PNG image files", "png" }, |
| { "All files", "*" }, |
| }; |
| |
| SDL_ShowOpenFileDialog(apply_icon, ptr, NULL, filters, 2, NULL, 0); |
| } |
| |
| static void SDLCALL print_entry(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_Log("Clicked on button '%s'", SDL_GetTrayEntryLabel(entry)); |
| } |
| |
| static void SDLCALL set_entry_enabled(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; |
| SDL_SetTrayEntryEnabled(target, true); |
| } |
| |
| static void SDLCALL set_entry_disabled(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; |
| SDL_SetTrayEntryEnabled(target, false); |
| } |
| |
| static void SDLCALL set_entry_checked(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; |
| SDL_SetTrayEntryChecked(target, true); |
| } |
| |
| static void SDLCALL set_entry_unchecked(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; |
| SDL_SetTrayEntryChecked(target, false); |
| } |
| |
| static void SDLCALL remove_entry(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; |
| SDL_RemoveTrayEntry(target); |
| |
| SDL_TrayMenu *ctrl_submenu = SDL_GetTrayEntryParent(entry); |
| SDL_TrayEntry *ctrl_entry = SDL_GetTrayMenuParentEntry(ctrl_submenu); |
| |
| if (!ctrl_entry) { |
| SDL_Log("Attempt to remove a menu that isn't a submenu. This shouldn't happen."); |
| return; |
| } |
| |
| SDL_RemoveTrayEntry(ctrl_entry); |
| } |
| |
| static void SDLCALL append_button_to(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; |
| SDL_TrayMenu *submenu; |
| SDL_TrayEntry *new_ctrl; |
| SDL_TrayEntry *new_ctrl_remove; |
| SDL_TrayEntry *new_ctrl_enabled; |
| SDL_TrayEntry *new_ctrl_disabled; |
| SDL_TrayEntry *new_example; |
| |
| new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New button", SDL_TRAYENTRY_SUBMENU); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| submenu = SDL_CreateTraySubmenu(new_ctrl); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| new_example = SDL_InsertTrayEntryAt(menu, -1, "New button", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_example == NULL) { |
| SDL_Log("Couldn't insert entry in example tray: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_example, print_entry, NULL); |
| |
| /* ---------- */ |
| |
| new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_remove == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_enabled == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_enabled: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_disabled == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_disabled: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); |
| } |
| |
| static void SDLCALL append_checkbox_to(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; |
| SDL_TrayMenu *submenu; |
| SDL_TrayEntry *new_ctrl; |
| SDL_TrayEntry *new_ctrl_remove; |
| SDL_TrayEntry *new_ctrl_enabled; |
| SDL_TrayEntry *new_ctrl_disabled; |
| SDL_TrayEntry *new_ctrl_checked; |
| SDL_TrayEntry *new_ctrl_unchecked; |
| SDL_TrayEntry *new_example; |
| |
| new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New checkbox", SDL_TRAYENTRY_SUBMENU); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| submenu = SDL_CreateTraySubmenu(new_ctrl); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| new_example = SDL_InsertTrayEntryAt(menu, -1, "New checkbox", SDL_TRAYENTRY_CHECKBOX); |
| |
| if (new_example == NULL) { |
| SDL_Log("Couldn't insert entry in example tray: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_example, print_entry, NULL); |
| |
| /* ---------- */ |
| |
| new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_remove == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_enabled == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_enabled: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_disabled == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_disabled: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_checked = SDL_InsertTrayEntryAt(submenu, -1, "Check", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_checked == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_checked: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_checked, set_entry_checked, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_unchecked = SDL_InsertTrayEntryAt(submenu, -1, "Uncheck", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_unchecked == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_unchecked: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_unchecked, set_entry_unchecked, new_example); |
| } |
| |
| static void SDLCALL append_separator_to(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; |
| SDL_TrayMenu *submenu; |
| SDL_TrayEntry *new_ctrl; |
| SDL_TrayEntry *new_ctrl_remove; |
| SDL_TrayEntry *new_example; |
| |
| new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "[Separator]", SDL_TRAYENTRY_SUBMENU); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| submenu = SDL_CreateTraySubmenu(new_ctrl); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| new_example = SDL_InsertTrayEntryAt(menu, -1, NULL, SDL_TRAYENTRY_BUTTON); |
| |
| if (new_example == NULL) { |
| SDL_Log("Couldn't insert separator in example tray: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_remove == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); |
| } |
| |
| static void SDLCALL append_submenu_to(void *ptr, SDL_TrayEntry *entry) |
| { |
| SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; |
| SDL_TrayMenu *submenu; |
| SDL_TrayMenu *entry_submenu; |
| SDL_TrayEntry *new_ctrl; |
| SDL_TrayEntry *new_ctrl_remove; |
| SDL_TrayEntry *new_ctrl_enabled; |
| SDL_TrayEntry *new_ctrl_disabled; |
| SDL_TrayEntry *new_example; |
| |
| new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New submenu", SDL_TRAYENTRY_SUBMENU); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| submenu = SDL_CreateTraySubmenu(new_ctrl); |
| |
| if (!new_ctrl) { |
| SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| new_example = SDL_InsertTrayEntryAt(menu, -1, "New submenu", SDL_TRAYENTRY_SUBMENU); |
| |
| if (new_example == NULL) { |
| SDL_Log("Couldn't insert entry in example tray: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_example, print_entry, NULL); |
| |
| /* ---------- */ |
| |
| entry_submenu = SDL_CreateTraySubmenu(new_example); |
| |
| if (entry_submenu == NULL) { |
| SDL_Log("Couldn't create new entry submenu: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| /* ---------- */ |
| |
| new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_remove == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_enabled == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_enabled: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); |
| |
| /* ---------- */ |
| |
| new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); |
| |
| if (new_ctrl_disabled == NULL) { |
| SDL_Log("Couldn't insert new_ctrl_disabled: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); |
| |
| /* ---------- */ |
| |
| SDL_InsertTrayEntryAt(submenu, -1, NULL, 0); |
| |
| /* ---------- */ |
| |
| SDL_TrayEntry *entry_newbtn = SDL_InsertTrayEntryAt(submenu, -1, "Create button", SDL_TRAYENTRY_BUTTON); |
| |
| if (entry_newbtn == NULL) { |
| SDL_Log("Couldn't insert entry_newbtn: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(entry_newbtn, append_button_to, entry_submenu); |
| |
| /* ---------- */ |
| |
| SDL_TrayEntry *entry_newchk = SDL_InsertTrayEntryAt(submenu, -1, "Create checkbox", SDL_TRAYENTRY_BUTTON); |
| |
| if (entry_newchk == NULL) { |
| SDL_Log("Couldn't insert entry_newchk: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(entry_newchk, append_checkbox_to, entry_submenu); |
| |
| /* ---------- */ |
| |
| SDL_TrayEntry *entry_newsub = SDL_InsertTrayEntryAt(submenu, -1, "Create submenu", SDL_TRAYENTRY_BUTTON); |
| |
| if (entry_newsub == NULL) { |
| SDL_Log("Couldn't insert entry_newsub: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(entry_newsub, append_submenu_to, entry_submenu); |
| |
| /* ---------- */ |
| |
| SDL_TrayEntry *entry_newsep = SDL_InsertTrayEntryAt(submenu, -1, "Create separator", SDL_TRAYENTRY_BUTTON); |
| |
| if (entry_newsep == NULL) { |
| SDL_Log("Couldn't insert entry_newsep: %s", SDL_GetError()); |
| SDL_RemoveTrayEntry(new_ctrl); |
| SDL_RemoveTrayEntry(new_example); |
| return; |
| } |
| |
| SDL_SetTrayEntryCallback(entry_newsep, append_separator_to, entry_submenu); |
| |
| /* ---------- */ |
| |
| SDL_InsertTrayEntryAt(submenu, -1, NULL, 0); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| SDL_Tray **trays = NULL; |
| SDLTest_CommonState *state; |
| int i; |
| |
| /* Initialize test framework */ |
| state = SDLTest_CommonCreateState(argv, 0); |
| if (state == NULL) { |
| return 1; |
| } |
| |
| /* Parse commandline */ |
| for (i = 1; i < argc;) { |
| int consumed; |
| |
| consumed = SDLTest_CommonArg(state, i); |
| |
| if (consumed <= 0) { |
| static const char *options[] = { NULL }; |
| SDLTest_CommonLogUsage(state, argv[0], options); |
| return 1; |
| } |
| |
| i += consumed; |
| } |
| |
| if (!SDL_Init(SDL_INIT_VIDEO)) { |
| SDL_Log("SDL_Init failed (%s)", SDL_GetError()); |
| return 1; |
| } |
| |
| if (!SDL_CreateWindowAndRenderer("testtray", 640, 480, 0, &window, &renderer)) { |
| SDL_Log("Couldn't create window and renderer: %s", SDL_GetError()); |
| goto quit; |
| } |
| |
| char *icon1filename = GetResourceFilename(NULL, "sdl-test_round.png"); |
| SDL_Surface *icon = SDL_LoadPNG(icon1filename); |
| SDL_free(icon1filename); |
| |
| if (!icon) { |
| SDL_Log("Couldn't load icon 1, proceeding without: %s", SDL_GetError()); |
| } |
| |
| char *icon2filename = GetResourceFilename(NULL, "speaker.png"); |
| SDL_Surface *icon2 = SDL_LoadPNG(icon2filename); |
| SDL_free(icon2filename); |
| |
| if (!icon2) { |
| SDL_Log("Couldn't load icon 2, proceeding without: %s", SDL_GetError()); |
| } |
| |
| SDL_Tray *tray = SDL_CreateTray(icon, "SDL Tray control menu"); |
| |
| if (!tray) { |
| SDL_Log("Couldn't create control tray: %s", SDL_GetError()); |
| goto clean_window; |
| } |
| |
| 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_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_MIDDLECLICK_CALLBACK_POINTER, tray2_middleclick); |
| SDL_Tray *tray2 = SDL_CreateTrayWithProperties(tray2_props); |
| SDL_DestroyProperties(tray2_props); |
| |
| if (!tray2) { |
| SDL_Log("Couldn't create example tray: %s", SDL_GetError()); |
| goto clean_tray1; |
| } |
| |
| SDL_DestroySurface(icon); |
| SDL_DestroySurface(icon2); |
| |
| #define CHECK(name) \ |
| if (!name) { \ |
| SDL_Log("Couldn't create " #name ": %s", SDL_GetError()); \ |
| goto clean_all; \ |
| } |
| |
| SDL_TrayMenu *menu = SDL_CreateTrayMenu(tray); |
| CHECK(menu); |
| |
| SDL_TrayMenu *menu2 = SDL_CreateTrayMenu(tray2); |
| CHECK(menu2); |
| |
| SDL_TrayEntry *entry_quit = SDL_InsertTrayEntryAt(menu, -1, "Quit", SDL_TRAYENTRY_BUTTON); |
| CHECK(entry_quit); |
| |
| SDL_TrayEntry *entry_close = SDL_InsertTrayEntryAt(menu, -1, "Destroy trays", SDL_TRAYENTRY_BUTTON); |
| CHECK(entry_close); |
| |
| /* TODO: Track memory! */ |
| trays = SDL_malloc(sizeof(SDL_Tray *) * 2); |
| if (!trays) { |
| goto clean_all; |
| } |
| |
| trays[0] = tray; |
| trays[1] = tray2; |
| |
| SDL_SetTrayEntryCallback(entry_quit, tray_quit, NULL); |
| SDL_SetTrayEntryCallback(entry_close, tray_close, trays); |
| |
| 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); |
| |
| SDL_SetTrayEntryCallback(entry_icon, change_icon, tray2); |
| |
| SDL_InsertTrayEntryAt(menu, -1, NULL, 0); |
| |
| SDL_TrayEntry *entry_newbtn = SDL_InsertTrayEntryAt(menu, -1, "Create button", SDL_TRAYENTRY_BUTTON); |
| CHECK(entry_newbtn); |
| |
| SDL_SetTrayEntryCallback(entry_newbtn, append_button_to, menu2); |
| |
| SDL_TrayEntry *entry_newchk = SDL_InsertTrayEntryAt(menu, -1, "Create checkbox", SDL_TRAYENTRY_BUTTON); |
| CHECK(entry_newchk); |
| |
| SDL_SetTrayEntryCallback(entry_newchk, append_checkbox_to, menu2); |
| |
| SDL_TrayEntry *entry_newsub = SDL_InsertTrayEntryAt(menu, -1, "Create submenu", SDL_TRAYENTRY_BUTTON); |
| CHECK(entry_newsub); |
| |
| SDL_SetTrayEntryCallback(entry_newsub, append_submenu_to, menu2); |
| |
| SDL_TrayEntry *entry_newsep = SDL_InsertTrayEntryAt(menu, -1, "Create separator", SDL_TRAYENTRY_BUTTON); |
| CHECK(entry_newsep); |
| |
| SDL_SetTrayEntryCallback(entry_newsep, append_separator_to, menu2); |
| |
| SDL_InsertTrayEntryAt(menu, -1, NULL, 0); |
| |
| SDL_Event e; |
| while (SDL_WaitEvent(&e)) { |
| if (e.type == SDL_EVENT_QUIT) { |
| break; |
| } else if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { |
| if (trays_destroyed) { |
| break; |
| } |
| toggle_window(NULL, entry_toggle); |
| } else if (e.type == SDL_EVENT_WINDOW_EXPOSED) { |
| SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); |
| SDL_RenderClear(renderer); |
| SDL_RenderPresent(renderer); |
| } |
| } |
| |
| clean_all: |
| if (!trays_destroyed) { |
| SDL_DestroyTray(tray2); |
| } |
| |
| clean_tray1: |
| if (!trays_destroyed) { |
| SDL_DestroyTray(tray); |
| } |
| SDL_free(trays); |
| |
| clean_window: |
| if (renderer) { |
| SDL_DestroyRenderer(renderer); |
| } |
| if (window) { |
| SDL_DestroyWindow(window); |
| } |
| |
| quit: |
| SDL_Quit(); |
| SDLTest_CommonDestroyState(state); |
| |
| return 0; |
| } |