Atlas - SDL_tray.c

Home / ext / SDL / src / tray / windows Lines: 1 | Size: 20663 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 Sam Lantinga <[email protected]> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21 22#include "SDL_internal.h" 23 24#include "../SDL_tray_utils.h" 25#include "../../core/windows/SDL_windows.h" 26 27#include <windowsx.h> 28#include <shellapi.h> 29 30#ifndef NOTIFYICON_VERSION_4 31#define NOTIFYICON_VERSION_4 4 32#endif 33#ifndef NIF_SHOWTIP 34#define NIF_SHOWTIP 0x00000080 35#endif 36 37#define WM_TRAYICON (WM_USER + 1) 38 39struct SDL_TrayMenu { 40 HMENU hMenu; 41 42 int nEntries; 43 SDL_TrayEntry **entries; 44 45 SDL_Tray *parent_tray; 46 SDL_TrayEntry *parent_entry; 47}; 48 49struct SDL_TrayEntry { 50 SDL_TrayMenu *parent; 51 UINT_PTR id; 52 53 char label_cache[4096]; 54 SDL_TrayEntryFlags flags; 55 SDL_TrayCallback callback; 56 void *userdata; 57 SDL_TrayMenu *submenu; 58}; 59 60struct SDL_Tray { 61 NOTIFYICONDATAW nid; 62 HWND hwnd; 63 HICON icon; 64 SDL_TrayMenu *menu; 65 66 void *userdata; 67 SDL_TrayClickCallback left_click_callback; 68 SDL_TrayClickCallback right_click_callback; 69 SDL_TrayClickCallback middle_click_callback; 70}; 71 72static UINT_PTR get_next_id(void) 73{ 74 static UINT_PTR next_id = 0; 75 return ++next_id; 76} 77 78static SDL_TrayEntry *find_entry_in_menu(SDL_TrayMenu *menu, UINT_PTR id) 79{ 80 for (int i = 0; i < menu->nEntries; i++) { 81 SDL_TrayEntry *entry = menu->entries[i]; 82 83 if (entry->id == id) { 84 return entry; 85 } 86 87 if (entry->submenu) { 88 SDL_TrayEntry *e = find_entry_in_menu(entry->submenu, id); 89 90 if (e) { 91 return e; 92 } 93 } 94 } 95 96 return NULL; 97} 98 99static SDL_TrayEntry *find_entry_with_id(SDL_Tray *tray, UINT_PTR id) 100{ 101 if (!tray->menu) { 102 return NULL; 103 } 104 105 return find_entry_in_menu(tray->menu, id); 106} 107 108LRESULT CALLBACK TrayWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 109 SDL_Tray *tray = (SDL_Tray *) GetWindowLongPtr(hwnd, GWLP_USERDATA); 110 SDL_TrayEntry *entry = NULL; 111 112 if (!tray) { 113 return DefWindowProc(hwnd, uMsg, wParam, lParam); 114 } 115 116 static UINT s_taskbarRestart = 0; 117 if (s_taskbarRestart == 0) { 118 s_taskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); 119 } 120 if (uMsg == s_taskbarRestart) { 121 Shell_NotifyIconW(NIM_ADD, &tray->nid); 122 Shell_NotifyIconW(NIM_SETVERSION, &tray->nid); 123 } 124 125 switch (uMsg) { 126 case WM_TRAYICON: 127 { 128 bool show_menu = false; 129 130 switch (LOWORD(lParam)) { 131 case WM_LBUTTONUP: 132 if (tray->left_click_callback) { 133 show_menu = tray->left_click_callback(tray->userdata, tray); 134 } else { 135 show_menu = true; 136 } 137 break; 138 139 case WM_CONTEXTMENU: 140 if (tray->right_click_callback) { 141 show_menu = tray->right_click_callback(tray->userdata, tray); 142 } else { 143 show_menu = true; 144 } 145 break; 146 147 case WM_MBUTTONUP: 148 if (tray->middle_click_callback) { 149 tray->middle_click_callback(tray->userdata, tray); 150 } 151 break; 152 } 153 154 if (show_menu && tray->menu) { 155 SetForegroundWindow(hwnd); 156 TrackPopupMenu(tray->menu->hMenu, TPM_BOTTOMALIGN | TPM_RIGHTALIGN, GET_X_LPARAM(wParam), GET_Y_LPARAM(wParam), 0, hwnd, NULL); 157 } 158 } 159 break; 160 161 case WM_COMMAND: 162 entry = find_entry_with_id(tray, LOWORD(wParam)); 163 164 if (entry && (entry->flags & SDL_TRAYENTRY_CHECKBOX)) { 165 SDL_SetTrayEntryChecked(entry, !SDL_GetTrayEntryChecked(entry)); 166 } 167 168 if (entry && entry->callback) { 169 entry->callback(entry->userdata, entry); 170 } 171 break; 172 173 case WM_SETTINGCHANGE: 174 if (wParam == 0 && lParam != 0 && SDL_wcscmp((wchar_t *)lParam, L"ImmersiveColorSet") == 0) { 175 WIN_UpdateDarkModeForHWND(hwnd); 176 } 177 break; 178 179 default: 180 return DefWindowProc(hwnd, uMsg, wParam, lParam); 181 } 182 return 0; 183} 184 185static void DestroySDLMenu(SDL_TrayMenu *menu) 186{ 187 for (int i = 0; i < menu->nEntries; i++) { 188 if (menu->entries[i] && menu->entries[i]->submenu) { 189 DestroySDLMenu(menu->entries[i]->submenu); 190 } 191 SDL_free(menu->entries[i]); 192 } 193 SDL_free(menu->entries); 194 DestroyMenu(menu->hMenu); 195 SDL_free(menu); 196} 197 198static wchar_t *escape_label(const char *in) 199{ 200 const char *c; 201 char *c2; 202 int len = 0; 203 204 for (c = in; *c; c++) { 205 len += (*c == '&') ? 2 : 1; 206 } 207 208 char *escaped = (char *)SDL_malloc(SDL_strlen(in) + len + 1); 209 if (!escaped) { 210 return NULL; 211 } 212 213 for (c = in, c2 = escaped; *c;) { 214 if (*c == '&') { 215 *c2++ = *c; 216 } 217 218 *c2++ = *c++; 219 } 220 221 *c2 = '\0'; 222 223 wchar_t *out = WIN_UTF8ToStringW(escaped); 224 SDL_free(escaped); 225 226 return out; 227} 228 229static HICON load_default_icon(void) 230{ 231 HINSTANCE hInstance = GetModuleHandle(NULL); 232 if (!hInstance) { 233 return LoadIcon(NULL, IDI_APPLICATION); 234 } 235 236 const char *hint = SDL_GetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL); 237 if (hint && *hint) { 238 HICON icon = LoadIcon(hInstance, MAKEINTRESOURCE(SDL_atoi(hint))); 239 return icon ? icon : LoadIcon(NULL, IDI_APPLICATION); 240 } 241 242 hint = SDL_GetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON); 243 if (hint && *hint) { 244 HICON icon = LoadIcon(hInstance, MAKEINTRESOURCE(SDL_atoi(hint))); 245 return icon ? icon : LoadIcon(NULL, IDI_APPLICATION); 246 } 247 248 return LoadIcon(NULL, IDI_APPLICATION); 249} 250 251void SDL_UpdateTrays(void) 252{ 253} 254 255static void SDL_PropTrayCleanupCb(void *userdata, void *cls) 256{ 257 WNDCLASSEX wcex; 258 259 wcex.hIcon = NULL; 260 wcex.hIconSm = NULL; 261 HINSTANCE h = GetModuleHandle(NULL); 262 if (GetClassInfoEx(h, cls, &wcex)) { 263 UnregisterClass(cls, h); 264 } 265} 266 267static bool SDL_RegisterTrayClass(LPCWSTR className) 268{ 269 SDL_PropertiesID props = SDL_GetGlobalProperties(); 270 if (!props) { 271 return false; 272 } 273 if (SDL_GetPointerProperty(props, SDL_PROP_TRAY_CLEANUP, NULL) != NULL) { 274 return true; 275 } 276 277 WNDCLASSEX wcex; 278 279 wcex.cbSize = sizeof(WNDCLASSEX); 280 wcex.hCursor = NULL; 281 wcex.hIcon = NULL; 282 wcex.hIconSm = NULL; 283 wcex.lpszMenuName = NULL; 284 wcex.lpszClassName = className; 285 wcex.style = 0; 286 wcex.hbrBackground = NULL; 287 wcex.lpfnWndProc = TrayWindowProc; 288 wcex.hInstance = NULL; 289 wcex.cbClsExtra = 0; 290 wcex.cbWndExtra = 0; 291 292 if (!RegisterClassEx(&wcex)) { 293 return SDL_SetError("Couldn't register tray class"); 294 } 295 296 SDL_SetPointerPropertyWithCleanup(props, SDL_PROP_TRAY_CLEANUP, (void *)wcex.lpszClassName, SDL_PropTrayCleanupCb, NULL); 297 return true; 298} 299 300SDL_Tray *SDL_CreateTrayWithProperties(SDL_PropertiesID props) 301{ 302 if (!SDL_IsMainThread()) { 303 SDL_SetError("This function should be called on the main thread"); 304 return NULL; 305 } 306 307 SDL_Tray *tray = (SDL_Tray *)SDL_calloc(1, sizeof(*tray)); 308 309 if (!tray) { 310 return NULL; 311 } 312 313 SDL_Surface *icon = (SDL_Surface *)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_ICON_POINTER, NULL); 314 const char *tooltip = SDL_GetStringProperty(props, SDL_PROP_TRAY_CREATE_TOOLTIP_STRING, NULL); 315 316 tray->userdata = SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_USERDATA_POINTER, NULL); 317 tray->left_click_callback = (SDL_TrayClickCallback)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_LEFTCLICK_CALLBACK_POINTER, NULL); 318 tray->right_click_callback = (SDL_TrayClickCallback)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_RIGHTCLICK_CALLBACK_POINTER, NULL); 319 tray->middle_click_callback = (SDL_TrayClickCallback)SDL_GetPointerProperty(props, SDL_PROP_TRAY_CREATE_MIDDLECLICK_CALLBACK_POINTER, NULL); 320 321 tray->menu = NULL; 322 if (!SDL_RegisterTrayClass(TEXT("SDL_TRAY"))) { 323 SDL_SetError("Failed to register SDL_TRAY window class"); 324 SDL_free(tray); 325 return NULL; 326 } 327 tray->hwnd = CreateWindowEx(0, TEXT("SDL_TRAY"), NULL, WS_OVERLAPPEDWINDOW, 328 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, NULL, NULL); 329 330 WIN_UpdateDarkModeForHWND(tray->hwnd); 331 332 SDL_zero(tray->nid); 333 tray->nid.cbSize = sizeof(NOTIFYICONDATAW); 334 tray->nid.hWnd = tray->hwnd; 335 tray->nid.uID = (UINT) get_next_id(); 336 tray->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_SHOWTIP; 337 tray->nid.uCallbackMessage = WM_TRAYICON; 338 tray->nid.uVersion = NOTIFYICON_VERSION_4; 339 if (tooltip) { 340 wchar_t *tooltipw = WIN_UTF8ToStringW(tooltip); 341 if(tooltipw) { 342 SDL_wcslcpy(tray->nid.szTip, tooltipw, sizeof(tray->nid.szTip) / sizeof(*tray->nid.szTip)); 343 SDL_free(tooltipw); 344 } 345 } 346 347 if (icon) { 348 tray->nid.hIcon = WIN_CreateIconFromSurface(icon); 349 350 if (!tray->nid.hIcon) { 351 tray->nid.hIcon = load_default_icon(); 352 } 353 354 tray->icon = tray->nid.hIcon; 355 } else { 356 tray->nid.hIcon = load_default_icon(); 357 tray->icon = tray->nid.hIcon; 358 } 359 360 Shell_NotifyIconW(NIM_ADD, &tray->nid); 361 Shell_NotifyIconW(NIM_SETVERSION, &tray->nid); 362 363 SetWindowLongPtr(tray->hwnd, GWLP_USERDATA, (LONG_PTR) tray); 364 365 SDL_RegisterTray(tray); 366 367 return tray; 368} 369 370SDL_Tray *SDL_CreateTray(SDL_Surface *icon, const char *tooltip) 371{ 372 SDL_Tray *tray; 373 SDL_PropertiesID props = SDL_CreateProperties(); 374 if (!props) { 375 return NULL; 376 } 377 if (icon) { 378 SDL_SetPointerProperty(props, SDL_PROP_TRAY_CREATE_ICON_POINTER, icon); 379 } 380 if (tooltip) { 381 SDL_SetStringProperty(props, SDL_PROP_TRAY_CREATE_TOOLTIP_STRING, tooltip); 382 } 383 tray = SDL_CreateTrayWithProperties(props); 384 SDL_DestroyProperties(props); 385 return tray; 386} 387 388void SDL_SetTrayIcon(SDL_Tray *tray, SDL_Surface *icon) 389{ 390 if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) { 391 return; 392 } 393 394 if (tray->icon) { 395 DestroyIcon(tray->icon); 396 } 397 398 if (icon) { 399 tray->nid.hIcon = WIN_CreateIconFromSurface(icon); 400 401 if (!tray->nid.hIcon) { 402 tray->nid.hIcon = load_default_icon(); 403 } 404 405 tray->icon = tray->nid.hIcon; 406 } else { 407 tray->nid.hIcon = load_default_icon(); 408 tray->icon = tray->nid.hIcon; 409 } 410 411 Shell_NotifyIconW(NIM_MODIFY, &tray->nid); 412} 413 414void SDL_SetTrayTooltip(SDL_Tray *tray, const char *tooltip) 415{ 416 if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) { 417 return; 418 } 419 420 if (tooltip) { 421 wchar_t *tooltipw = WIN_UTF8ToStringW(tooltip); 422 SDL_wcslcpy(tray->nid.szTip, tooltipw, sizeof(tray->nid.szTip) / sizeof(*tray->nid.szTip)); 423 SDL_free(tooltipw); 424 } else { 425 tray->nid.szTip[0] = '\0'; 426 } 427 428 Shell_NotifyIconW(NIM_MODIFY, &tray->nid); 429} 430 431SDL_TrayMenu *SDL_CreateTrayMenu(SDL_Tray *tray) 432{ 433 CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) { 434 SDL_InvalidParamError("tray"); 435 return NULL; 436 } 437 438 tray->menu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*tray->menu)); 439 440 if (!tray->menu) { 441 return NULL; 442 } 443 444 tray->menu->hMenu = CreatePopupMenu(); 445 tray->menu->parent_tray = tray; 446 tray->menu->parent_entry = NULL; 447 448 return tray->menu; 449} 450 451SDL_TrayMenu *SDL_GetTrayMenu(SDL_Tray *tray) 452{ 453 CHECK_PARAM(!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) { 454 SDL_InvalidParamError("tray"); 455 return NULL; 456 } 457 458 return tray->menu; 459} 460 461SDL_TrayMenu *SDL_CreateTraySubmenu(SDL_TrayEntry *entry) 462{ 463 CHECK_PARAM(!entry) { 464 SDL_InvalidParamError("entry"); 465 return NULL; 466 } 467 468 if (!entry->submenu) { 469 SDL_SetError("Cannot create submenu for entry not created with SDL_TRAYENTRY_SUBMENU"); 470 return NULL; 471 } 472 473 return entry->submenu; 474} 475 476SDL_TrayMenu *SDL_GetTraySubmenu(SDL_TrayEntry *entry) 477{ 478 CHECK_PARAM(!entry) { 479 SDL_InvalidParamError("entry"); 480 return NULL; 481 } 482 483 return entry->submenu; 484} 485 486const SDL_TrayEntry **SDL_GetTrayEntries(SDL_TrayMenu *menu, int *count) 487{ 488 CHECK_PARAM(!menu) { 489 SDL_InvalidParamError("menu"); 490 return NULL; 491 } 492 493 if (count) { 494 *count = menu->nEntries; 495 } 496 return (const SDL_TrayEntry **)menu->entries; 497} 498 499void SDL_RemoveTrayEntry(SDL_TrayEntry *entry) 500{ 501 if (!entry) { 502 return; 503 } 504 505 SDL_TrayMenu *menu = entry->parent; 506 507 bool found = false; 508 for (int i = 0; i < menu->nEntries - 1; i++) { 509 if (menu->entries[i] == entry) { 510 found = true; 511 } 512 513 if (found) { 514 menu->entries[i] = menu->entries[i + 1]; 515 } 516 } 517 518 if (entry->submenu) { 519 DestroySDLMenu(entry->submenu); 520 } 521 522 menu->nEntries--; 523 SDL_TrayEntry **new_entries = (SDL_TrayEntry **)SDL_realloc(menu->entries, (menu->nEntries + 1) * sizeof(*new_entries)); 524 525 /* Not sure why shrinking would fail, but even if it does, we can live with a "too big" array */ 526 if (new_entries) { 527 menu->entries = new_entries; 528 menu->entries[menu->nEntries] = NULL; 529 } 530 531 if (!DeleteMenu(menu->hMenu, (UINT) entry->id, MF_BYCOMMAND)) { 532 /* This is somewhat useless since we don't return anything, but might help with eventual bugs */ 533 SDL_SetError("Couldn't destroy tray entry"); 534 } 535 536 SDL_free(entry); 537} 538 539SDL_TrayEntry *SDL_InsertTrayEntryAt(SDL_TrayMenu *menu, int pos, const char *label, SDL_TrayEntryFlags flags) 540{ 541 CHECK_PARAM(!menu) { 542 SDL_InvalidParamError("menu"); 543 return NULL; 544 } 545 546 CHECK_PARAM(pos < -1 || pos > menu->nEntries) { 547 SDL_InvalidParamError("pos"); 548 return NULL; 549 } 550 551 int windows_compatible_pos = pos; 552 553 if (pos == -1) { 554 pos = menu->nEntries; 555 } else if (pos == menu->nEntries) { 556 windows_compatible_pos = -1; 557 } 558 559 SDL_TrayEntry *entry = (SDL_TrayEntry *)SDL_calloc(1, sizeof(*entry)); 560 if (!entry) { 561 return NULL; 562 } 563 564 wchar_t *label_w = NULL; 565 566 if (label && (label_w = escape_label(label)) == NULL) { 567 SDL_free(entry); 568 return NULL; 569 } 570 571 entry->parent = menu; 572 entry->flags = flags; 573 entry->callback = NULL; 574 entry->userdata = NULL; 575 entry->submenu = NULL; 576 SDL_snprintf(entry->label_cache, sizeof(entry->label_cache), "%s", label ? label : ""); 577 578 if (label != NULL && flags & SDL_TRAYENTRY_SUBMENU) { 579 entry->submenu = (SDL_TrayMenu *)SDL_calloc(1, sizeof(*entry->submenu)); 580 if (!entry->submenu) { 581 SDL_free(entry); 582 SDL_free(label_w); 583 return NULL; 584 } 585 586 entry->submenu->hMenu = CreatePopupMenu(); 587 entry->submenu->nEntries = 0; 588 entry->submenu->entries = NULL; 589 entry->submenu->parent_entry = entry; 590 entry->submenu->parent_tray = NULL; 591 592 entry->id = (UINT_PTR) entry->submenu->hMenu; 593 } else { 594 entry->id = get_next_id(); 595 } 596 597 SDL_TrayEntry **new_entries = (SDL_TrayEntry **)SDL_realloc(menu->entries, (menu->nEntries + 2) * sizeof(*new_entries)); 598 if (!new_entries) { 599 SDL_free(entry); 600 SDL_free(label_w); 601 if (entry->submenu) { 602 DestroyMenu(entry->submenu->hMenu); 603 SDL_free(entry->submenu); 604 } 605 return NULL; 606 } 607 608 menu->entries = new_entries; 609 menu->nEntries++; 610 611 for (int i = menu->nEntries - 1; i > pos; i--) { 612 menu->entries[i] = menu->entries[i - 1]; 613 } 614 615 new_entries[pos] = entry; 616 new_entries[menu->nEntries] = NULL; 617 618 if (label == NULL) { 619 InsertMenuW(menu->hMenu, windows_compatible_pos, MF_SEPARATOR | MF_BYPOSITION, entry->id, NULL); 620 } else { 621 UINT mf = MF_STRING | MF_BYPOSITION; 622 if (flags & SDL_TRAYENTRY_SUBMENU) { 623 mf = MF_POPUP; 624 } 625 626 if (flags & SDL_TRAYENTRY_DISABLED) { 627 mf |= MF_DISABLED | MF_GRAYED; 628 } 629 630 if (flags & SDL_TRAYENTRY_CHECKED) { 631 mf |= MF_CHECKED; 632 } 633 634 InsertMenuW(menu->hMenu, windows_compatible_pos, mf, entry->id, label_w); 635 636 SDL_free(label_w); 637 } 638 639 return entry; 640} 641 642void SDL_SetTrayEntryLabel(SDL_TrayEntry *entry, const char *label) 643{ 644 if (!entry) { 645 return; 646 } 647 648 SDL_snprintf(entry->label_cache, sizeof(entry->label_cache), "%s", label); 649 650 wchar_t *label_w = escape_label(label); 651 652 if (!label_w) { 653 return; 654 } 655 656 MENUITEMINFOW mii; 657 mii.cbSize = sizeof(MENUITEMINFOW); 658 mii.fMask = MIIM_STRING; 659 660 mii.dwTypeData = label_w; 661 mii.cch = (UINT) SDL_wcslen(label_w); 662 663 if (!SetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, FALSE, &mii)) { 664 SDL_SetError("Couldn't update tray entry label"); 665 } 666 667 SDL_free(label_w); 668} 669 670const char *SDL_GetTrayEntryLabel(SDL_TrayEntry *entry) 671{ 672 CHECK_PARAM(!entry) { 673 SDL_InvalidParamError("entry"); 674 return NULL; 675 } 676 677 return entry->label_cache; 678} 679 680void SDL_SetTrayEntryChecked(SDL_TrayEntry *entry, bool checked) 681{ 682 if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { 683 return; 684 } 685 686 CheckMenuItem(entry->parent->hMenu, (UINT) entry->id, checked ? MF_CHECKED : MF_UNCHECKED); 687} 688 689bool SDL_GetTrayEntryChecked(SDL_TrayEntry *entry) 690{ 691 if (!entry || !(entry->flags & SDL_TRAYENTRY_CHECKBOX)) { 692 return false; 693 } 694 695 MENUITEMINFOW mii; 696 mii.cbSize = sizeof(MENUITEMINFOW); 697 mii.fMask = MIIM_STATE; 698 699 GetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, FALSE, &mii); 700 701 return ((mii.fState & MFS_CHECKED) != 0); 702} 703 704void SDL_SetTrayEntryEnabled(SDL_TrayEntry *entry, bool enabled) 705{ 706 if (!entry) { 707 return; 708 } 709 710 EnableMenuItem(entry->parent->hMenu, (UINT) entry->id, MF_BYCOMMAND | (enabled ? MF_ENABLED : (MF_DISABLED | MF_GRAYED))); 711} 712 713bool SDL_GetTrayEntryEnabled(SDL_TrayEntry *entry) 714{ 715 if (!entry) { 716 return false; 717 } 718 719 MENUITEMINFOW mii; 720 mii.cbSize = sizeof(MENUITEMINFOW); 721 mii.fMask = MIIM_STATE; 722 723 GetMenuItemInfoW(entry->parent->hMenu, (UINT) entry->id, FALSE, &mii); 724 725 return ((mii.fState & MFS_ENABLED) != 0); 726} 727 728void SDL_SetTrayEntryCallback(SDL_TrayEntry *entry, SDL_TrayCallback callback, void *userdata) 729{ 730 if (!entry) { 731 return; 732 } 733 734 entry->callback = callback; 735 entry->userdata = userdata; 736} 737 738void SDL_ClickTrayEntry(SDL_TrayEntry *entry) 739{ 740 if (!entry) { 741 return; 742 } 743 744 if (entry->flags & SDL_TRAYENTRY_CHECKBOX) { 745 SDL_SetTrayEntryChecked(entry, !SDL_GetTrayEntryChecked(entry)); 746 } 747 748 if (entry->callback) { 749 entry->callback(entry->userdata, entry); 750 } 751} 752 753SDL_TrayMenu *SDL_GetTrayEntryParent(SDL_TrayEntry *entry) 754{ 755 CHECK_PARAM(!entry) { 756 SDL_InvalidParamError("entry"); 757 return NULL; 758 } 759 760 return entry->parent; 761} 762 763SDL_TrayEntry *SDL_GetTrayMenuParentEntry(SDL_TrayMenu *menu) 764{ 765 CHECK_PARAM(!menu) { 766 SDL_InvalidParamError("menu"); 767 return NULL; 768 } 769 770 return menu->parent_entry; 771} 772 773SDL_Tray *SDL_GetTrayMenuParentTray(SDL_TrayMenu *menu) 774{ 775 CHECK_PARAM(!menu) { 776 SDL_InvalidParamError("menu"); 777 return NULL; 778 } 779 780 return menu->parent_tray; 781} 782 783void SDL_DestroyTray(SDL_Tray *tray) 784{ 785 if (!SDL_ObjectValid(tray, SDL_OBJECT_TYPE_TRAY)) { 786 return; 787 } 788 789 SDL_UnregisterTray(tray); 790 791 Shell_NotifyIconW(NIM_DELETE, &tray->nid); 792 793 if (tray->menu) { 794 DestroySDLMenu(tray->menu); 795 } 796 797 if (tray->icon) { 798 DestroyIcon(tray->icon); 799 } 800 801 if (tray->hwnd) { 802 DestroyWindow(tray->hwnd); 803 } 804 805 SDL_free(tray); 806} 807
[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.