1 /*
2 Bacula(R) - The Network Backup Solution
3
4 Copyright (C) 2000-2019 Kern Sibbald
5
6 The original author of Bacula is Kern Sibbald, with contributions
7 from many others, a complete list can be found in the file AUTHORS.
8
9 You may use this file and others of this release according to the
10 license defined in the LICENSE file, which includes the Affero General
11 Public License, v3.0 ("AGPLv3") and some additional permissions and
12 terms pursuant to its AGPLv3 Section 7.
13
14 This notice must be preserved when any source code is
15 conveyed and/or propagated.
16
17 Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20 *
21 * Kern Sibbald, August 2007
22 *
23 * This is a generic tray monitor routine, which is used by all three
24 * of the daemons. Each one compiles it with slightly different
25 * #defines.
26 *
27 */
28
29 #include "bacula.h"
30 #include "jcr.h"
31 #include "win32.h"
32
trayMonitor()33 trayMonitor::trayMonitor()
34 {
35
36 // m_tbcreated_msg = RegisterWindowMessage("TaskbarCreated");
37
38 /* Create a window to handle tray icon messages */
39 WNDCLASSEX trayclass;
40
41 trayclass.cbSize = sizeof(trayclass);
42 trayclass.style = 0;
43 trayclass.lpfnWndProc = trayMonitor::trayWinProc;
44 trayclass.cbClsExtra = 0;
45 trayclass.cbWndExtra = 0;
46 trayclass.hInstance = appInstance;
47 trayclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
48 trayclass.hCursor = LoadCursor(NULL, IDC_ARROW);
49 trayclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
50 trayclass.lpszMenuName = NULL;
51 trayclass.lpszClassName = APP_NAME;
52 trayclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
53
54 RegisterClassEx(&trayclass);
55
56 m_hwnd = CreateWindow(APP_NAME, APP_NAME, WS_OVERLAPPEDWINDOW,
57 CW_USEDEFAULT, CW_USEDEFAULT, 200, 200,
58 NULL, NULL, appInstance, NULL);
59 if (!m_hwnd) {
60 PostQuitMessage(0);
61 return;
62 }
63
64 /* Save our class pointer */
65 SetWindowLong(m_hwnd, GWL_USERDATA, (LPARAM)this);
66
67
68 // Load the icons for the tray
69 m_idle_icon = LoadIcon(appInstance, MAKEINTRESOURCE(IDI_IDLE));
70 m_running_icon = LoadIcon(appInstance, MAKEINTRESOURCE(IDI_RUNNING));
71 m_error_icon = LoadIcon(appInstance, MAKEINTRESOURCE(IDI_JOB_ERROR));
72 m_warn_icon = LoadIcon(appInstance, MAKEINTRESOURCE(IDI_JOB_WARNING));
73
74 /* Load the menu */
75 m_hmenu = LoadMenu(appInstance, MAKEINTRESOURCE(IDR_TRAYMENU));
76 m_visible = false;
77 m_installed = false;
78
79 /* Install the icon in the tray */
80 install();
81
82 /* Timer to trigger icon updating */
83 SetTimer(m_hwnd, 1, 5000, NULL);
84 }
85
~trayMonitor()86 trayMonitor::~trayMonitor()
87 {
88 /* Remove the icon from the tray */
89 sendMessage(NIM_DELETE, 0);
90
91 if (m_hmenu) {
92 DestroyMenu(m_hmenu);
93 m_hmenu = NULL;
94 }
95 }
96
install()97 void trayMonitor::install()
98 {
99 m_installed = true;
100 sendMessage(NIM_ADD, bacstat);
101 }
102
update(int bacstat)103 void trayMonitor::update(int bacstat)
104 {
105 if (!m_installed) {
106 install();
107 }
108 (void)bac_status(NULL, 0);
109 sendMessage(NIM_MODIFY, bacstat);
110 }
111
sendMessage(DWORD msg,int bacstat)112 void trayMonitor::sendMessage(DWORD msg, int bacstat)
113 {
114 struct s_last_job *job;
115
116 // Create the tray icon message
117 m_nid.hWnd = m_hwnd;
118 m_nid.cbSize = sizeof(m_nid);
119 m_nid.uID = IDI_BACULA; // never changes after construction
120 switch (bacstat) {
121 case 0:
122 m_nid.hIcon = m_idle_icon;
123 break;
124 case JS_Running:
125 m_nid.hIcon = m_running_icon;
126 break;
127 case JS_ErrorTerminated:
128 m_nid.hIcon = m_error_icon;
129 break;
130 default:
131 if (last_jobs->size() > 0) {
132 job = (struct s_last_job *)last_jobs->last();
133 if (job->Errors) {
134 m_nid.hIcon = m_warn_icon;
135 } else {
136 m_nid.hIcon = m_idle_icon;
137 }
138 } else {
139 m_nid.hIcon = m_idle_icon;
140 }
141 break;
142 }
143
144 m_nid.uFlags = NIF_ICON | NIF_MESSAGE;
145 m_nid.uCallbackMessage = WM_TRAYNOTIFY;
146
147
148 /* Use the resource string as tip */
149 if (LoadString(appInstance, IDI_BACULA, m_nid.szTip, sizeof(m_nid.szTip))) {
150 m_nid.uFlags |= NIF_TIP;
151 }
152
153 /* Add the Bacula status to the tip string */
154 if (m_nid.uFlags & NIF_TIP) {
155 bac_status(m_nid.szTip, sizeof(m_nid.szTip));
156 }
157
158 if (Shell_NotifyIcon(msg, &m_nid)) {
159 EnableMenuItem(m_hmenu, ID_CLOSE, MF_ENABLED);
160 }
161 }
162
163 /*
164 * This is the windows call back for our tray window
165 */
trayWinProc(HWND hwnd,UINT iMsg,WPARAM wParam,LPARAM lParam)166 LRESULT CALLBACK trayMonitor::trayWinProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
167 {
168 HMENU menu;
169 trayMonitor *mon = (trayMonitor *)GetWindowLong(hwnd, GWL_USERDATA);
170
171 switch (iMsg) {
172
173 /* Every five seconds, a timer message causes the icon to update */
174 case WM_TIMER:
175 if (isAService()) {
176 mon->install();
177 }
178 mon->update(bacstat);
179 break;
180
181 case WM_CREATE:
182 return 0;
183
184 case WM_COMMAND:
185 /* User has clicked an item on the tray monitor menu */
186 switch (LOWORD(wParam)) {
187 case ID_STATUS:
188 /* show the dialog box */
189 mon->m_status.show(true);
190 mon->update(bacstat);
191 break;
192
193 case ID_ABOUT:
194 /* Show the About box */
195 mon->m_about.show(true);
196 break;
197
198 /* This is turned off now */
199 #ifdef xxx
200 case ID_CLOSE:
201 /* User selected Close from the tray menu */
202 PostMessage(hwnd, WM_CLOSE, 0, 0);
203 break;
204 #endif
205
206 }
207 return 0;
208
209 /* Our special command to check for mouse events */
210 case WM_TRAYNOTIFY:
211 /* Right button click pops up the menu */
212 if (lParam == WM_RBUTTONUP) {
213 POINT mouse;
214 /* Get the menu and pop it up */
215 menu = GetSubMenu(mon->m_hmenu, 0);
216 if (!menu) {
217 return 0;
218 }
219
220 /* The first menu item (Status) is the default */
221 SetMenuDefaultItem(menu, 0, TRUE);
222 GetCursorPos(&mouse);
223 SetForegroundWindow(mon->m_nid.hWnd); /* display the menu */
224
225 /* Open the menu at the mouse position */
226 TrackPopupMenu(menu, 0, mouse.x, mouse.y, 0, mon->m_nid.hWnd, NULL);
227
228 /* Left double click brings up status dialog directly */
229 } else if (lParam == WM_LBUTTONDBLCLK) {
230 mon->m_status.show(true);
231 mon->update(bacstat);
232 }
233 return 0;
234
235 case WM_CLOSE:
236 if (isAService()) {
237 mon->sendMessage(NIM_DELETE, 0);
238 }
239 terminate_app(0);
240 break;
241
242 case WM_DESTROY:
243 /* zap everything */
244 PostQuitMessage(0);
245 return 0;
246
247 case WM_QUERYENDSESSION:
248 if (!isAService() || lParam == 0) {
249 PostQuitMessage(0);
250 return TRUE;
251 }
252 return TRUE;
253
254 default:
255 /* Need to redraw tray icon */
256 // if (iMsg == mon->m_tbcreated_msg) {
257 // mon->install();
258 // }
259 break;
260 }
261
262 return DefWindowProc(hwnd, iMsg, wParam, lParam);
263 }
264