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