source: vendor/synergy/current/lib/arch/CArchTaskBarWindows.cpp

Last change on this file was 2749, checked in by bird, 19 years ago

synergy v1.3.1 sources (zip).

File size: 11.6 KB
Line 
1/*
2 * synergy -- mouse and keyboard sharing utility
3 * Copyright (C) 2003 Chris Schoeneman
4 *
5 * This package is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * found in the file COPYING that should have accompanied this file.
8 *
9 * This package is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include "CArchTaskBarWindows.h"
16#include "CArchMiscWindows.h"
17#include "IArchTaskBarReceiver.h"
18#include "CArch.h"
19#include "XArch.h"
20#include <string.h>
21#include <shellapi.h>
22
23static const UINT kAddReceiver = WM_USER + 10;
24static const UINT kRemoveReceiver = WM_USER + 11;
25static const UINT kUpdateReceiver = WM_USER + 12;
26static const UINT kNotifyReceiver = WM_USER + 13;
27static const UINT kFirstReceiverID = WM_USER + 14;
28
29//
30// CArchTaskBarWindows
31//
32
33CArchTaskBarWindows* CArchTaskBarWindows::s_instance = NULL;
34HINSTANCE CArchTaskBarWindows::s_appInstance = NULL;
35
36CArchTaskBarWindows::CArchTaskBarWindows(void* appInstance) :
37 m_nextID(kFirstReceiverID)
38{
39 // save the singleton instance
40 s_instance = this;
41
42 // save app instance
43 s_appInstance = reinterpret_cast<HINSTANCE>(appInstance);
44
45 // we need a mutex
46 m_mutex = ARCH->newMutex();
47
48 // and a condition variable which uses the above mutex
49 m_ready = false;
50 m_condVar = ARCH->newCondVar();
51
52 // we're going to want to get a result from the thread we're
53 // about to create to know if it initialized successfully.
54 // so we lock the condition variable.
55 ARCH->lockMutex(m_mutex);
56
57 // open a window and run an event loop in a separate thread.
58 // this has to happen in a separate thread because if we
59 // create a window on the current desktop with the current
60 // thread then the current thread won't be able to switch
61 // desktops if it needs to.
62 m_thread = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this);
63
64 // wait for child thread
65 while (!m_ready) {
66 ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
67 }
68
69 // ready
70 ARCH->unlockMutex(m_mutex);
71}
72
73CArchTaskBarWindows::~CArchTaskBarWindows()
74{
75 if (m_thread != NULL) {
76 PostMessage(m_hwnd, WM_QUIT, 0, 0);
77 ARCH->wait(m_thread, -1.0);
78 ARCH->closeThread(m_thread);
79 }
80 ARCH->closeCondVar(m_condVar);
81 ARCH->closeMutex(m_mutex);
82 s_instance = NULL;
83}
84
85void
86CArchTaskBarWindows::addDialog(HWND hwnd)
87{
88 CArchMiscWindows::addDialog(hwnd);
89}
90
91void
92CArchTaskBarWindows::removeDialog(HWND hwnd)
93{
94 CArchMiscWindows::removeDialog(hwnd);
95}
96
97void
98CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
99{
100 // ignore bogus receiver
101 if (receiver == NULL) {
102 return;
103 }
104
105 // add receiver if necessary
106 CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
107 if (index == m_receivers.end()) {
108 // add it, creating a new message ID for it
109 CReceiverInfo info;
110 info.m_id = getNextID();
111 index = m_receivers.insert(std::make_pair(receiver, info)).first;
112
113 // add ID to receiver mapping
114 m_idTable.insert(std::make_pair(info.m_id, index));
115 }
116
117 // add receiver
118 PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0);
119}
120
121void
122CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
123{
124 // find receiver
125 CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
126 if (index == m_receivers.end()) {
127 return;
128 }
129
130 // remove icon. wait for this to finish before returning.
131 SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0);
132
133 // recycle the ID
134 recycleID(index->second.m_id);
135
136 // discard
137 m_idTable.erase(index->second.m_id);
138 m_receivers.erase(index);
139}
140
141void
142CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
143{
144 // find receiver
145 CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver);
146 if (index == m_receivers.end()) {
147 return;
148 }
149
150 // update icon and tool tip
151 PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0);
152}
153
154UINT
155CArchTaskBarWindows::getNextID()
156{
157 if (m_oldIDs.empty()) {
158 return m_nextID++;
159 }
160 UINT id = m_oldIDs.back();
161 m_oldIDs.pop_back();
162 return id;
163}
164
165void
166CArchTaskBarWindows::recycleID(UINT id)
167{
168 m_oldIDs.push_back(id);
169}
170
171void
172CArchTaskBarWindows::addIcon(UINT id)
173{
174 ARCH->lockMutex(m_mutex);
175 CIDToReceiverMap::const_iterator index = m_idTable.find(id);
176 if (index != m_idTable.end()) {
177 modifyIconNoLock(index->second, NIM_ADD);
178 }
179 ARCH->unlockMutex(m_mutex);
180}
181
182void
183CArchTaskBarWindows::removeIcon(UINT id)
184{
185 ARCH->lockMutex(m_mutex);
186 removeIconNoLock(id);
187 ARCH->unlockMutex(m_mutex);
188}
189
190void
191CArchTaskBarWindows::updateIcon(UINT id)
192{
193 ARCH->lockMutex(m_mutex);
194 CIDToReceiverMap::const_iterator index = m_idTable.find(id);
195 if (index != m_idTable.end()) {
196 modifyIconNoLock(index->second, NIM_MODIFY);
197 }
198 ARCH->unlockMutex(m_mutex);
199}
200
201void
202CArchTaskBarWindows::addAllIcons()
203{
204 ARCH->lockMutex(m_mutex);
205 for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
206 index != m_receivers.end(); ++index) {
207 modifyIconNoLock(index, NIM_ADD);
208 }
209 ARCH->unlockMutex(m_mutex);
210}
211
212void
213CArchTaskBarWindows::removeAllIcons()
214{
215 ARCH->lockMutex(m_mutex);
216 for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
217 index != m_receivers.end(); ++index) {
218 removeIconNoLock(index->second.m_id);
219 }
220 ARCH->unlockMutex(m_mutex);
221}
222
223void
224CArchTaskBarWindows::modifyIconNoLock(
225 CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage)
226{
227 // get receiver
228 UINT id = index->second.m_id;
229 IArchTaskBarReceiver* receiver = index->first;
230
231 // lock receiver so icon and tool tip are guaranteed to be consistent
232 receiver->lock();
233
234 // get icon data
235 HICON icon = reinterpret_cast<HICON>(
236 const_cast<IArchTaskBarReceiver::Icon>(receiver->getIcon()));
237
238 // get tool tip
239 std::string toolTip = receiver->getToolTip();
240
241 // done querying
242 receiver->unlock();
243
244 // prepare to add icon
245 NOTIFYICONDATA data;
246 data.cbSize = sizeof(NOTIFYICONDATA);
247 data.hWnd = m_hwnd;
248 data.uID = id;
249 data.uFlags = NIF_MESSAGE;
250 data.uCallbackMessage = kNotifyReceiver;
251 data.hIcon = icon;
252 if (icon != NULL) {
253 data.uFlags |= NIF_ICON;
254 }
255 if (!toolTip.empty()) {
256 strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip));
257 data.szTip[sizeof(data.szTip) - 1] = '\0';
258 data.uFlags |= NIF_TIP;
259 }
260 else {
261 data.szTip[0] = '\0';
262 }
263
264 // add icon
265 if (Shell_NotifyIcon(taskBarMessage, &data) == 0) {
266 // failed
267 }
268}
269
270void
271CArchTaskBarWindows::removeIconNoLock(UINT id)
272{
273 NOTIFYICONDATA data;
274 data.cbSize = sizeof(NOTIFYICONDATA);
275 data.hWnd = m_hwnd;
276 data.uID = id;
277 if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) {
278 // failed
279 }
280}
281
282void
283CArchTaskBarWindows::handleIconMessage(
284 IArchTaskBarReceiver* receiver, LPARAM lParam)
285{
286 // process message
287 switch (lParam) {
288 case WM_LBUTTONDOWN:
289 receiver->showStatus();
290 break;
291
292 case WM_LBUTTONDBLCLK:
293 receiver->primaryAction();
294 break;
295
296 case WM_RBUTTONUP: {
297 POINT p;
298 GetCursorPos(&p);
299 receiver->runMenu(p.x, p.y);
300 break;
301 }
302
303 case WM_MOUSEMOVE:
304 // currently unused
305 break;
306
307 default:
308 // unused
309 break;
310 }
311}
312
313bool
314CArchTaskBarWindows::processDialogs(MSG* msg)
315{
316 // only one thread can be in this method on any particular object
317 // at any given time. that's not a problem since only our event
318 // loop calls this method and there's just one of those.
319
320 ARCH->lockMutex(m_mutex);
321
322 // remove removed dialogs
323 m_dialogs.erase(false);
324
325 // merge added dialogs into the dialog list
326 for (CDialogs::const_iterator index = m_addedDialogs.begin();
327 index != m_addedDialogs.end(); ++index) {
328 m_dialogs.insert(std::make_pair(index->first, index->second));
329 }
330 m_addedDialogs.clear();
331
332 ARCH->unlockMutex(m_mutex);
333
334 // check message against all dialogs until one handles it.
335 // note that we don't hold a lock while checking because
336 // the message is processed and may make calls to this
337 // object. that's okay because addDialog() and
338 // removeDialog() don't change the map itself (just the
339 // values of some elements).
340 ARCH->lockMutex(m_mutex);
341 for (CDialogs::const_iterator index = m_dialogs.begin();
342 index != m_dialogs.end(); ++index) {
343 if (index->second) {
344 ARCH->unlockMutex(m_mutex);
345 if (IsDialogMessage(index->first, msg)) {
346 return true;
347 }
348 ARCH->lockMutex(m_mutex);
349 }
350 }
351 ARCH->unlockMutex(m_mutex);
352
353 return false;
354}
355
356LRESULT
357CArchTaskBarWindows::wndProc(HWND hwnd,
358 UINT msg, WPARAM wParam, LPARAM lParam)
359{
360 switch (msg) {
361 case kNotifyReceiver: {
362 // lookup receiver
363 CIDToReceiverMap::const_iterator index = m_idTable.find(wParam);
364 if (index != m_idTable.end()) {
365 IArchTaskBarReceiver* receiver = index->second->first;
366 handleIconMessage(receiver, lParam);
367 return 0;
368 }
369 break;
370 }
371
372 case kAddReceiver:
373 addIcon(wParam);
374 break;
375
376 case kRemoveReceiver:
377 removeIcon(wParam);
378 break;
379
380 case kUpdateReceiver:
381 updateIcon(wParam);
382 break;
383
384 default:
385 if (msg == m_taskBarRestart) {
386 // task bar was recreated so re-add our icons
387 addAllIcons();
388 }
389 break;
390 }
391
392 return DefWindowProc(hwnd, msg, wParam, lParam);
393}
394
395LRESULT CALLBACK
396CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
397 WPARAM wParam, LPARAM lParam)
398{
399 // if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put
400 // it in the extra window data then forward the call.
401 CArchTaskBarWindows* self = NULL;
402 if (msg == WM_NCCREATE) {
403 CREATESTRUCT* createInfo;
404 createInfo = reinterpret_cast<CREATESTRUCT*>(lParam);
405 self = reinterpret_cast<CArchTaskBarWindows*>(
406 createInfo->lpCreateParams);
407 SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(self));
408 }
409 else {
410 // get the extra window data and forward the call
411 LONG data = GetWindowLong(hwnd, 0);
412 if (data != 0) {
413 self = reinterpret_cast<CArchTaskBarWindows*>(
414 reinterpret_cast<void*>(data));
415 }
416 }
417
418 // forward the message
419 if (self != NULL) {
420 return self->wndProc(hwnd, msg, wParam, lParam);
421 }
422 else {
423 return DefWindowProc(hwnd, msg, wParam, lParam);
424 }
425}
426
427void
428CArchTaskBarWindows::threadMainLoop()
429{
430 // register the task bar restart message
431 m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
432
433 // register a window class
434 WNDCLASSEX classInfo;
435 classInfo.cbSize = sizeof(classInfo);
436 classInfo.style = CS_NOCLOSE;
437 classInfo.lpfnWndProc = &CArchTaskBarWindows::staticWndProc;
438 classInfo.cbClsExtra = 0;
439 classInfo.cbWndExtra = sizeof(CArchTaskBarWindows*);
440 classInfo.hInstance = s_appInstance;
441 classInfo.hIcon = NULL;
442 classInfo.hCursor = NULL;
443 classInfo.hbrBackground = NULL;
444 classInfo.lpszMenuName = NULL;
445 classInfo.lpszClassName = TEXT("SynergyTaskBar");
446 classInfo.hIconSm = NULL;
447 ATOM windowClass = RegisterClassEx(&classInfo);
448
449 // create window
450 m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
451 reinterpret_cast<LPCTSTR>(windowClass),
452 TEXT("Synergy Task Bar"),
453 WS_POPUP,
454 0, 0, 1, 1,
455 NULL,
456 NULL,
457 s_appInstance,
458 reinterpret_cast<void*>(this));
459
460 // signal ready
461 ARCH->lockMutex(m_mutex);
462 m_ready = true;
463 ARCH->broadcastCondVar(m_condVar);
464 ARCH->unlockMutex(m_mutex);
465
466 // handle failure
467 if (m_hwnd == NULL) {
468 UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
469 return;
470 }
471
472 // main loop
473 MSG msg;
474 while (GetMessage(&msg, NULL, 0, 0)) {
475 if (!processDialogs(&msg)) {
476 TranslateMessage(&msg);
477 DispatchMessage(&msg);
478 }
479 }
480
481 // clean up
482 removeAllIcons();
483 DestroyWindow(m_hwnd);
484 UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), s_appInstance);
485}
486
487void*
488CArchTaskBarWindows::threadEntry(void* self)
489{
490 reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
491 return NULL;
492}
Note: See TracBrowser for help on using the repository browser.