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,
20 #include "balloonmgr.h"
23 #define MAX_TIMEOUT 10000
24 #define MIN_TIMEOUT 2000
26 #define ARRAY_SIZE(x) ( sizeof(x) / sizeof((x)[0]) )
28 BalloonMgr::BalloonMgr()
32 _mutex
= CreateMutex(NULL
, false, NULL
);
33 _event
= CreateEvent(NULL
, false, false, NULL
);
34 _timer
= CreateWaitableTimer(NULL
, false, NULL
);
37 _thread
= CreateThread(NULL
, 0, &BalloonMgr::Thread
, this, 0, &tid
);
40 BalloonMgr::~BalloonMgr()
42 // Request thread exit
46 // Wait for thread exit and force if necessary
48 if (WaitForSingleObject(_thread
, 5000) == WAIT_TIMEOUT
)
49 TerminateThread(_thread
, 0);
58 void BalloonMgr::PostBalloon(HWND hwnd
, const char *title
, const char *text
)
62 Balloon balloon
= {hwnd
, title
, text
};
63 _pending
.push_back(balloon
);
69 void BalloonMgr::lock()
71 WaitForSingleObject(_mutex
, INFINITE
);
74 void BalloonMgr::unlock()
79 void BalloonMgr::signal()
84 void BalloonMgr::post()
87 return; // No active balloon!?
90 Balloon
&balloon
= _pending
.front();
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();
122 nid
.hWnd
= balloon
.hwnd
;
123 nid
.cbSize
= sizeof(nid
);
124 nid
.uID
= IDI_APCUPSD
;
125 nid
.uFlags
= NIF_INFO
;
127 nid
.szInfoTitle
[0] = '\0';
128 nid
.szInfo
[0] = '\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
;
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
155 // New balloon request has arrived
156 case WAIT_OBJECT_0
+ 0:
159 if (!_this
->_active
) {
160 // No balloon active: Post new balloon immediately
161 if (!_this
->_pending
.empty()) {
163 _this
->_active
= true;
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;
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);
184 case WAIT_OBJECT_0
+ 1:
187 // Clear active balloon
190 // Post next balloon if there is one
191 if (!_this
->_pending
.empty()) {
193 _this
->_active
= true;
195 _this
->_active
= false;
202 // Should never happen...but if it does, sleep a bit to prevent