github.com/shurcool/trayhost@v0.0.0-20181020202213-114974ef9e16/platform/windows/tray.c (about) 1 #include <stdio.h> 2 #include <windows.h> 3 #include <shellapi.h> 4 5 #define WM_MYMESSAGE (WM_USER + 1) 6 7 #define MAX_LOADSTRING 100 8 9 HINSTANCE hInst; 10 HMENU hSubMenu; 11 TCHAR szTitle[MAX_LOADSTRING]; 12 TCHAR szWindowClass[MAX_LOADSTRING]; 13 wchar_t *titleWide; 14 NOTIFYICONDATA nid; 15 16 ATOM MyRegisterClass(HINSTANCE hInstance); 17 HWND InitInstance(HINSTANCE, int); 18 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); 19 20 extern void tray_callback(int itemId); 21 22 void add_separator_item() 23 { 24 AppendMenuW(hSubMenu, MF_SEPARATOR, 0, NULL); 25 } 26 27 void add_menu_item(int id, const char* title, int disabled) 28 { 29 UINT uFlags = MF_STRING; 30 if (disabled == TRUE) { 31 uFlags |= MF_GRAYED; 32 } 33 AppendMenuW(hSubMenu, uFlags, id, (wchar_t*)title); 34 } 35 36 void clear_menu_items() 37 { 38 int count = GetMenuItemCount(hSubMenu); 39 UINT i = 0; 40 for (; i < count; i++) { 41 // always remove at 0 because they shift every time 42 RemoveMenu(hSubMenu, 0, MF_BYPOSITION); 43 } 44 } 45 46 void native_loop() 47 { 48 MSG msg; 49 // Main message loop: 50 while (GetMessage(&msg, NULL, 0, 0)) 51 { 52 TranslateMessage(&msg); 53 DispatchMessage(&msg); 54 } 55 } 56 57 int init(const char * title, struct image img) { 58 HWND hWnd; 59 HINSTANCE hInstance = GetModuleHandle(NULL); 60 61 // get thish shit into windows whide chars or whatever 62 titleWide = (wchar_t*)calloc(strlen(title) + 1, sizeof(wchar_t)); 63 mbstowcs(titleWide, title, strlen(title)); 64 65 wcscpy((wchar_t*)szTitle, titleWide); 66 wcscpy((wchar_t*)szWindowClass, (wchar_t*)TEXT("MyClass")); 67 MyRegisterClass(hInstance); 68 69 hWnd = InitInstance(hInstance, FALSE); // Don't show window 70 if (!hWnd) 71 { 72 return -1; 73 } 74 75 // Let's load up the tray icon 76 HICON hIcon; 77 { 78 // This is really hacky, but LoadImage won't let me load an image from memory. 79 // So we have to write out a temporary file, load it from there, then delete the file. 80 81 // From http://msdn.microsoft.com/en-us/library/windows/desktop/aa363875.aspx 82 TCHAR szTempFileName[MAX_PATH+1]; 83 TCHAR lpTempPathBuffer[MAX_PATH+1]; 84 int dwRetVal = GetTempPath(MAX_PATH+1, // length of the buffer 85 lpTempPathBuffer); // buffer for path 86 if (dwRetVal > MAX_PATH+1 || (dwRetVal == 0)) 87 { 88 return -1; // Failure 89 } 90 91 // Generates a temporary file name. 92 int uRetVal = GetTempFileName(lpTempPathBuffer, // directory for tmp files 93 TEXT("_tmpicon"), // temp file name prefix 94 0, // create unique name 95 szTempFileName); // buffer for name 96 if (uRetVal == 0) 97 { 98 return -1; // Failure 99 } 100 101 // Dump the icon to the temp file 102 FILE* fIcon = fopen(szTempFileName, "wb"); 103 fwrite(img.bytes, 1, img.length, fIcon); 104 fclose(fIcon); 105 fIcon = NULL; 106 107 // Load the image from the file 108 hIcon = LoadImage(NULL, szTempFileName, IMAGE_ICON, 64, 64, LR_LOADFROMFILE); 109 110 // Delete the temp file 111 remove(szTempFileName); 112 } 113 114 nid.cbSize = sizeof(NOTIFYICONDATA); 115 nid.hWnd = hWnd; 116 nid.uID = 100; 117 nid.uCallbackMessage = WM_MYMESSAGE; 118 nid.hIcon = hIcon; 119 120 strcpy(nid.szTip, title); // MinGW seems to use ANSI 121 nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; 122 123 Shell_NotifyIcon(NIM_ADD, &nid); 124 125 hSubMenu = CreatePopupMenu(); 126 return 0; 127 } 128 129 void exit_loop() { 130 Shell_NotifyIcon(NIM_DELETE, &nid); 131 PostQuitMessage(0); 132 } 133 134 135 ATOM MyRegisterClass(HINSTANCE hInstance) 136 { 137 WNDCLASSEX wcex; 138 139 wcex.cbSize = sizeof(WNDCLASSEX); 140 141 wcex.style = CS_HREDRAW | CS_VREDRAW; 142 wcex.lpfnWndProc = WndProc; 143 wcex.cbClsExtra = 0; 144 wcex.cbWndExtra = 0; 145 wcex.hInstance = hInstance; 146 wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); 147 wcex.hCursor = LoadCursor(NULL, IDC_ARROW); 148 wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); 149 wcex.lpszMenuName = 0; 150 wcex.lpszClassName = szWindowClass; 151 wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); 152 153 return RegisterClassEx(&wcex); 154 } 155 156 HWND InitInstance(HINSTANCE hInstance, int nCmdShow) 157 { 158 HWND hWnd; 159 160 hInst = hInstance; 161 162 hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, 163 CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 164 // hWnd = CreateWindowW(L"Krneki", L"Title", WS_OVERLAPPEDWINDOW, 165 // CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 166 167 if (!hWnd) 168 { 169 return 0; 170 } 171 172 ShowWindow(hWnd, nCmdShow); 173 UpdateWindow(hWnd); 174 175 return hWnd; 176 } 177 178 void ShowMenu(HWND hWnd) 179 { 180 POINT p; 181 GetCursorPos(&p); 182 SetForegroundWindow(hWnd); // Win32 bug work-around 183 TrackPopupMenu(hSubMenu, TPM_BOTTOMALIGN | TPM_LEFTALIGN, p.x, p.y, 0, hWnd, NULL); 184 } 185 186 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) 187 { 188 switch (message) 189 { 190 case WM_COMMAND: 191 tray_callback(LOWORD(wParam)); 192 break; 193 case WM_DESTROY: 194 exit_loop(); 195 break; 196 case WM_MYMESSAGE: 197 switch(lParam) 198 { 199 case WM_RBUTTONUP: 200 ShowMenu(hWnd); 201 break; 202 case WM_LBUTTONUP: 203 tray_callback(-1); 204 break; 205 default: 206 return DefWindowProc(hWnd, message, wParam, lParam); 207 }; 208 break; 209 default: 210 return DefWindowProc(hWnd, message, wParam, lParam); 211 } 212 return 0; 213 } 214 215 void set_clipboard_string(const char * string) { 216 // TODO: Implement. 217 } 218 219 struct clipboard_content get_clipboard_content() { 220 // TODO: Implement. 221 struct clipboard_content cc; 222 return cc; 223 } 224 225 void display_notification(int notificationId, const char * title, const char * body, struct image img, double timeout) { 226 // TODO: Implement. 227 }