#include "window.h"
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int window_show(const void *ptr)
{
if (!ptr)
{
return 0;
}
HWND hwnd = (HWND)ptr;
ShowWindow(hwnd, SW_SHOW);
return 1;
}
int window_hide(const void *ptr)
{
if (!ptr)
{
return 0;
}
HWND hwnd = (HWND)ptr;
ShowWindow(hwnd, SW_HIDE);
return 1;
}
const LPCWSTR char2lpcwstr(char *str)
{
if (!str || *str == '\0')
{
static const wchar_t empty[] = L"";
return empty;
}
int wchar_count = MultiByteToWideChar(
CP_UTF8, 0, str, -1, NULL, 0);
if (wchar_count == 0)
{
static const wchar_t empty[] = L"";
return empty;
}
static wchar_t *buffer = NULL;
static size_t buffer_size = 0;
if (buffer_size < (size_t)wchar_count)
{
if (buffer)
{
free(buffer);
}
buffer = (wchar_t *)malloc(wchar_count * sizeof(wchar_t));
if (!buffer)
{
buffer_size = 0;
static const wchar_t empty[] = L"";
return empty;
}
buffer_size = wchar_count;
}
if (!MultiByteToWideChar(
CP_UTF8, 0, str, -1, buffer, wchar_count))
{
static const wchar_t empty[] = L"";
return empty;
}
return buffer;
}
typedef struct
{
int id;
char *text;
int disabled;
int checked;
void (*callback)(const void *ptr);
} TrayMenuItem;
typedef struct
{
NOTIFYICONDATA nid;
HMENU hMenu;
HWND hwnd;
HWND hTrayWnd;
TrayMenuItem *items;
int item_count;
HICON hIcon;
} TrayData;
static const char *TRAY_WINDOW_CLASS = "TrayHelperWindowClass";
static LRESULT CALLBACK TrayWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
TrayData *tray_data = (TrayData *)GetWindowLongPtr(hwnd, GWLP_USERDATA);
switch (msg)
{
case WM_CREATE:
{
CREATESTRUCT *cs = (CREATESTRUCT *)lParam;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_COMMAND:
if (tray_data && tray_data->items)
{
int id = LOWORD(wParam);
for (int i = 0; i < tray_data->item_count; i++)
{
if (tray_data->items[i].id == id &&
tray_data->items[i].callback &&
!tray_data->items[i].disabled)
{
tray_data->items[i].callback(&tray_data->items[i]);
break;
}
}
}
break;
case WM_USER + 1:
if (lParam == WM_RBUTTONUP || lParam == WM_CONTEXTMENU || lParam == WM_LBUTTONUP)
{
if (tray_data && tray_data->hMenu)
{
POINT pt;
GetCursorPos(&pt);
SetForegroundWindow(hwnd);
for (int i = 0; i < tray_data->item_count; i++)
{
UINT state = MF_BYCOMMAND;
if (tray_data->items[i].disabled)
state |= MF_GRAYED;
else
state |= MF_ENABLED;
if (tray_data->items[i].checked)
state |= MF_CHECKED;
else
state |= MF_UNCHECKED;
CheckMenuItem(tray_data->hMenu, tray_data->items[i].id, state);
EnableMenuItem(tray_data->hMenu, tray_data->items[i].id,
tray_data->items[i].disabled ? MF_GRAYED : MF_ENABLED);
}
TrackPopupMenu(tray_data->hMenu,
TPM_RIGHTBUTTON | TPM_BOTTOMALIGN,
pt.x, pt.y, 0, hwnd, NULL);
PostMessage(hwnd, WM_NULL, 0, 0);
}
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
* @brief 创建窗口托盘
*
* @param ptr 窗口句柄
* @param icon 托盘图标路径
* @return void* 托盘句柄
*/
void *window_tray(const void *ptr, const char *icon)
{
if (!ptr)
{
return NULL;
}
HWND hwnd = (HWND)ptr;
WNDCLASSEX wc = {0};
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = TrayWndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = TRAY_WINDOW_CLASS;
if (!GetClassInfoEx(wc.hInstance, TRAY_WINDOW_CLASS, &wc))
{
if (!RegisterClassEx(&wc))
{
return NULL;
}
}
TrayData *tray_data = (TrayData *)calloc(1, sizeof(TrayData));
if (!tray_data)
{
return NULL;
}
tray_data->hwnd = hwnd;
tray_data->hMenu = CreatePopupMenu();
tray_data->item_count = 0;
tray_data->items = NULL;
tray_data->hTrayWnd = CreateWindowEx(
0,
TRAY_WINDOW_CLASS,
"TrayHelper",
0,
0, 0, 0, 0,
NULL,
NULL,
GetModuleHandle(NULL),
tray_data
);
if (!tray_data->hTrayWnd)
{
free(tray_data);
return NULL;
}
if (icon)
{
tray_data->hIcon = (HICON)LoadImage(
NULL,
icon,
IMAGE_ICON,
0, 0,
LR_LOADFROMFILE | LR_DEFAULTSIZE | LR_LOADTRANSPARENT);
}
if (!tray_data->hIcon)
{
tray_data->hIcon = LoadIcon(NULL, IDI_APPLICATION);
}
tray_data->nid.cbSize = sizeof(NOTIFYICONDATA);
tray_data->nid.hWnd = tray_data->hTrayWnd;
tray_data->nid.uID = 1;
tray_data->nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
tray_data->nid.uCallbackMessage = WM_USER + 1;
tray_data->nid.hIcon = tray_data->hIcon;
strncpy(tray_data->nid.szTip, "", sizeof(tray_data->nid.szTip) - 1);
if (!Shell_NotifyIcon(NIM_ADD, &tray_data->nid))
{
if (tray_data->hIcon && tray_data->hIcon != LoadIcon(NULL, IDI_APPLICATION))
{
DestroyIcon(tray_data->hIcon);
}
DestroyWindow(tray_data->hTrayWnd);
free(tray_data);
return NULL;
}
return tray_data;
}
* @brief 添加托盘菜单
*
* @param tray 托盘句柄
* @param menu 菜单配置
*/
void window_tray_add_menu(const void *tray, struct tray_menu *menu)
{
if (!tray || !menu || !menu->text)
{
return;
}
TrayData *tray_data = (TrayData *)tray;
TrayMenuItem *new_items = realloc(
tray_data->items,
sizeof(TrayMenuItem) * (tray_data->item_count + 1));
if (!new_items)
{
return;
}
tray_data->items = new_items;
TrayMenuItem *item = &tray_data->items[tray_data->item_count];
item->id = menu->id;
item->disabled = menu->disabled;
item->checked = menu->checked;
item->callback = menu->callback;
item->text = _strdup(menu->text);
if (!item->text)
{
return;
}
UINT flags = MF_STRING;
if (menu->disabled)
flags |= MF_GRAYED;
if (menu->checked)
flags |= MF_CHECKED;
const LPCWSTR wtext = char2lpcwstr(menu->text);
AppendMenuW(tray_data->hMenu, flags, menu->id, wtext);
tray_data->item_count++;
}
* @brief 移除托盘图标(清理函数)
*
* @param tray 托盘句柄
*/
void window_tray_remove(void *tray)
{
if (!tray)
{
return;
}
TrayData *tray_data = (TrayData *)tray;
Shell_NotifyIcon(NIM_DELETE, &tray_data->nid);
if (tray_data->hIcon && tray_data->hIcon != LoadIcon(NULL, IDI_APPLICATION))
{
DestroyIcon(tray_data->hIcon);
}
if (tray_data->hMenu)
{
DestroyMenu(tray_data->hMenu);
}
if (tray_data->items)
{
for (int i = 0; i < tray_data->item_count; i++)
{
if (tray_data->items[i].text)
{
free(tray_data->items[i].text);
}
}
free(tray_data->items);
}
if (tray_data->hTrayWnd && IsWindow(tray_data->hTrayWnd))
{
DestroyWindow(tray_data->hTrayWnd);
}
free(tray_data);
}