testtray.c 22 KB

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