1 /*
2 * barrier -- mouse and keyboard sharing utility
3 * Copyright (C) 2012-2016 Symless Ltd.
4 * Copyright (C) 2003 Chris Schoeneman
5 *
6 * This package is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * found in the file LICENSE that should have accompanied this file.
9 *
10 * This package 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.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "MSWindowsServerTaskBarReceiver.h"
20
21 #include "resource.h"
22 #include "server/Server.h"
23 #include "platform/MSWindowsClipboard.h"
24 #include "platform/MSWindowsScreen.h"
25 #include "arch/win32/ArchTaskBarWindows.h"
26 #include "arch/win32/ArchMiscWindows.h"
27 #include "arch/Arch.h"
28 #include "base/EventQueue.h"
29 #include "base/IEventQueue.h"
30 #include "base/log_outputters.h"
31 #include "base/EventTypes.h"
32
33 //
34 // MSWindowsServerTaskBarReceiver
35 //
36
37 const UINT MSWindowsServerTaskBarReceiver::s_stateToIconID[kMaxState] =
38 {
39 IDI_TASKBAR_NOT_RUNNING,
40 IDI_TASKBAR_NOT_WORKING,
41 IDI_TASKBAR_NOT_CONNECTED,
42 IDI_TASKBAR_CONNECTED
43 };
44
MSWindowsServerTaskBarReceiver(HINSTANCE appInstance,const BufferedLogOutputter * logBuffer,IEventQueue * events)45 MSWindowsServerTaskBarReceiver::MSWindowsServerTaskBarReceiver(
46 HINSTANCE appInstance, const BufferedLogOutputter* logBuffer, IEventQueue* events) :
47 ServerTaskBarReceiver(events),
48 m_events(events),
49 m_appInstance(appInstance),
50 m_window(NULL),
51 m_logBuffer(logBuffer)
52 {
53 for (UInt32 i = 0; i < kMaxState; ++i) {
54 m_icon[i] = loadIcon(s_stateToIconID[i]);
55 }
56 m_menu = LoadMenu(m_appInstance, MAKEINTRESOURCE(IDR_TASKBAR));
57
58 // don't create the window yet. we'll create it on demand. this
59 // has the side benefit of being created in the thread used for
60 // the task bar. that's good because it means the existence of
61 // the window won't prevent changing the main thread's desktop.
62
63 // add ourself to the task bar
64 ARCH->addReceiver(this);
65 }
66
67 void
cleanup()68 MSWindowsServerTaskBarReceiver::cleanup()
69 {
70 ARCH->removeReceiver(this);
71 for (UInt32 i = 0; i < kMaxState; ++i) {
72 deleteIcon(m_icon[i]);
73 }
74 DestroyMenu(m_menu);
75 destroyWindow();
76 }
77
~MSWindowsServerTaskBarReceiver()78 MSWindowsServerTaskBarReceiver::~MSWindowsServerTaskBarReceiver()
79 {
80 cleanup();
81 }
82
83 void
showStatus()84 MSWindowsServerTaskBarReceiver::showStatus()
85 {
86 // create the window
87 createWindow();
88
89 // lock self while getting status
90 lock();
91
92 // get the current status
93 std::string status = getToolTip();
94
95 // get the connect clients, if any
96 const Clients& clients = getClients();
97
98 // done getting status
99 unlock();
100
101 // update dialog
102 HWND child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_STATUS);
103 SendMessage(child, WM_SETTEXT, 0, (LPARAM)status.c_str());
104 child = GetDlgItem(m_window, IDC_TASKBAR_STATUS_CLIENTS);
105 SendMessage(child, LB_RESETCONTENT, 0, 0);
106 for (Clients::const_iterator index = clients.begin();
107 index != clients.end(); ) {
108 const char* client = index->c_str();
109 if (++index == clients.end()) {
110 SendMessage(child, LB_ADDSTRING, 0, (LPARAM)client);
111 }
112 else {
113 SendMessage(child, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)client);
114 }
115 }
116
117 if (!IsWindowVisible(m_window)) {
118 // position it by the mouse
119 POINT cursorPos;
120 GetCursorPos(&cursorPos);
121 RECT windowRect;
122 GetWindowRect(m_window, &windowRect);
123 int x = cursorPos.x;
124 int y = cursorPos.y;
125 int fw = GetSystemMetrics(SM_CXDLGFRAME);
126 int fh = GetSystemMetrics(SM_CYDLGFRAME);
127 int ww = windowRect.right - windowRect.left;
128 int wh = windowRect.bottom - windowRect.top;
129 int sw = GetSystemMetrics(SM_CXFULLSCREEN);
130 int sh = GetSystemMetrics(SM_CYFULLSCREEN);
131 if (fw < 1) {
132 fw = 1;
133 }
134 if (fh < 1) {
135 fh = 1;
136 }
137 if (x + ww - fw > sw) {
138 x -= ww - fw;
139 }
140 else {
141 x -= fw;
142 }
143 if (x < 0) {
144 x = 0;
145 }
146 if (y + wh - fh > sh) {
147 y -= wh - fh;
148 }
149 else {
150 y -= fh;
151 }
152 if (y < 0) {
153 y = 0;
154 }
155 SetWindowPos(m_window, HWND_TOPMOST, x, y, ww, wh,
156 SWP_SHOWWINDOW);
157 }
158 }
159
160 void
runMenu(int x,int y)161 MSWindowsServerTaskBarReceiver::runMenu(int x, int y)
162 {
163 // do popup menu. we need a window to pass to TrackPopupMenu().
164 // the SetForegroundWindow() and SendMessage() calls around
165 // TrackPopupMenu() are to get the menu to be dismissed when
166 // another window gets activated and are just one of those
167 // win32 weirdnesses.
168 createWindow();
169 SetForegroundWindow(m_window);
170 HMENU menu = GetSubMenu(m_menu, 0);
171 SetMenuDefaultItem(menu, IDC_TASKBAR_STATUS, FALSE);
172 HMENU logLevelMenu = GetSubMenu(menu, 3);
173 CheckMenuRadioItem(logLevelMenu, 0, 6,
174 CLOG->getFilter() - kERROR, MF_BYPOSITION);
175 int n = TrackPopupMenu(menu,
176 TPM_NONOTIFY |
177 TPM_RETURNCMD |
178 TPM_LEFTBUTTON |
179 TPM_RIGHTBUTTON,
180 x, y, 0, m_window, NULL);
181 SendMessage(m_window, WM_NULL, 0, 0);
182
183 // perform the requested operation
184 switch (n) {
185 case IDC_TASKBAR_STATUS:
186 showStatus();
187 break;
188
189 case IDC_TASKBAR_LOG:
190 copyLog();
191 break;
192
193 case IDC_TASKBAR_SHOW_LOG:
194 ARCH->showConsole(true);
195 break;
196
197 case IDC_RELOAD_CONFIG:
198 m_events->addEvent(Event(m_events->forServerApp().reloadConfig(),
199 m_events->getSystemTarget()));
200 break;
201
202 case IDC_FORCE_RECONNECT:
203 m_events->addEvent(Event(m_events->forServerApp().forceReconnect(),
204 m_events->getSystemTarget()));
205 break;
206
207 case ID_BARRIER_RESETSERVER:
208 m_events->addEvent(Event(m_events->forServerApp().resetServer(),
209 m_events->getSystemTarget()));
210 break;
211
212 case IDC_TASKBAR_LOG_LEVEL_ERROR:
213 CLOG->setFilter(kERROR);
214 break;
215
216 case IDC_TASKBAR_LOG_LEVEL_WARNING:
217 CLOG->setFilter(kWARNING);
218 break;
219
220 case IDC_TASKBAR_LOG_LEVEL_NOTE:
221 CLOG->setFilter(kNOTE);
222 break;
223
224 case IDC_TASKBAR_LOG_LEVEL_INFO:
225 CLOG->setFilter(kINFO);
226 break;
227
228 case IDC_TASKBAR_LOG_LEVEL_DEBUG:
229 CLOG->setFilter(kDEBUG);
230 break;
231
232 case IDC_TASKBAR_LOG_LEVEL_DEBUG1:
233 CLOG->setFilter(kDEBUG1);
234 break;
235
236 case IDC_TASKBAR_LOG_LEVEL_DEBUG2:
237 CLOG->setFilter(kDEBUG2);
238 break;
239
240 case IDC_TASKBAR_QUIT:
241 quit();
242 break;
243 }
244 }
245
246 void
primaryAction()247 MSWindowsServerTaskBarReceiver::primaryAction()
248 {
249 showStatus();
250 }
251
252 const IArchTaskBarReceiver::Icon
getIcon() const253 MSWindowsServerTaskBarReceiver::getIcon() const
254 {
255 return static_cast<Icon>(m_icon[getStatus()]);
256 }
257
258 void
copyLog() const259 MSWindowsServerTaskBarReceiver::copyLog() const
260 {
261 if (m_logBuffer != NULL) {
262 // collect log buffer
263 String data;
264 for (BufferedLogOutputter::const_iterator index = m_logBuffer->begin();
265 index != m_logBuffer->end(); ++index) {
266 data += *index;
267 data += "\n";
268 }
269
270 // copy log to clipboard
271 if (!data.empty()) {
272 MSWindowsClipboard clipboard(m_window);
273 clipboard.open(0);
274 clipboard.emptyUnowned();
275 clipboard.add(IClipboard::kText, data);
276 clipboard.close();
277 }
278 }
279 }
280
281 void
onStatusChanged()282 MSWindowsServerTaskBarReceiver::onStatusChanged()
283 {
284 if (IsWindowVisible(m_window)) {
285 showStatus();
286 }
287 }
288
289 HICON
loadIcon(UINT id)290 MSWindowsServerTaskBarReceiver::loadIcon(UINT id)
291 {
292 HANDLE icon = LoadImage(m_appInstance,
293 MAKEINTRESOURCE(id),
294 IMAGE_ICON,
295 0, 0,
296 LR_DEFAULTCOLOR);
297 return static_cast<HICON>(icon);
298 }
299
300 void
deleteIcon(HICON icon)301 MSWindowsServerTaskBarReceiver::deleteIcon(HICON icon)
302 {
303 if (icon != NULL) {
304 DestroyIcon(icon);
305 }
306 }
307
308 void
createWindow()309 MSWindowsServerTaskBarReceiver::createWindow()
310 {
311 // ignore if already created
312 if (m_window != NULL) {
313 return;
314 }
315
316 // get the status dialog
317 m_window = CreateDialogParam(m_appInstance,
318 MAKEINTRESOURCE(IDD_TASKBAR_STATUS),
319 NULL,
320 (DLGPROC)&MSWindowsServerTaskBarReceiver::staticDlgProc,
321 reinterpret_cast<LPARAM>(
322 static_cast<void*>(this)));
323
324 // window should appear on top of everything, including (especially)
325 // the task bar.
326 LONG_PTR style = GetWindowLongPtr(m_window, GWL_EXSTYLE);
327 style |= WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
328 SetWindowLongPtr(m_window, GWL_EXSTYLE, style);
329
330 // tell the task bar about this dialog
331 ArchTaskBarWindows::addDialog(m_window);
332 }
333
334 void
destroyWindow()335 MSWindowsServerTaskBarReceiver::destroyWindow()
336 {
337 if (m_window != NULL) {
338 ArchTaskBarWindows::removeDialog(m_window);
339 DestroyWindow(m_window);
340 m_window = NULL;
341 }
342 }
343
344 BOOL
dlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM)345 MSWindowsServerTaskBarReceiver::dlgProc(HWND hwnd,
346 UINT msg, WPARAM wParam, LPARAM)
347 {
348 switch (msg) {
349 case WM_INITDIALOG:
350 // use default focus
351 return TRUE;
352
353 case WM_ACTIVATE:
354 // hide when another window is activated
355 if (LOWORD(wParam) == WA_INACTIVE) {
356 ShowWindow(hwnd, SW_HIDE);
357 }
358 break;
359 }
360 return FALSE;
361 }
362
363 BOOL CALLBACK
staticDlgProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)364 MSWindowsServerTaskBarReceiver::staticDlgProc(HWND hwnd,
365 UINT msg, WPARAM wParam, LPARAM lParam)
366 {
367 // if msg is WM_INITDIALOG, extract the MSWindowsServerTaskBarReceiver*
368 // and put it in the extra window data then forward the call.
369 MSWindowsServerTaskBarReceiver* self = NULL;
370 if (msg == WM_INITDIALOG) {
371 self = static_cast<MSWindowsServerTaskBarReceiver*>(
372 reinterpret_cast<void*>(lParam));
373 SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
374 }
375 else {
376 // get the extra window data and forward the call
377 LONG_PTR data = GetWindowLongPtr(hwnd, GWLP_USERDATA);
378 if (data != 0) {
379 self = static_cast<MSWindowsServerTaskBarReceiver*>(
380 reinterpret_cast<void*>(data));
381 }
382 }
383
384 // forward the message
385 if (self != NULL) {
386 return self->dlgProc(hwnd, msg, wParam, lParam);
387 }
388 else {
389 return (msg == WM_INITDIALOG) ? TRUE : FALSE;
390 }
391 }
392
393 IArchTaskBarReceiver*
createTaskBarReceiver(const BufferedLogOutputter * logBuffer,IEventQueue * events)394 createTaskBarReceiver(const BufferedLogOutputter* logBuffer, IEventQueue* events)
395 {
396 ArchMiscWindows::setIcons(
397 (HICON)LoadImage(ArchMiscWindows::instanceWin32(),
398 MAKEINTRESOURCE(IDI_BARRIER),
399 IMAGE_ICON,
400 32, 32, LR_SHARED),
401 (HICON)LoadImage(ArchMiscWindows::instanceWin32(),
402 MAKEINTRESOURCE(IDI_BARRIER),
403 IMAGE_ICON,
404 16, 16, LR_SHARED));
405
406 return new MSWindowsServerTaskBarReceiver(
407 MSWindowsScreen::getWindowInstance(), logBuffer, events);
408 }
409