UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / win32 / balloonmgr.cpp
blob938941220d6f3a0f8a0d6ec45b832c90cc12a5ed
1 /*
2 * Copyright (C) 2007 Adam Kropelin
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General
6 * Public License as published by the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the Free
15 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
19 #include "apc.h"
20 #include "balloonmgr.h"
21 #include "resource.h"
23 #define MAX_TIMEOUT 10000
24 #define MIN_TIMEOUT 2000
26 #define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) )
28 BalloonMgr::BalloonMgr()
29 : _exit(false),
30 _active(false)
32 _mutex = CreateMutex(NULL, false, NULL);
33 _event = CreateEvent(NULL, false, false, NULL);
34 _timer = CreateWaitableTimer(NULL, false, NULL);
36 DWORD tid;
37 _thread = CreateThread(NULL, 0, &BalloonMgr::Thread, this, 0, &tid);
40 BalloonMgr::~BalloonMgr()
42 // Request thread exit
43 _exit = true;
44 signal();
46 // Wait for thread exit and force if necessary
47 if (_thread) {
48 if (WaitForSingleObject(_thread, 5000) == WAIT_TIMEOUT)
49 TerminateThread(_thread, 0);
50 CloseHandle(_thread);
53 CloseHandle(_mutex);
54 CloseHandle(_event);
55 CloseHandle(_timer);
58 void BalloonMgr::PostBalloon(HWND hwnd, const char *title, const char *text)
60 lock();
62 Balloon balloon = {hwnd, title, text};
63 _pending.push_back(balloon);
64 signal();
66 unlock();
69 void BalloonMgr::lock()
71 WaitForSingleObject(_mutex, INFINITE);
74 void BalloonMgr::unlock()
76 ReleaseMutex(_mutex);
79 void BalloonMgr::signal()
81 SetEvent(_event);
84 void BalloonMgr::post()
86 if (_pending.empty())
87 return; // No active balloon!?
89 // Post balloon tip
90 Balloon &balloon = _pending.front();
91 NOTIFYICONDATA nid;
92 nid.hWnd = balloon.hwnd;
93 nid.cbSize = sizeof(nid);
94 nid.uID = IDI_APCUPSD;
95 nid.uFlags = NIF_INFO;
96 astrncpy(nid.szInfo, balloon.text.c_str(), sizeof(nid.szInfo));
97 astrncpy(nid.szInfoTitle, balloon.title.c_str(), sizeof(nid.szInfoTitle));
98 nid.uTimeout = MAX_TIMEOUT;
99 nid.dwInfoFlags = NIIF_INFO;
100 Shell_NotifyIcon(NIM_MODIFY, &nid);
102 // Set a timeout to clear the balloon
103 LARGE_INTEGER timeout;
104 if (_pending.size() > 1) // More balloons pending: use minimum timeout
105 timeout.QuadPart = -(MIN_TIMEOUT * 10000);
106 else // No other balloons pending: Use maximum timeout
107 timeout.QuadPart = -(MAX_TIMEOUT * 10000);
108 SetWaitableTimer(_timer, &timeout, 0, NULL, NULL, false);
110 // Remember the time at which we started the timer
111 gettimeofday(&_time, NULL);
114 void BalloonMgr::clear()
116 if (_pending.empty())
117 return; // No active balloon!?
119 // Clear active balloon
120 Balloon &balloon = _pending.front();
121 NOTIFYICONDATA nid;
122 nid.hWnd = balloon.hwnd;
123 nid.cbSize = sizeof(nid);
124 nid.uID = IDI_APCUPSD;
125 nid.uFlags = NIF_INFO;
126 nid.uTimeout = 0;
127 nid.szInfoTitle[0] = '\0';
128 nid.szInfo[0] = '\0';
129 nid.dwInfoFlags = 0;
130 Shell_NotifyIcon(NIM_MODIFY, &nid);
132 // Remove vector entry for active balloon
133 _pending.erase(_pending.begin());
136 DWORD WINAPI BalloonMgr::Thread(LPVOID param)
138 BalloonMgr *_this = (BalloonMgr*)param;
139 HANDLE handles[] = {_this->_event, _this->_timer};
140 LARGE_INTEGER timeout;
141 struct timeval now;
142 DWORD index;
143 long diff;
145 while (1) {
146 // Wait for timeout or new balloon request
147 index = WaitForMultipleObjects(
148 ARRAY_SIZE(handles), handles, false, INFINITE);
150 // Exit if we've been asked to do so
151 if (_this->_exit)
152 break;
154 switch (index) {
155 // New balloon request has arrived
156 case WAIT_OBJECT_0 + 0:
157 _this->lock();
159 if (!_this->_active) {
160 // No balloon active: Post new balloon immediately
161 if (!_this->_pending.empty()) {
162 _this->post();
163 _this->_active = true;
165 } else {
166 // A balloon is active: Shorten timer to minimum
167 CancelWaitableTimer(_this->_timer);
168 gettimeofday(&now, NULL);
169 diff = TV_DIFF_MS(_this->_time, now);
170 if (diff >= MIN_TIMEOUT) {
171 // Min timeout already expired
172 timeout.QuadPart = -1;
173 } else {
174 // Wait enough additional time to meet minimum timeout
175 timeout.QuadPart = -((MIN_TIMEOUT - diff) * 10000);
177 SetWaitableTimer(_this->_timer, &timeout, 0, NULL, NULL, false);
180 _this->unlock();
181 break;
183 // Timeout ocurred
184 case WAIT_OBJECT_0 + 1:
185 _this->lock();
187 // Clear active balloon
188 _this->clear();
190 // Post next balloon if there is one
191 if (!_this->_pending.empty()) {
192 _this->post();
193 _this->_active = true;
194 } else {
195 _this->_active = false;
198 _this->unlock();
199 break;
201 default:
202 // Should never happen...but if it does, sleep a bit to prevent
203 // spinning.
204 Sleep(1000);
205 break;