#include "testutils.h" #include #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) * - 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_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; } window = SDL_CreateWindow("testtray", 640, 480, 0); if (!window) { SDL_Log("Couldn't create window: %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); } } clean_all: if (!trays_destroyed) { SDL_DestroyTray(tray2); } clean_tray1: if (!trays_destroyed) { SDL_DestroyTray(tray); } SDL_free(trays); clean_window: if (window) { SDL_DestroyWindow(window); } quit: SDL_Quit(); SDLTest_CommonDestroyState(state); return 0; }