Atlas - testtray.c
Home / ext / SDL / test Lines: 1 | Size: 22449 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 3 4 This software is provided 'as-is', without any express or implied 5 warranty. In no event will the authors be held liable for any damages 6 arising from the use of this software. 7 8 Permission is granted to anyone to use this software for any purpose, 9 including commercial applications, and to alter it and redistribute it 10 freely. 11*/ 12 13#include "testutils.h" 14#include <SDL3/SDL.h> 15#include <SDL3/SDL_main.h> 16#include <SDL3/SDL_test.h> 17 18/* 19 * testtray - SDL system tray API test application 20 * 21 * This program creates two system tray icons to demonstrate and test the 22 * SDL tray API: 23 * 24 * 1. Control tray (sdl-test_round.png) - Provides a menu to: 25 * - Quit: Exit the application 26 * - Destroy trays: Remove both tray icons and show the window 27 * - Hide/Show window: Toggle the window visibility 28 * - Change icon: Change the example tray's icon via file dialog 29 * - Create button/checkbox/submenu/separator: Add menu items to the 30 * example tray, demonstrating dynamic menu construction 31 * 32 * 2. Example tray (speaker.png) - A target tray that can be manipulated 33 * through the control tray's menu. Menu items created here can be 34 * enabled, disabled, checked, unchecked, or removed via submenus that 35 * appear in the control tray. This tray is created with 36 * SDL_CreateTrayWithProperties to demonstrate click callbacks: 37 * - Left click: Logs a message and shows the menu (returns true) 38 * - Right click: Logs a message and suppresses the menu (returns false) 39 * - Middle click: Logs a message (menu never shows for middle click) 40 * 41 * Window behavior: 42 * - Closing the window (X button) hides it to the tray rather than exiting 43 * - The "Hide/Show window" menu item toggles visibility and updates its label 44 * - If trays are destroyed while the window is hidden, it is shown first 45 * - If trays are destroyed, closing the window exits the application 46 */ 47 48static void SDLCALL tray_quit(void *ptr, SDL_TrayEntry *entry) 49{ 50 SDL_Event e; 51 e.type = SDL_EVENT_QUIT; 52 SDL_PushEvent(&e); 53} 54 55static bool SDLCALL tray2_leftclick(void *userdata, SDL_Tray *tray) 56{ 57 SDL_Log("Left click on example tray - menu shown"); 58 return true; 59} 60 61static bool SDLCALL tray2_rightclick(void *userdata, SDL_Tray *tray) 62{ 63 SDL_Log("Right click on example tray - menu suppressed"); 64 return false; 65} 66 67static bool SDLCALL tray2_middleclick(void *userdata, SDL_Tray *tray) 68{ 69 SDL_Log("Middle click on example tray - menu doesn't show for middle click"); 70 return false; 71} 72 73static bool trays_destroyed = false; 74static SDL_Window *window = NULL; 75static SDL_Renderer *renderer = NULL; 76static SDL_TrayEntry *entry_toggle = NULL; 77 78static void SDLCALL tray_close(void *ptr, SDL_TrayEntry *entry) 79{ 80 SDL_Tray **trays = (SDL_Tray **)ptr; 81 82 trays_destroyed = true; 83 84 if (window) { 85 SDL_ShowWindow(window); 86 } 87 88 SDL_DestroyTray(trays[0]); 89 SDL_DestroyTray(trays[1]); 90} 91 92static void SDLCALL toggle_window(void *ptr, SDL_TrayEntry *entry) 93{ 94 if (SDL_GetWindowFlags(window) & SDL_WINDOW_HIDDEN) { 95 SDL_ShowWindow(window); 96 SDL_SetTrayEntryLabel(entry, "Hide window"); 97 } else { 98 SDL_HideWindow(window); 99 SDL_SetTrayEntryLabel(entry, "Show window"); 100 } 101} 102 103static void SDLCALL apply_icon(void *ptr, const char *const *filelist, int filter) 104{ 105 if (!*filelist) { 106 return; 107 } 108 109 SDL_Surface *icon = SDL_LoadPNG(*filelist); 110 111 if (!icon) { 112 SDL_Log("Couldn't load icon '%s': %s", *filelist, SDL_GetError()); 113 return; 114 } 115 116 SDL_Tray *tray = (SDL_Tray *)ptr; 117 SDL_SetTrayIcon(tray, icon); 118 119 SDL_DestroySurface(icon); 120} 121 122static void SDLCALL change_icon(void *ptr, SDL_TrayEntry *entry) 123{ 124 SDL_DialogFileFilter filters[] = { 125 { "PNG image files", "png" }, 126 { "All files", "*" }, 127 }; 128 129 SDL_ShowOpenFileDialog(apply_icon, ptr, NULL, filters, 2, NULL, 0); 130} 131 132static void SDLCALL print_entry(void *ptr, SDL_TrayEntry *entry) 133{ 134 SDL_Log("Clicked on button '%s'", SDL_GetTrayEntryLabel(entry)); 135} 136 137static void SDLCALL set_entry_enabled(void *ptr, SDL_TrayEntry *entry) 138{ 139 SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; 140 SDL_SetTrayEntryEnabled(target, true); 141} 142 143static void SDLCALL set_entry_disabled(void *ptr, SDL_TrayEntry *entry) 144{ 145 SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; 146 SDL_SetTrayEntryEnabled(target, false); 147} 148 149static void SDLCALL set_entry_checked(void *ptr, SDL_TrayEntry *entry) 150{ 151 SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; 152 SDL_SetTrayEntryChecked(target, true); 153} 154 155static void SDLCALL set_entry_unchecked(void *ptr, SDL_TrayEntry *entry) 156{ 157 SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; 158 SDL_SetTrayEntryChecked(target, false); 159} 160 161static void SDLCALL remove_entry(void *ptr, SDL_TrayEntry *entry) 162{ 163 SDL_TrayEntry *target = (SDL_TrayEntry *)ptr; 164 SDL_RemoveTrayEntry(target); 165 166 SDL_TrayMenu *ctrl_submenu = SDL_GetTrayEntryParent(entry); 167 SDL_TrayEntry *ctrl_entry = SDL_GetTrayMenuParentEntry(ctrl_submenu); 168 169 if (!ctrl_entry) { 170 SDL_Log("Attempt to remove a menu that isn't a submenu. This shouldn't happen."); 171 return; 172 } 173 174 SDL_RemoveTrayEntry(ctrl_entry); 175} 176 177static void SDLCALL append_button_to(void *ptr, SDL_TrayEntry *entry) 178{ 179 SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; 180 SDL_TrayMenu *submenu; 181 SDL_TrayEntry *new_ctrl; 182 SDL_TrayEntry *new_ctrl_remove; 183 SDL_TrayEntry *new_ctrl_enabled; 184 SDL_TrayEntry *new_ctrl_disabled; 185 SDL_TrayEntry *new_example; 186 187 new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New button", SDL_TRAYENTRY_SUBMENU); 188 189 if (!new_ctrl) { 190 SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); 191 return; 192 } 193 194 /* ---------- */ 195 196 submenu = SDL_CreateTraySubmenu(new_ctrl); 197 198 if (!new_ctrl) { 199 SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); 200 SDL_RemoveTrayEntry(new_ctrl); 201 return; 202 } 203 204 /* ---------- */ 205 206 new_example = SDL_InsertTrayEntryAt(menu, -1, "New button", SDL_TRAYENTRY_BUTTON); 207 208 if (new_example == NULL) { 209 SDL_Log("Couldn't insert entry in example tray: %s", SDL_GetError()); 210 SDL_RemoveTrayEntry(new_ctrl); 211 return; 212 } 213 214 SDL_SetTrayEntryCallback(new_example, print_entry, NULL); 215 216 /* ---------- */ 217 218 new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); 219 220 if (new_ctrl_remove == NULL) { 221 SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); 222 SDL_RemoveTrayEntry(new_ctrl); 223 SDL_RemoveTrayEntry(new_example); 224 return; 225 } 226 227 SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); 228 229 /* ---------- */ 230 231 new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); 232 233 if (new_ctrl_enabled == NULL) { 234 SDL_Log("Couldn't insert new_ctrl_enabled: %s", SDL_GetError()); 235 SDL_RemoveTrayEntry(new_ctrl); 236 SDL_RemoveTrayEntry(new_example); 237 return; 238 } 239 240 SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); 241 242 /* ---------- */ 243 244 new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); 245 246 if (new_ctrl_disabled == NULL) { 247 SDL_Log("Couldn't insert new_ctrl_disabled: %s", SDL_GetError()); 248 SDL_RemoveTrayEntry(new_ctrl); 249 SDL_RemoveTrayEntry(new_example); 250 return; 251 } 252 253 SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); 254} 255 256static void SDLCALL append_checkbox_to(void *ptr, SDL_TrayEntry *entry) 257{ 258 SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; 259 SDL_TrayMenu *submenu; 260 SDL_TrayEntry *new_ctrl; 261 SDL_TrayEntry *new_ctrl_remove; 262 SDL_TrayEntry *new_ctrl_enabled; 263 SDL_TrayEntry *new_ctrl_disabled; 264 SDL_TrayEntry *new_ctrl_checked; 265 SDL_TrayEntry *new_ctrl_unchecked; 266 SDL_TrayEntry *new_example; 267 268 new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New checkbox", SDL_TRAYENTRY_SUBMENU); 269 270 if (!new_ctrl) { 271 SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); 272 return; 273 } 274 275 /* ---------- */ 276 277 submenu = SDL_CreateTraySubmenu(new_ctrl); 278 279 if (!new_ctrl) { 280 SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); 281 SDL_RemoveTrayEntry(new_ctrl); 282 return; 283 } 284 285 /* ---------- */ 286 287 new_example = SDL_InsertTrayEntryAt(menu, -1, "New checkbox", SDL_TRAYENTRY_CHECKBOX); 288 289 if (new_example == NULL) { 290 SDL_Log("Couldn't insert entry in example tray: %s", SDL_GetError()); 291 SDL_RemoveTrayEntry(new_ctrl); 292 return; 293 } 294 295 SDL_SetTrayEntryCallback(new_example, print_entry, NULL); 296 297 /* ---------- */ 298 299 new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); 300 301 if (new_ctrl_remove == NULL) { 302 SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); 303 SDL_RemoveTrayEntry(new_ctrl); 304 SDL_RemoveTrayEntry(new_example); 305 return; 306 } 307 308 SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); 309 310 /* ---------- */ 311 312 new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); 313 314 if (new_ctrl_enabled == NULL) { 315 SDL_Log("Couldn't insert new_ctrl_enabled: %s", SDL_GetError()); 316 SDL_RemoveTrayEntry(new_ctrl); 317 SDL_RemoveTrayEntry(new_example); 318 return; 319 } 320 321 SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); 322 323 /* ---------- */ 324 325 new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); 326 327 if (new_ctrl_disabled == NULL) { 328 SDL_Log("Couldn't insert new_ctrl_disabled: %s", SDL_GetError()); 329 SDL_RemoveTrayEntry(new_ctrl); 330 SDL_RemoveTrayEntry(new_example); 331 return; 332 } 333 334 SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); 335 336 /* ---------- */ 337 338 new_ctrl_checked = SDL_InsertTrayEntryAt(submenu, -1, "Check", SDL_TRAYENTRY_BUTTON); 339 340 if (new_ctrl_checked == NULL) { 341 SDL_Log("Couldn't insert new_ctrl_checked: %s", SDL_GetError()); 342 SDL_RemoveTrayEntry(new_ctrl); 343 SDL_RemoveTrayEntry(new_example); 344 return; 345 } 346 347 SDL_SetTrayEntryCallback(new_ctrl_checked, set_entry_checked, new_example); 348 349 /* ---------- */ 350 351 new_ctrl_unchecked = SDL_InsertTrayEntryAt(submenu, -1, "Uncheck", SDL_TRAYENTRY_BUTTON); 352 353 if (new_ctrl_unchecked == NULL) { 354 SDL_Log("Couldn't insert new_ctrl_unchecked: %s", SDL_GetError()); 355 SDL_RemoveTrayEntry(new_ctrl); 356 SDL_RemoveTrayEntry(new_example); 357 return; 358 } 359 360 SDL_SetTrayEntryCallback(new_ctrl_unchecked, set_entry_unchecked, new_example); 361} 362 363static void SDLCALL append_separator_to(void *ptr, SDL_TrayEntry *entry) 364{ 365 SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; 366 SDL_TrayMenu *submenu; 367 SDL_TrayEntry *new_ctrl; 368 SDL_TrayEntry *new_ctrl_remove; 369 SDL_TrayEntry *new_example; 370 371 new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "[Separator]", SDL_TRAYENTRY_SUBMENU); 372 373 if (!new_ctrl) { 374 SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); 375 return; 376 } 377 378 /* ---------- */ 379 380 submenu = SDL_CreateTraySubmenu(new_ctrl); 381 382 if (!new_ctrl) { 383 SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); 384 SDL_RemoveTrayEntry(new_ctrl); 385 return; 386 } 387 388 /* ---------- */ 389 390 new_example = SDL_InsertTrayEntryAt(menu, -1, NULL, SDL_TRAYENTRY_BUTTON); 391 392 if (new_example == NULL) { 393 SDL_Log("Couldn't insert separator in example tray: %s", SDL_GetError()); 394 SDL_RemoveTrayEntry(new_ctrl); 395 return; 396 } 397 398 /* ---------- */ 399 400 new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); 401 402 if (new_ctrl_remove == NULL) { 403 SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); 404 SDL_RemoveTrayEntry(new_ctrl); 405 SDL_RemoveTrayEntry(new_example); 406 return; 407 } 408 409 SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); 410} 411 412static void SDLCALL append_submenu_to(void *ptr, SDL_TrayEntry *entry) 413{ 414 SDL_TrayMenu *menu = (SDL_TrayMenu *)ptr; 415 SDL_TrayMenu *submenu; 416 SDL_TrayMenu *entry_submenu; 417 SDL_TrayEntry *new_ctrl; 418 SDL_TrayEntry *new_ctrl_remove; 419 SDL_TrayEntry *new_ctrl_enabled; 420 SDL_TrayEntry *new_ctrl_disabled; 421 SDL_TrayEntry *new_example; 422 423 new_ctrl = SDL_InsertTrayEntryAt(SDL_GetTrayEntryParent(entry), -1, "New submenu", SDL_TRAYENTRY_SUBMENU); 424 425 if (!new_ctrl) { 426 SDL_Log("Couldn't insert entry in control tray: %s", SDL_GetError()); 427 return; 428 } 429 430 /* ---------- */ 431 432 submenu = SDL_CreateTraySubmenu(new_ctrl); 433 434 if (!new_ctrl) { 435 SDL_Log("Couldn't create control tray entry submenu: %s", SDL_GetError()); 436 SDL_RemoveTrayEntry(new_ctrl); 437 return; 438 } 439 440 /* ---------- */ 441 442 new_example = SDL_InsertTrayEntryAt(menu, -1, "New submenu", SDL_TRAYENTRY_SUBMENU); 443 444 if (new_example == NULL) { 445 SDL_Log("Couldn't insert entry in example tray: %s", SDL_GetError()); 446 SDL_RemoveTrayEntry(new_ctrl); 447 return; 448 } 449 450 SDL_SetTrayEntryCallback(new_example, print_entry, NULL); 451 452 /* ---------- */ 453 454 entry_submenu = SDL_CreateTraySubmenu(new_example); 455 456 if (entry_submenu == NULL) { 457 SDL_Log("Couldn't create new entry submenu: %s", SDL_GetError()); 458 SDL_RemoveTrayEntry(new_ctrl); 459 SDL_RemoveTrayEntry(new_example); 460 return; 461 } 462 463 /* ---------- */ 464 465 new_ctrl_remove = SDL_InsertTrayEntryAt(submenu, -1, "Remove", SDL_TRAYENTRY_BUTTON); 466 467 if (new_ctrl_remove == NULL) { 468 SDL_Log("Couldn't insert new_ctrl_remove: %s", SDL_GetError()); 469 SDL_RemoveTrayEntry(new_ctrl); 470 SDL_RemoveTrayEntry(new_example); 471 return; 472 } 473 474 SDL_SetTrayEntryCallback(new_ctrl_remove, remove_entry, new_example); 475 476 /* ---------- */ 477 478 new_ctrl_enabled = SDL_InsertTrayEntryAt(submenu, -1, "Enable", SDL_TRAYENTRY_BUTTON); 479 480 if (new_ctrl_enabled == NULL) { 481 SDL_Log("Couldn't insert new_ctrl_enabled: %s", SDL_GetError()); 482 SDL_RemoveTrayEntry(new_ctrl); 483 SDL_RemoveTrayEntry(new_example); 484 return; 485 } 486 487 SDL_SetTrayEntryCallback(new_ctrl_enabled, set_entry_enabled, new_example); 488 489 /* ---------- */ 490 491 new_ctrl_disabled = SDL_InsertTrayEntryAt(submenu, -1, "Disable", SDL_TRAYENTRY_BUTTON); 492 493 if (new_ctrl_disabled == NULL) { 494 SDL_Log("Couldn't insert new_ctrl_disabled: %s", SDL_GetError()); 495 SDL_RemoveTrayEntry(new_ctrl); 496 SDL_RemoveTrayEntry(new_example); 497 return; 498 } 499 500 SDL_SetTrayEntryCallback(new_ctrl_disabled, set_entry_disabled, new_example); 501 502 /* ---------- */ 503 504 SDL_InsertTrayEntryAt(submenu, -1, NULL, 0); 505 506 /* ---------- */ 507 508 SDL_TrayEntry *entry_newbtn = SDL_InsertTrayEntryAt(submenu, -1, "Create button", SDL_TRAYENTRY_BUTTON); 509 510 if (entry_newbtn == NULL) { 511 SDL_Log("Couldn't insert entry_newbtn: %s", SDL_GetError()); 512 SDL_RemoveTrayEntry(new_ctrl); 513 SDL_RemoveTrayEntry(new_example); 514 return; 515 } 516 517 SDL_SetTrayEntryCallback(entry_newbtn, append_button_to, entry_submenu); 518 519 /* ---------- */ 520 521 SDL_TrayEntry *entry_newchk = SDL_InsertTrayEntryAt(submenu, -1, "Create checkbox", SDL_TRAYENTRY_BUTTON); 522 523 if (entry_newchk == NULL) { 524 SDL_Log("Couldn't insert entry_newchk: %s", SDL_GetError()); 525 SDL_RemoveTrayEntry(new_ctrl); 526 SDL_RemoveTrayEntry(new_example); 527 return; 528 } 529 530 SDL_SetTrayEntryCallback(entry_newchk, append_checkbox_to, entry_submenu); 531 532 /* ---------- */ 533 534 SDL_TrayEntry *entry_newsub = SDL_InsertTrayEntryAt(submenu, -1, "Create submenu", SDL_TRAYENTRY_BUTTON); 535 536 if (entry_newsub == NULL) { 537 SDL_Log("Couldn't insert entry_newsub: %s", SDL_GetError()); 538 SDL_RemoveTrayEntry(new_ctrl); 539 SDL_RemoveTrayEntry(new_example); 540 return; 541 } 542 543 SDL_SetTrayEntryCallback(entry_newsub, append_submenu_to, entry_submenu); 544 545 /* ---------- */ 546 547 SDL_TrayEntry *entry_newsep = SDL_InsertTrayEntryAt(submenu, -1, "Create separator", SDL_TRAYENTRY_BUTTON); 548 549 if (entry_newsep == NULL) { 550 SDL_Log("Couldn't insert entry_newsep: %s", SDL_GetError()); 551 SDL_RemoveTrayEntry(new_ctrl); 552 SDL_RemoveTrayEntry(new_example); 553 return; 554 } 555 556 SDL_SetTrayEntryCallback(entry_newsep, append_separator_to, entry_submenu); 557 558 /* ---------- */ 559 560 SDL_InsertTrayEntryAt(submenu, -1, NULL, 0); 561} 562 563int main(int argc, char **argv) 564{ 565 SDL_Tray **trays = NULL; 566 SDLTest_CommonState *state; 567 int i; 568 569 /* Initialize test framework */ 570 state = SDLTest_CommonCreateState(argv, 0); 571 if (state == NULL) { 572 return 1; 573 } 574 575 /* Parse commandline */ 576 for (i = 1; i < argc;) { 577 int consumed; 578 579 consumed = SDLTest_CommonArg(state, i); 580 581 if (consumed <= 0) { 582 static const char *options[] = { NULL }; 583 SDLTest_CommonLogUsage(state, argv[0], options); 584 return 1; 585 } 586 587 i += consumed; 588 } 589 590 if (!SDL_Init(SDL_INIT_VIDEO)) { 591 SDL_Log("SDL_Init failed (%s)", SDL_GetError()); 592 return 1; 593 } 594 595 if (!SDL_CreateWindowAndRenderer("testtray", 640, 480, 0, &window, &renderer)) { 596 SDL_Log("Couldn't create window and renderer: %s", SDL_GetError()); 597 goto quit; 598 } 599 600 char *icon1filename = GetResourceFilename(NULL, "sdl-test_round.png"); 601 SDL_Surface *icon = SDL_LoadPNG(icon1filename); 602 SDL_free(icon1filename); 603 604 if (!icon) { 605 SDL_Log("Couldn't load icon 1, proceeding without: %s", SDL_GetError()); 606 } 607 608 char *icon2filename = GetResourceFilename(NULL, "speaker.png"); 609 SDL_Surface *icon2 = SDL_LoadPNG(icon2filename); 610 SDL_free(icon2filename); 611 612 if (!icon2) { 613 SDL_Log("Couldn't load icon 2, proceeding without: %s", SDL_GetError()); 614 } 615 616 SDL_Tray *tray = SDL_CreateTray(icon, "SDL Tray control menu"); 617 618 if (!tray) { 619 SDL_Log("Couldn't create control tray: %s", SDL_GetError()); 620 goto clean_window; 621 } 622 623 SDL_PropertiesID tray2_props = SDL_CreateProperties(); 624 SDL_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_ICON_POINTER, icon2); 625 SDL_SetStringProperty(tray2_props, SDL_PROP_TRAY_CREATE_TOOLTIP_STRING, "SDL Tray example"); 626 SDL_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_LEFTCLICK_CALLBACK_POINTER, tray2_leftclick); 627 SDL_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_RIGHTCLICK_CALLBACK_POINTER, tray2_rightclick); 628 SDL_SetPointerProperty(tray2_props, SDL_PROP_TRAY_CREATE_MIDDLECLICK_CALLBACK_POINTER, tray2_middleclick); 629 SDL_Tray *tray2 = SDL_CreateTrayWithProperties(tray2_props); 630 SDL_DestroyProperties(tray2_props); 631 632 if (!tray2) { 633 SDL_Log("Couldn't create example tray: %s", SDL_GetError()); 634 goto clean_tray1; 635 } 636 637 SDL_DestroySurface(icon); 638 SDL_DestroySurface(icon2); 639 640#define CHECK(name) \ 641 if (!name) { \ 642 SDL_Log("Couldn't create " #name ": %s", SDL_GetError()); \ 643 goto clean_all; \ 644 } 645 646 SDL_TrayMenu *menu = SDL_CreateTrayMenu(tray); 647 CHECK(menu); 648 649 SDL_TrayMenu *menu2 = SDL_CreateTrayMenu(tray2); 650 CHECK(menu2); 651 652 SDL_TrayEntry *entry_quit = SDL_InsertTrayEntryAt(menu, -1, "Quit", SDL_TRAYENTRY_BUTTON); 653 CHECK(entry_quit); 654 655 SDL_TrayEntry *entry_close = SDL_InsertTrayEntryAt(menu, -1, "Destroy trays", SDL_TRAYENTRY_BUTTON); 656 CHECK(entry_close); 657 658 /* TODO: Track memory! */ 659 trays = SDL_malloc(sizeof(SDL_Tray *) * 2); 660 if (!trays) { 661 goto clean_all; 662 } 663 664 trays[0] = tray; 665 trays[1] = tray2; 666 667 SDL_SetTrayEntryCallback(entry_quit, tray_quit, NULL); 668 SDL_SetTrayEntryCallback(entry_close, tray_close, trays); 669 670 SDL_InsertTrayEntryAt(menu, -1, NULL, 0); 671 672 entry_toggle = SDL_InsertTrayEntryAt(menu, -1, "Hide window", SDL_TRAYENTRY_BUTTON); 673 CHECK(entry_toggle); 674 675 SDL_SetTrayEntryCallback(entry_toggle, toggle_window, NULL); 676 677 SDL_InsertTrayEntryAt(menu, -1, NULL, 0); 678 679 SDL_TrayEntry *entry_icon = SDL_InsertTrayEntryAt(menu, -1, "Change icon", SDL_TRAYENTRY_BUTTON); 680 CHECK(entry_icon); 681 682 SDL_SetTrayEntryCallback(entry_icon, change_icon, tray2); 683 684 SDL_InsertTrayEntryAt(menu, -1, NULL, 0); 685 686 SDL_TrayEntry *entry_newbtn = SDL_InsertTrayEntryAt(menu, -1, "Create button", SDL_TRAYENTRY_BUTTON); 687 CHECK(entry_newbtn); 688 689 SDL_SetTrayEntryCallback(entry_newbtn, append_button_to, menu2); 690 691 SDL_TrayEntry *entry_newchk = SDL_InsertTrayEntryAt(menu, -1, "Create checkbox", SDL_TRAYENTRY_BUTTON); 692 CHECK(entry_newchk); 693 694 SDL_SetTrayEntryCallback(entry_newchk, append_checkbox_to, menu2); 695 696 SDL_TrayEntry *entry_newsub = SDL_InsertTrayEntryAt(menu, -1, "Create submenu", SDL_TRAYENTRY_BUTTON); 697 CHECK(entry_newsub); 698 699 SDL_SetTrayEntryCallback(entry_newsub, append_submenu_to, menu2); 700 701 SDL_TrayEntry *entry_newsep = SDL_InsertTrayEntryAt(menu, -1, "Create separator", SDL_TRAYENTRY_BUTTON); 702 CHECK(entry_newsep); 703 704 SDL_SetTrayEntryCallback(entry_newsep, append_separator_to, menu2); 705 706 SDL_InsertTrayEntryAt(menu, -1, NULL, 0); 707 708 SDL_Event e; 709 while (SDL_WaitEvent(&e)) { 710 if (e.type == SDL_EVENT_QUIT) { 711 break; 712 } else if (e.type == SDL_EVENT_WINDOW_CLOSE_REQUESTED) { 713 if (trays_destroyed) { 714 break; 715 } 716 toggle_window(NULL, entry_toggle); 717 } else if (e.type == SDL_EVENT_WINDOW_EXPOSED) { 718 SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); 719 SDL_RenderClear(renderer); 720 SDL_RenderPresent(renderer); 721 } 722 } 723 724clean_all: 725 if (!trays_destroyed) { 726 SDL_DestroyTray(tray2); 727 } 728 729clean_tray1: 730 if (!trays_destroyed) { 731 SDL_DestroyTray(tray); 732 } 733 SDL_free(trays); 734 735clean_window: 736 if (renderer) { 737 SDL_DestroyRenderer(renderer); 738 } 739 if (window) { 740 SDL_DestroyWindow(window); 741 } 742 743quit: 744 SDL_Quit(); 745 SDLTest_CommonDestroyState(state); 746 747 return 0; 748} 749[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.