1 // Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
3 // This file was part of the vnc system.
5 // The vnc system is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 // If the source code for the vnc system is not available from the place
21 // whence you received this file, check http://www.uk.research.att.com/vnc or contact
22 // the authors on vnc@uk.research.att.com for information on obtaining it.
24 // This file has been adapted to the Win32 version of Bacula
25 // by Kern E. Sibbald. Many thanks to ATT and James Weatherall,
26 // the original author, for providing an excellent template.
28 // Copyright (2000-2003) Kern E. Sibbald
35 // Implementation of a system tray icon & menu for Bacula
37 #include "winbacula.h"
38 #include "winservice.h"
46 #ifdef properties_implemented
47 const UINT MENU_PROPERTIES_SHOW = RegisterWindowMessage("Bacula.Properties.User.Show");
48 const UINT MENU_DEFAULT_PROPERTIES_SHOW = RegisterWindowMessage("Bacula.Properties.Default.Show");
50 const UINT MENU_ABOUTBOX_SHOW = RegisterWindowMessage("Bacula.AboutBox.Show");
51 const UINT MENU_STATUS_SHOW = RegisterWindowMessage("Bacula.Status.Show");
52 const UINT MENU_EVENTS_SHOW = RegisterWindowMessage("Bacula.Events.Show");
53 const UINT MENU_SERVICEHELPER_MSG = RegisterWindowMessage("Bacula.ServiceHelper.Message");
54 const UINT MENU_ADD_CLIENT_MSG = RegisterWindowMessage("Bacula.AddClient.Message");
55 const char *MENU_CLASS_NAME = "Bacula Tray Icon";
57 extern void terminate_filed(int sig);
58 extern char *bac_status(int stat);
65 // Create a dummy window to handle tray icon messages
68 wndclass.cbSize = sizeof(wndclass);
70 wndclass.lpfnWndProc = bacMenu::WndProc;
71 wndclass.cbClsExtra = 0;
72 wndclass.cbWndExtra = 0;
73 wndclass.hInstance = hAppInstance;
74 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
75 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
76 wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
77 wndclass.lpszMenuName = (const char *) NULL;
78 wndclass.lpszClassName = MENU_CLASS_NAME;
79 wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
81 RegisterClassEx(&wndclass);
83 m_hwnd = CreateWindow(MENU_CLASS_NAME,
98 // record which client created this window
99 SetWindowLong(m_hwnd, GWL_USERDATA, (LONG) this);
101 // Timer to trigger icon updating
102 SetTimer(m_hwnd, 1, 5000, NULL);
104 // Load the icons for the tray
105 m_idle_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_IDLE));
106 m_running_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_RUNNING));
107 m_error_icon = LoadIcon(hAppInstance, MAKEINTRESOURCE(IDI_JOB_ERROR));
109 // Load the popup menu
110 m_hmenu = LoadMenu(hAppInstance, MAKEINTRESOURCE(IDR_TRAYMENU));
112 // Install the tray icon!
118 // Remove the tray icon
119 SendTrayMsg(NIM_DELETE, 0);
121 // Destroy the loaded menu
123 DestroyMenu(m_hmenu);
127 bacMenu::AddTrayIcon()
129 SendTrayMsg(NIM_ADD, bacstat);
133 bacMenu::DelTrayIcon()
135 SendTrayMsg(NIM_DELETE, 0);
140 bacMenu::UpdateTrayIcon(int bacstat)
142 (void *)bac_status(0);
143 SendTrayMsg(NIM_MODIFY, bacstat);
147 bacMenu::SendTrayMsg(DWORD msg, int bacstat)
149 // Create the tray icon message
151 m_nid.cbSize = sizeof(m_nid);
152 m_nid.uID = IDI_BACULA; // never changes after construction
154 m_nid.hIcon = m_idle_icon;
155 else if (bacstat == 1)
156 m_nid.hIcon = m_running_icon;
157 else if (bacstat < 0)
158 m_nid.hIcon = m_error_icon;
160 m_nid.uFlags = NIF_ICON | NIF_MESSAGE;
161 m_nid.uCallbackMessage = WM_TRAYNOTIFY;
164 // Use resource string as tip if there is one
165 if (LoadString(hAppInstance, IDI_BACULA, m_nid.szTip, sizeof(m_nid.szTip))) {
166 m_nid.uFlags |= NIF_TIP;
169 // Try to add the Bacula status to the tip string, if possible
170 if (m_nid.uFlags & NIF_TIP) {
171 strncpy(m_nid.szTip, bac_status(0), (sizeof(m_nid.szTip)-1));
175 if (Shell_NotifyIcon(msg, &m_nid)) {
176 EnableMenuItem(m_hmenu, ID_CLOSE, MF_ENABLED);
178 if (!bacService::RunningAsService()) {
179 if (msg == NIM_ADD) {
180 // The tray icon couldn't be created, so use the Properties dialog
181 // as the main program window
188 // Process window messages
189 LRESULT CALLBACK bacMenu::WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
191 // This is a static method, so we don't know which instantiation we're
192 // dealing with. We use Allen Hadden's (ahadden@taratec.com) suggestion
193 // from a newsgroup to get the pseudo-this.
194 bacMenu *_this = (bacMenu *) GetWindowLong(hwnd, GWL_USERDATA);
198 // Every five seconds, a timer message causes the icon to update
200 // *** HACK for running servicified
201 if (bacService::RunningAsService()) {
202 // Attempt to add the icon if it's not already there
203 _this->AddTrayIcon();
204 // Trigger a check of the current user
205 // PostMessage(hwnd, WM_USERCHANGED, 0, 0);
209 _this->UpdateTrayIcon(bacstat);
214 // DEAL WITH NOTIFICATIONS FROM THE SERVER:
215 case WM_SRV_CLIENT_AUTHENTICATED:
216 case WM_SRV_CLIENT_DISCONNECT:
217 // Adjust the icon accordingly
218 _this->UpdateTrayIcon(bacstat);
222 // STANDARD MESSAGE HANDLING
227 // User has clicked an item on the tray menu
228 switch (LOWORD(wParam)) {
230 // Show the status dialog
231 _this->m_status.Show(TRUE);
232 _this->UpdateTrayIcon(bacstat);
236 // Show the Events dialog
237 _this->m_events.Show(TRUE);
238 _this->UpdateTrayIcon(bacstat);
243 // Disconnect all currently connected clients
247 // Show the About box
248 _this->m_about.Show(TRUE);
252 // User selected Close from the tray menu
253 PostMessage(hwnd, WM_CLOSE, 0, 0);
260 // User has clicked on the tray icon or the menu
262 // Get the submenu to use as a pop-up menu
263 HMENU submenu = GetSubMenu(_this->m_hmenu, 0);
265 // What event are we responding to, RMB click?
266 if (lParam==WM_RBUTTONUP) {
267 if (submenu == NULL) {
271 // Make the first menu item the default (bold font)
272 SetMenuDefaultItem(submenu, 0, TRUE);
274 // Get the current cursor position, to display the menu at
276 GetCursorPos(&mouse);
279 // (Microsoft calls it a feature) in Windows 95 that requires calling
280 // SetForegroundWindow. To find out more, search for Q135788 in MSDN.
282 SetForegroundWindow(_this->m_nid.hWnd);
284 // Display the menu at the desired position
285 TrackPopupMenu(submenu,
286 0, mouse.x, mouse.y, 0,
287 _this->m_nid.hWnd, NULL);
292 // Or was there a LMB double click?
293 if (lParam==WM_LBUTTONDBLCLK) {
294 // double click: execute first menu item
295 SendMessage(_this->m_nid.hWnd,
297 GetMenuItemID(submenu, 0),
309 // The user wants Bacula to quit cleanly...
313 case WM_QUERYENDSESSION:
314 // Are we running as a system service?
315 // Or is the system shutting down (in which case we should check anyway!)
316 if ((!bacService::RunningAsService()) || (lParam == 0)) {
317 // No, so we are about to be killed
319 // If there are remote connections then we should verify
320 // that the user is happy about killing them.
322 // Finally, post a quit message, just in case
327 // Tell the OS that we've handled it anyway
328 // PostQuitMessage(0);
333 if (iMsg == MENU_ABOUTBOX_SHOW) {
334 // External request to show our About dialog
335 PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_ABOUT, 0), 0);
338 if (iMsg == MENU_STATUS_SHOW) {
339 // External request to show our status
340 PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_STATUS, 0), 0);
345 if (iMsg == MENU_EVENTS_SHOW) {
346 // External request to show our Events dialogue
347 PostMessage(hwnd, WM_COMMAND, MAKELONG(ID_EVENTS, 0), 0);
351 if (iMsg == MENU_SERVICEHELPER_MSG) {
352 // External ServiceHelper message.
353 // This message holds a process id which we can use to
354 // impersonate a specific user. In doing so, we can load their
355 // preferences correctly
356 bacService::ProcessUserHelperMessage(wParam, lParam);
358 // - Trigger a check of the current user
359 PostMessage(hwnd, WM_USERCHANGED, 0, 0);
362 if (iMsg == MENU_ADD_CLIENT_MSG) {
363 // Add Client message. This message includes an IP address
364 // of a listening client, to which we should connect.
371 // Message not recognised
372 return DefWindowProc(hwnd, iMsg, wParam, lParam);