#include #include #include #include #include "tray.h" #define WM_TRAY_CALLBACK_MESSAGE (WM_USER + 1) #define WC_TRAY_CLASS_NAME L"TRAY" #define ID_TRAY_FIRST 1000 static WNDCLASSEXW wc; static NOTIFYICONDATAW nid; static HWND hwnd; static HMENU hmenu = NULL; static struct tray *g_tray = NULL; static int g_icon_is_shared = 0; static LRESULT CALLBACK _tray_wnd_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { switch (msg) { case WM_CLOSE: DestroyWindow(hwnd); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; case WM_TRAY_CALLBACK_MESSAGE: if (lparam == WM_LBUTTONUP) { if (g_tray && g_tray->left_click_cb) { g_tray->left_click_cb(g_tray->left_click_context); } return 0; } if (lparam == WM_RBUTTONUP) { POINT p; GetCursorPos(&p); SetForegroundWindow(hwnd); WORD cmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, p.x, p.y, 0, hwnd, NULL); SendMessage(hwnd, WM_COMMAND, cmd, 0); return 0; } break; case WM_COMMAND: if (wparam >= ID_TRAY_FIRST) { MENUITEMINFOW item = { .cbSize = sizeof(MENUITEMINFOW), .fMask = MIIM_ID | MIIM_DATA, }; if (GetMenuItemInfoW(hmenu, wparam, FALSE, &item)) { struct tray_menu *menu = (struct tray_menu *)item.dwItemData; if (menu != NULL && menu->cb != NULL) { menu->cb(menu); } } return 0; } break; } return DefWindowProcW(hwnd, msg, wparam, lparam); } static HMENU _tray_menu(struct tray_menu *m, UINT *id) { HMENU hmenu = CreatePopupMenu(); for (; m != NULL && m->text != NULL; m++, (*id)++) { if (strcmp(m->text, "-") == 0) { InsertMenuW(hmenu, *id, MF_BYPOSITION | MF_SEPARATOR, *id, L""); } else { // UTF-8 zu UTF-16 (WCHAR) konvertieren int len = MultiByteToWideChar(CP_UTF8, 0, m->text, -1, NULL, 0); if (len > 0) { wchar_t *wtext = (wchar_t*)malloc(len * sizeof(wchar_t)); if (wtext) { MultiByteToWideChar(CP_UTF8, 0, m->text, -1, wtext, len); MENUITEMINFOW item; memset(&item, 0, sizeof(item)); item.cbSize = sizeof(MENUITEMINFOW); item.fMask = MIIM_ID | MIIM_TYPE | MIIM_STATE | MIIM_DATA; item.fType = 0; item.fState = 0; if (m->submenu != NULL) { item.fMask = item.fMask | MIIM_SUBMENU; item.hSubMenu = _tray_menu(m->submenu, id); } if (m->disabled) { item.fState |= MFS_DISABLED; } if (m->checked) { item.fState |= MFS_CHECKED; } item.wID = *id; item.dwTypeData = wtext; item.dwItemData = (ULONG_PTR)m; InsertMenuItemW(hmenu, *id, TRUE, &item); free(wtext); } } } } return hmenu; } int tray_init(struct tray *tray) { memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEXW); wc.lpfnWndProc = _tray_wnd_proc; wc.hInstance = GetModuleHandle(NULL); wc.lpszClassName = WC_TRAY_CLASS_NAME; if (!RegisterClassExW(&wc)) { return -1; } hwnd = CreateWindowExW(0, WC_TRAY_CLASS_NAME, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0); if (hwnd == NULL) { return -1; } UpdateWindow(hwnd); memset(&nid, 0, sizeof(nid)); nid.cbSize = sizeof(NOTIFYICONDATA); nid.hWnd = hwnd; nid.uID = 0; nid.uFlags = NIF_ICON | NIF_MESSAGE; nid.uCallbackMessage = WM_TRAY_CALLBACK_MESSAGE; Shell_NotifyIconW(NIM_ADD, &nid); g_tray = tray; tray_update(tray); return 0; } int tray_loop(int blocking) { MSG msg; if (blocking) { GetMessage(&msg, hwnd, 0, 0); } else { PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE); } if (msg.message == WM_QUIT) { return -1; } TranslateMessage(&msg); DispatchMessage(&msg); return 0; } void tray_update(struct tray *tray) { g_tray = tray; HMENU prevmenu = hmenu; UINT id = ID_TRAY_FIRST; hmenu = _tray_menu(tray->menu, &id); SendMessage(hwnd, WM_INITMENUPOPUP, (WPARAM)hmenu, 0); HICON icon = NULL; int icon_is_shared = 0; if (tray->icon_handle) { icon = (HICON)tray->icon_handle; icon_is_shared = tray->icon_is_shared; } else if (tray->icon) { ExtractIconExA(tray->icon, 0, NULL, &icon, 1); } if (nid.hIcon && !g_icon_is_shared) { DestroyIcon(nid.hIcon); } if (icon) { nid.hIcon = icon; g_icon_is_shared = icon_is_shared; } if(tray->tooltip != 0 && strlen(tray->tooltip) > 0) { MultiByteToWideChar(CP_UTF8, 0, tray->tooltip, -1, nid.szTip, sizeof(nid.szTip)/sizeof(wchar_t)); nid.uFlags |= NIF_TIP; } Shell_NotifyIconW(NIM_MODIFY, &nid); if (prevmenu != NULL) { DestroyMenu(prevmenu); } } void tray_exit(void) { Shell_NotifyIconW(NIM_DELETE, &nid); if (nid.hIcon != 0 && !g_icon_is_shared) { DestroyIcon(nid.hIcon); } if (hmenu != 0) { DestroyMenu(hmenu); } PostQuitMessage(0); UnregisterClassW(WC_TRAY_CLASS_NAME, GetModuleHandle(NULL)); }