ReactOS 0.4.15-dev-7998-gdb93cb1
CUserNotification.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2018 Hermes Belusca-Maito
3 *
4 * Pass on icon notification messages to the systray implementation
5 * in the currently running shell.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "precomp.h"
23
24#include <mmsystem.h>
25#undef PlaySound
26
28
29
30/* Use Windows-compatible window callback message */
31#define WM_TRAYNOTIFY (WM_USER + 100)
32
33/* Notification icon ID */
34#define ID_NOTIFY_ICON 0
35
36/* Balloon timers */
37#define ID_BALLOON_TIMEOUT 1
38#define ID_BALLOON_DELAYREMOVE 2
39#define ID_BALLOON_QUERYCONT 3
40#define ID_BALLOON_SHOWTIME 4
41
42#define BALLOON_DELAYREMOVE_TIMEOUT 250 // milliseconds
43
44
46 m_hWorkerWnd(NULL),
47 m_hIcon(NULL),
48 m_dwInfoFlags(0),
49 m_uShowTime(15000),
50 m_uInterval(10000),
51 m_cRetryCount(-1),
52 m_uContinuePoolInterval(0),
53 m_bIsShown(FALSE),
54 m_hRes(S_OK),
55 m_pqc(NULL)
56{
57}
58
60{
61 /* If we have a notification window... */
62 if (m_hWorkerWnd)
63 {
64 /* ... remove the notification icon and destroy the window */
65 RemoveIcon();
68 }
69
70 /* Destroy our local icon copy */
71 if (m_hIcon)
73}
74
76{
77 NOTIFYICONDATAW nid = {0};
78
79 nid.cbSize = NOTIFYICONDATAW_V3_SIZE; // sizeof(nid);
82
83 /* Remove the notification icon */
85}
86
88{
89 /* Set the return value for CUserNotification::Show() and defer icon removal */
90 m_hRes = hRes;
93}
94
96{
97 /*
98 * The balloon timed out, we need to wait before showing it again.
99 * If we retried too many times, delete the notification icon.
100 */
101 if (m_cRetryCount > 0)
102 {
103 /* Decrement the retry count */
105
106 /* Set the timeout interval timer */
108 }
109 else
110 {
111 /* No other retry: delete the notification icon */
113 }
114}
115
117 IN UINT uFlags,
119{
120 pnid->cbSize = NOTIFYICONDATAW_V3_SIZE; // sizeof(nid);
121 pnid->hWnd = m_hWorkerWnd;
122 pnid->uID = ID_NOTIFY_ICON;
123 // pnid->uVersion = NOTIFYICON_VERSION;
124
125 if (uFlags & NIF_MESSAGE)
126 {
127 pnid->uFlags |= NIF_MESSAGE;
128 pnid->uCallbackMessage = WM_TRAYNOTIFY;
129 }
130
131 if (uFlags & NIF_ICON)
132 {
133 pnid->uFlags |= NIF_ICON;
134 /* Use a default icon if we do not have one already */
135 pnid->hIcon = (m_hIcon ? m_hIcon : LoadIcon(NULL, IDI_WINLOGO));
136 }
137
138 if (uFlags & NIF_TIP)
139 {
140 pnid->uFlags |= NIF_TIP;
141 ::StringCchCopyW(pnid->szTip, _countof(pnid->szTip), m_szTip);
142 }
143
144 if (uFlags & NIF_INFO)
145 {
146 pnid->uFlags |= NIF_INFO;
147
148 // pnid->uTimeout = m_uShowTime; // NOTE: Deprecated
149 pnid->dwInfoFlags = m_dwInfoFlags;
150
151 ::StringCchCopyW(pnid->szInfo, _countof(pnid->szInfo), m_szInfo);
152 ::StringCchCopyW(pnid->szInfoTitle, _countof(pnid->szInfoTitle), m_szInfoTitle);
153 }
154}
155
156
157/* IUserNotification Implementation */
158
161 IN LPCWSTR pszTitle,
162 IN LPCWSTR pszText,
163 IN DWORD dwInfoFlags)
164{
165 NOTIFYICONDATAW nid = {0};
166
167 m_szInfo = pszText;
168 m_szInfoTitle = pszTitle;
169 m_dwInfoFlags = dwInfoFlags;
170
171 /* Update the notification icon if we have one */
172 if (!m_hWorkerWnd)
173 return S_OK;
174
175 /* Modify the notification icon */
176 SetUpNotifyData(NIF_INFO, &nid);
178 return S_OK;
179 else
180 return E_FAIL;
181}
182
185 IN DWORD dwShowTime, // Time intervals in milliseconds
186 IN DWORD dwInterval,
187 IN UINT cRetryCount)
188{
189 m_uShowTime = dwShowTime;
190 m_uInterval = dwInterval;
191 m_cRetryCount = cRetryCount;
192 return S_OK;
193}
194
197 IN HICON hIcon,
198 IN LPCWSTR pszToolTip)
199{
200 NOTIFYICONDATAW nid = {0};
201
202 /* Destroy our local icon copy */
203 if (m_hIcon)
205
206 if (hIcon)
207 {
208 /* Copy the icon from the user */
210 }
211 else
212 {
213 /* Use the same icon as the one for the balloon if specified */
214 UINT uIcon = (m_dwInfoFlags & NIIF_ICON_MASK);
215 LPCWSTR pIcon = NULL;
216
217 if (uIcon == NIIF_INFO)
218 pIcon = IDI_INFORMATION;
219 else if (uIcon == NIIF_WARNING)
220 pIcon = IDI_WARNING;
221 else if (uIcon == NIIF_ERROR)
222 pIcon = IDI_ERROR;
223 else if (uIcon == NIIF_USER)
224 pIcon = NULL;
225
226 m_hIcon = (pIcon ? ::LoadIconW(NULL, pIcon) : NULL);
227 }
228
229 m_szTip = pszToolTip;
230
231 /* Update the notification icon if we have one */
232 if (!m_hWorkerWnd)
233 return S_OK;
234
235 /* Modify the notification icon */
238 return S_OK;
239 else
240 return E_FAIL;
241}
242
243
246 IN HWND hWnd,
247 IN UINT uMsg,
250{
251 /* Retrieve the current user notification object stored in the window extra bits */
252 CUserNotification* pThis = reinterpret_cast<CUserNotification*>(::GetWindowLongPtrW(hWnd, 0));
253 ASSERT(pThis);
254 ASSERT(hWnd == pThis->m_hWorkerWnd);
255
256 TRACE("Msg = 0x%x\n", uMsg);
257 switch (uMsg)
258 {
259 /*
260 * We do not receive any WM_(NC)CREATE message since worker windows
261 * are first created using the default window procedure DefWindowProcW.
262 * The window procedure is changed only subsequently to the user one.
263 * We however receive WM_(NC)DESTROY messages.
264 */
265 case WM_DESTROY:
266 {
267 /* Post a WM_QUIT message only if the Show() method's message loop is running */
268 if (pThis->m_bIsShown)
270 return 0;
271 }
272
273 case WM_NCDESTROY:
274 {
276 pThis->m_hWorkerWnd = NULL;
277 return 0;
278 }
279
281 {
282 /*
283 * User session is ending or a shutdown is occurring: perform cleanup.
284 * Set the return value for CUserNotification::Show() and remove the notification.
285 */
287 pThis->RemoveIcon();
289 return TRUE;
290 }
291
292 case WM_TIMER:
293 {
294 TRACE("WM_TIMER(0x%lx)\n", wParam);
295
296 /* Destroy the associated timer */
298
300 {
301 /* Timeout interval timer expired: display the balloon again */
302 NOTIFYICONDATAW nid = {0};
303 pThis->SetUpNotifyData(NIF_INFO, &nid);
305 }
306 else if (wParam == ID_BALLOON_DELAYREMOVE)
307 {
308 /* Delay-remove timer expired: remove the notification */
309 pThis->RemoveIcon();
311 }
312 else if (wParam == ID_BALLOON_QUERYCONT)
313 {
314 /*
315 * Query-continue timer expired: ask the user whether the
316 * notification should continue to be displayed or not.
317 */
318 if (pThis->m_pqc && pThis->m_pqc->QueryContinue() == S_OK)
319 {
320 /* The notification can be displayed */
322 }
323 else
324 {
325 /* The notification should be removed */
326 pThis->DelayRemoveIcon(S_FALSE);
327 }
328 }
329 else if (wParam == ID_BALLOON_SHOWTIME)
330 {
331 /* Show-time timer expired: wait before showing the balloon again */
332 pThis->TimeoutIcon();
333 }
334 return 0;
335 }
336
337 /*
338 * Shell User Notification message.
339 * We use NOTIFYICON_VERSION == 0 or 3 callback version, with:
340 * wParam == identifier of the taskbar icon in which the event occurred;
341 * lParam == holds the mouse or keyboard message associated with the event.
342 */
343 case WM_TRAYNOTIFY:
344 {
345 TRACE("WM_TRAYNOTIFY - wParam = 0x%lx ; lParam = 0x%lx\n", wParam, lParam);
347
348 switch (lParam)
349 {
350 case NIN_BALLOONSHOW:
351 TRACE("NIN_BALLOONSHOW\n");
352 break;
353
354 case NIN_BALLOONHIDE:
355 TRACE("NIN_BALLOONHIDE\n");
356 break;
357
358 /* The balloon timed out, or the user closed it by clicking on the 'X' button */
359 case NIN_BALLOONTIMEOUT:
360 {
361 TRACE("NIN_BALLOONTIMEOUT\n");
362 pThis->TimeoutIcon();
363 break;
364 }
365
366 /* The user clicked on the balloon: delete the notification icon */
367 case NIN_BALLOONUSERCLICK:
368 TRACE("NIN_BALLOONUSERCLICK\n");
369 /* Fall back to icon click behaviour */
370
371 /* The user clicked on the notification icon: delete it */
372 case WM_LBUTTONDOWN:
373 case WM_RBUTTONDOWN:
374 {
375 pThis->DelayRemoveIcon(S_OK);
376 break;
377 }
378
379 default:
380 break;
381 }
382
383 return 0;
384 }
385 }
386
387 return ::DefWindowProcW(hWnd, uMsg, wParam, lParam);
388}
389
390
391// Blocks until the notification times out.
394 IN IQueryContinue* pqc,
395 IN DWORD dwContinuePollInterval)
396{
397 NOTIFYICONDATAW nid = {0};
398 MSG msg;
399
400 /* Create the hidden notification message worker window if we do not have one already */
401 if (!m_hWorkerWnd)
402 {
404 NULL, 0, 0, NULL, (LONG_PTR)this);
405 if (!m_hWorkerWnd)
406 {
408 return E_FAIL;
409 }
410
411 /* Add and display the notification icon */
414 {
417 return E_FAIL;
418 }
419 }
420
421 m_hRes = S_OK;
422
423 /* Set up the user-continue callback mechanism */
424 m_pqc = pqc;
425 if (pqc)
426 {
427 m_uContinuePoolInterval = dwContinuePollInterval;
429 }
430
431 /* Control how long the balloon notification is displayed */
432 if ((nid.uFlags & NIF_INFO) && !*nid.szInfo /* && !*nid.szInfoTitle */)
434
435 /* Dispatch messsages to the worker window */
437 while (::GetMessageW(&msg, NULL, 0, 0))
438 {
441 }
443
444 /* Reset the user-continue callback mechanism */
445 if (pqc)
446 {
449 }
450 m_pqc = NULL;
451
452 /* Return the notification error code */
453 return m_hRes;
454}
455
456#if 0 // IUserNotification2
457// Blocks until the notification times out.
460 IN IQueryContinue* pqc,
461 IN DWORD dwContinuePollInterval,
462 IN IUserNotificationCallback* pSink)
463{
464 return S_OK;
465}
466#endif
467
470 IN LPCWSTR pszSoundName)
471{
472 /* Call the Win32 API - Ignore the PlaySoundW() return value as on Windows */
473 ::PlaySoundW(pszSoundName,
474 NULL,
477 return S_OK;
478}
#define msg(x)
Definition: auth_time.c:54
HWND hWnd
Definition: settings.c:17
#define WINE_DEFAULT_DEBUG_CHANNEL(t)
Definition: precomp.h:23
#define STDMETHODCALLTYPE
Definition: bdasup.h:9
static LRESULT CALLBACK WorkerWndProc(IN HWND hWnd, IN UINT uMsg, IN WPARAM wParam, IN LPARAM lParam)
STDMETHOD() PlaySound(IN LPCWSTR pszSoundName) override
IQueryContinue * m_pqc
STDMETHOD() SetBalloonInfo(IN LPCWSTR pszTitle, IN LPCWSTR pszText, IN DWORD dwInfoFlags) override
VOID DelayRemoveIcon(IN HRESULT hRes)
VOID SetUpNotifyData(IN UINT uFlags, IN OUT PNOTIFYICONDATAW pnid)
STDMETHOD() SetBalloonRetry(IN DWORD dwShowTime, IN DWORD dwInterval, IN UINT cRetryCount) override
STDMETHOD() SetIconInfo(IN HICON hIcon, IN LPCWSTR pszToolTip) override
STDMETHOD() Show(IN IQueryContinue *pqc, IN DWORD dwContinuePollInterval) override
WPARAM wParam
Definition: combotst.c:138
LPARAM lParam
Definition: combotst.c:139
#define E_FAIL
Definition: ddrawi.h:102
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
UINT uFlags
Definition: api.c:59
#define CALLBACK
Definition: compat.h:35
#define FAILED_UNEXPECTEDLY(hr)
Definition: precomp.h:121
#define WM_TRAYNOTIFY
#define ID_BALLOON_TIMEOUT
#define BALLOON_DELAYREMOVE_TIMEOUT
#define ID_BALLOON_DELAYREMOVE
#define ID_BALLOON_QUERYCONT
#define ID_BALLOON_SHOWTIME
#define ID_NOTIFY_ICON
HWND WINAPI SHCreateWorkerWindowW(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle, DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra)
Definition: ordinal.c:3000
unsigned long DWORD
Definition: ntddk_ex.h:95
#define S_OK
Definition: intsafe.h:52
NOTIFYICONDATA nid
Definition: magnifier.c:44
#define SND_ALIAS
Definition: mmsystem.h:160
#define SND_ASYNC
Definition: mmsystem.h:154
#define SND_NOSTOP
Definition: mmsystem.h:158
#define SND_NODEFAULT
Definition: mmsystem.h:155
#define SND_APPLICATION
Definition: mmsystem.h:165
#define ASSERT(a)
Definition: mode.c:44
static HICON
Definition: imagelist.c:84
HICON hIcon
Definition: msconfig.c:44
__int3264 LONG_PTR
Definition: mstsclib_h.h:276
unsigned __int3264 UINT_PTR
Definition: mstsclib_h.h:274
unsigned int UINT
Definition: ndis.h:50
BOOL WINAPI PlaySoundW(LPCWSTR pszSoundW, HMODULE hmod, DWORD fdwSound)
Definition: playsound.c:653
#define NOTIFYICONDATAW_V3_SIZE
Definition: shellapi.h:291
#define NIM_DELETE
Definition: shellapi.h:96
#define NIM_MODIFY
Definition: shellapi.h:95
#define NIF_ICON
Definition: shellapi.h:106
#define NIF_MESSAGE
Definition: shellapi.h:105
#define NIM_ADD
Definition: shellapi.h:94
#define NIF_TIP
Definition: shellapi.h:107
#define _countof(array)
Definition: sndvol32.h:68
#define TRACE(s)
Definition: solgame.cpp:4
STRSAFEAPI StringCchCopyW(STRSAFE_LPWSTR pszDest, size_t cchDest, STRSAFE_LPCWSTR pszSrc)
Definition: strsafe.h:149
CHAR szInfo[256]
Definition: shellapi.h:240
BOOL WINAPI Shell_NotifyIconW(DWORD dwMessage, PNOTIFYICONDATAW pnid)
Definition: systray.cpp:128
TW_UINT32 TW_UINT16 TW_UINT16 MSG
Definition: twain.h:1829
#define IN
Definition: typedefs.h:39
#define OUT
Definition: typedefs.h:40
LONG_PTR LPARAM
Definition: windef.h:208
LONG_PTR LRESULT
Definition: windef.h:209
UINT_PTR WPARAM
Definition: windef.h:207
#define S_FALSE
Definition: winerror.h:2357
#define ERROR_CANCELLED
Definition: winerror.h:726
#define HRESULT_FROM_WIN32(x)
Definition: winerror.h:92
#define GetWindowLongPtrW
Definition: winuser.h:4829
BOOL WINAPI TranslateMessage(_In_ const MSG *)
#define IDI_WARNING
Definition: winuser.h:718
BOOL WINAPI GetMessageW(_Out_ LPMSG, _In_opt_ HWND, _In_ UINT, _In_ UINT)
__analysis_noreturn void WINAPI PostQuitMessage(_In_ int)
HICON WINAPI CopyIcon(_In_ HICON)
Definition: cursoricon.c:2042
#define IDI_ERROR
Definition: winuser.h:719
#define WM_LBUTTONDOWN
Definition: winuser.h:1776
#define IDI_WINLOGO
Definition: winuser.h:709
UINT_PTR WINAPI SetTimer(_In_opt_ HWND, _In_ UINT_PTR, _In_ UINT, _In_opt_ TIMERPROC)
#define IDI_INFORMATION
Definition: winuser.h:720
#define WM_RBUTTONDOWN
Definition: winuser.h:1779
#define WM_TIMER
Definition: winuser.h:1742
#define WM_QUERYENDSESSION
Definition: winuser.h:1622
#define LoadIcon
Definition: winuser.h:5813
#define WM_NCDESTROY
Definition: winuser.h:1684
LRESULT WINAPI DispatchMessageW(_In_ const MSG *)
#define WM_DESTROY
Definition: winuser.h:1609
BOOL WINAPI KillTimer(_In_opt_ HWND, _In_ UINT_PTR)
#define SetWindowLongPtrW
Definition: winuser.h:5346
BOOL WINAPI DestroyWindow(_In_ HWND)
HICON WINAPI LoadIconW(_In_opt_ HINSTANCE hInstance, _In_ LPCWSTR lpIconName)
Definition: cursoricon.c:2106
BOOL WINAPI DestroyIcon(_In_ HICON)
Definition: cursoricon.c:2084
const WCHAR * LPCWSTR
Definition: xmlstorage.h:185