1 // Copyright (c) 2014 GitHub, Inc.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
5 #include "atom/browser/ui/win/notify_icon_host.h"
10 #include "atom/browser/ui/win/notify_icon.h"
11 #include "base/bind.h"
12 #include "base/stl_util.h"
13 #include "base/threading/non_thread_safe.h"
14 #include "base/threading/thread.h"
15 #include "base/win/win_util.h"
16 #include "base/win/wrapped_window_proc.h"
17 #include "ui/events/event_constants.h"
18 #include "ui/events/win/system_event_state_lookup.h"
19 #include "ui/gfx/win/hwnd_util.h"
25 const UINT kNotifyIconMessage = WM_APP + 1;
27 // |kBaseIconId| is 2 to avoid conflicts with plugins that hard-code id 1.
28 const UINT kBaseIconId = 2;
30 const wchar_t kNotifyIconHostWindowClass[] = L"Electron_NotifyIconHostWindow";
33 return ((::GetKeyState(VK_LWIN) & 0x8000) == 0x8000) ||
34 ((::GetKeyState(VK_RWIN) & 0x8000) == 0x8000);
37 int GetKeyboardModifers() {
38 int modifiers = ui::EF_NONE;
39 if (ui::win::IsShiftPressed())
40 modifiers |= ui::EF_SHIFT_DOWN;
41 if (ui::win::IsCtrlPressed())
42 modifiers |= ui::EF_CONTROL_DOWN;
43 if (ui::win::IsAltPressed())
44 modifiers |= ui::EF_ALT_DOWN;
46 modifiers |= ui::EF_COMMAND_DOWN;
52 NotifyIconHost::NotifyIconHost()
57 // Register our window class
58 WNDCLASSEX window_class;
59 base::win::InitializeWindowClass(
60 kNotifyIconHostWindowClass,
61 &base::win::WrappedWindowProc<NotifyIconHost::WndProcStatic>,
62 0, 0, 0, NULL, NULL, NULL, NULL, NULL,
64 instance_ = window_class.hInstance;
65 atom_ = RegisterClassEx(&window_class);
68 // If the taskbar is re-created after we start up, we have to rebuild all of
70 taskbar_created_message_ = RegisterWindowMessage(TEXT("TaskbarCreated"));
72 // Create an offscreen window for handling messages for the status icons. We
73 // create a hidden WS_POPUP window instead of an HWND_MESSAGE window, because
74 // only top-level windows such as popups can receive broadcast messages like
76 window_ = CreateWindow(MAKEINTATOM(atom_),
77 0, WS_POPUP, 0, 0, 0, 0, 0, 0, instance_, 0);
78 gfx::CheckWindowCreated(window_);
79 gfx::SetWindowUserData(window_, this);
82 NotifyIconHost::~NotifyIconHost() {
84 DestroyWindow(window_);
87 UnregisterClass(MAKEINTATOM(atom_), instance_);
89 for (NotifyIcon* ptr : notify_icons_)
93 NotifyIcon* NotifyIconHost::CreateNotifyIcon() {
94 NotifyIcon* notify_icon =
95 new NotifyIcon(this, NextIconId(), window_, kNotifyIconMessage);
96 notify_icons_.push_back(notify_icon);
100 void NotifyIconHost::Remove(NotifyIcon* icon) {
101 NotifyIcons::iterator i(
102 std::find(notify_icons_.begin(), notify_icons_.end(), icon));
104 if (i == notify_icons_.end()) {
109 notify_icons_.erase(i);
112 LRESULT CALLBACK NotifyIconHost::WndProcStatic(HWND hwnd,
116 NotifyIconHost* msg_wnd = reinterpret_cast<NotifyIconHost*>(
117 GetWindowLongPtr(hwnd, GWLP_USERDATA));
119 return msg_wnd->WndProc(hwnd, message, wparam, lparam);
121 return ::DefWindowProc(hwnd, message, wparam, lparam);
124 LRESULT CALLBACK NotifyIconHost::WndProc(HWND hwnd,
128 if (message == taskbar_created_message_) {
129 // We need to reset all of our icons because the taskbar went away.
130 for (NotifyIcons::const_iterator i(notify_icons_.begin());
131 i != notify_icons_.end(); ++i) {
132 NotifyIcon* win_icon = static_cast<NotifyIcon*>(*i);
133 win_icon->ResetIcon();
136 } else if (message == kNotifyIconMessage) {
137 NotifyIcon* win_icon = NULL;
139 // Find the selected status icon.
140 for (NotifyIcons::const_iterator i(notify_icons_.begin());
141 i != notify_icons_.end(); ++i) {
142 NotifyIcon* current_win_icon = static_cast<NotifyIcon*>(*i);
143 if (current_win_icon->icon_id() == wparam) {
144 win_icon = current_win_icon;
149 // It is possible for this procedure to be called with an obsolete icon
150 // id. In that case we should just return early before handling any
157 win_icon->NotifyBalloonShow();
160 case TB_INDETERMINATE:
161 win_icon->NotifyBalloonClicked();
165 win_icon->NotifyBalloonClosed();
170 case WM_LBUTTONDBLCLK:
171 case WM_RBUTTONDBLCLK:
173 // Walk our icons, find which one was clicked on, and invoke its
174 // HandleClickEvent() method.
175 win_icon->HandleClickEvent(
176 GetKeyboardModifers(),
177 (lparam == WM_LBUTTONDOWN || lparam == WM_LBUTTONDBLCLK),
178 (lparam == WM_LBUTTONDBLCLK || lparam == WM_RBUTTONDBLCLK));
182 return ::DefWindowProc(hwnd, message, wparam, lparam);
185 UINT NotifyIconHost::NextIconId() {
186 UINT icon_id = next_icon_id_++;
187 return kBaseIconId + icon_id;