/*
  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;
}
