• Main Page
  • Classes
  • Files
  • File List

CArchTaskBarWindows.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2012 Bolton Software Ltd.
00004  * Copyright (C) 2003 Chris Schoeneman
00005  * 
00006  * This package is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * found in the file COPYING that should have accompanied this file.
00009  * 
00010  * This package is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "CArchTaskBarWindows.h"
00020 #include "CArchMiscWindows.h"
00021 #include "IArchTaskBarReceiver.h"
00022 #include "CArch.h"
00023 #include "XArch.h"
00024 #include <string.h>
00025 #include <shellapi.h>
00026 #include "CAppUtilWindows.h"
00027 
00028 static const UINT       kAddReceiver     = WM_USER + 10;
00029 static const UINT       kRemoveReceiver  = WM_USER + 11;
00030 static const UINT       kUpdateReceiver  = WM_USER + 12;
00031 static const UINT       kNotifyReceiver  = WM_USER + 13;
00032 static const UINT       kFirstReceiverID = WM_USER + 14;
00033 
00034 //
00035 // CArchTaskBarWindows
00036 //
00037 
00038 CArchTaskBarWindows*    CArchTaskBarWindows::s_instance    = NULL;
00039 
00040 CArchTaskBarWindows::CArchTaskBarWindows() :
00041     m_nextID(kFirstReceiverID)
00042 {
00043     // save the singleton instance
00044     s_instance    = this;
00045 }
00046 
00047 CArchTaskBarWindows::~CArchTaskBarWindows()
00048 {
00049     if (m_thread != NULL) {
00050         PostMessage(m_hwnd, WM_QUIT, 0, 0);
00051         ARCH->wait(m_thread, -1.0);
00052         ARCH->closeThread(m_thread);
00053     }
00054     ARCH->closeCondVar(m_condVar);
00055     ARCH->closeMutex(m_mutex);
00056     s_instance = NULL;
00057 }
00058 
00059 void
00060 CArchTaskBarWindows::init()
00061 {
00062     // we need a mutex
00063     m_mutex       = ARCH->newMutex();
00064 
00065     // and a condition variable which uses the above mutex
00066     m_ready       = false;
00067     m_condVar     = ARCH->newCondVar();
00068 
00069     // we're going to want to get a result from the thread we're
00070     // about to create to know if it initialized successfully.
00071     // so we lock the condition variable.
00072     ARCH->lockMutex(m_mutex);
00073 
00074     // open a window and run an event loop in a separate thread.
00075     // this has to happen in a separate thread because if we
00076     // create a window on the current desktop with the current
00077     // thread then the current thread won't be able to switch
00078     // desktops if it needs to.
00079     m_thread      = ARCH->newThread(&CArchTaskBarWindows::threadEntry, this);
00080 
00081     // wait for child thread
00082     while (!m_ready) {
00083         ARCH->waitCondVar(m_condVar, m_mutex, -1.0);
00084     }
00085 
00086     // ready
00087     ARCH->unlockMutex(m_mutex);
00088 }
00089 
00090 void
00091 CArchTaskBarWindows::addDialog(HWND hwnd)
00092 {
00093     CArchMiscWindows::addDialog(hwnd);
00094 }
00095 
00096 void
00097 CArchTaskBarWindows::removeDialog(HWND hwnd)
00098 {
00099     CArchMiscWindows::removeDialog(hwnd);
00100 }
00101 
00102 void
00103 CArchTaskBarWindows::addReceiver(IArchTaskBarReceiver* receiver)
00104 {
00105     // ignore bogus receiver
00106     if (receiver == NULL) {
00107         return;
00108     }
00109 
00110     // add receiver if necessary
00111     CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
00112     if (index == m_receivers.end()) {
00113         // add it, creating a new message ID for it
00114         CReceiverInfo info;
00115         info.m_id = getNextID();
00116         index = m_receivers.insert(std::make_pair(receiver, info)).first;
00117 
00118         // add ID to receiver mapping
00119         m_idTable.insert(std::make_pair(info.m_id, index));
00120     }
00121 
00122     // add receiver
00123     PostMessage(m_hwnd, kAddReceiver, index->second.m_id, 0);
00124 }
00125 
00126 void
00127 CArchTaskBarWindows::removeReceiver(IArchTaskBarReceiver* receiver)
00128 {
00129     // find receiver
00130     CReceiverToInfoMap::iterator index = m_receivers.find(receiver);
00131     if (index == m_receivers.end()) {
00132         return;
00133     }
00134 
00135     // remove icon.  wait for this to finish before returning.
00136     SendMessage(m_hwnd, kRemoveReceiver, index->second.m_id, 0);
00137 
00138     // recycle the ID
00139     recycleID(index->second.m_id);
00140 
00141     // discard
00142     m_idTable.erase(index->second.m_id);
00143     m_receivers.erase(index);
00144 }
00145 
00146 void
00147 CArchTaskBarWindows::updateReceiver(IArchTaskBarReceiver* receiver)
00148 {
00149     // find receiver
00150     CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver);
00151     if (index == m_receivers.end()) {
00152         return;
00153     }
00154 
00155     // update icon and tool tip
00156     PostMessage(m_hwnd, kUpdateReceiver, index->second.m_id, 0);
00157 }
00158 
00159 UINT
00160 CArchTaskBarWindows::getNextID()
00161 {
00162     if (m_oldIDs.empty()) {
00163         return m_nextID++;
00164     }
00165     UINT id = m_oldIDs.back();
00166     m_oldIDs.pop_back();
00167     return id;
00168 }
00169 
00170 void
00171 CArchTaskBarWindows::recycleID(UINT id)
00172 {
00173     m_oldIDs.push_back(id);
00174 }
00175 
00176 void
00177 CArchTaskBarWindows::addIcon(UINT id)
00178 {
00179     ARCH->lockMutex(m_mutex);
00180     CIDToReceiverMap::const_iterator index = m_idTable.find(id);
00181     if (index != m_idTable.end()) {
00182         modifyIconNoLock(index->second, NIM_ADD);
00183     }
00184     ARCH->unlockMutex(m_mutex);
00185 }
00186 
00187 void
00188 CArchTaskBarWindows::removeIcon(UINT id)
00189 {
00190     ARCH->lockMutex(m_mutex);
00191     removeIconNoLock(id);
00192     ARCH->unlockMutex(m_mutex);
00193 }
00194 
00195 void
00196 CArchTaskBarWindows::updateIcon(UINT id)
00197 {
00198     ARCH->lockMutex(m_mutex);
00199     CIDToReceiverMap::const_iterator index = m_idTable.find(id);
00200     if (index != m_idTable.end()) {
00201         modifyIconNoLock(index->second, NIM_MODIFY);
00202     }
00203     ARCH->unlockMutex(m_mutex);
00204 }
00205 
00206 void
00207 CArchTaskBarWindows::addAllIcons()
00208 {
00209     ARCH->lockMutex(m_mutex);
00210     for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
00211                                     index != m_receivers.end(); ++index) {
00212         modifyIconNoLock(index, NIM_ADD);
00213     }
00214     ARCH->unlockMutex(m_mutex);
00215 }
00216 
00217 void
00218 CArchTaskBarWindows::removeAllIcons()
00219 {
00220     ARCH->lockMutex(m_mutex);
00221     for (CReceiverToInfoMap::const_iterator index = m_receivers.begin();
00222                                     index != m_receivers.end(); ++index) {
00223         removeIconNoLock(index->second.m_id);
00224     }
00225     ARCH->unlockMutex(m_mutex);
00226 }
00227 
00228 void
00229 CArchTaskBarWindows::modifyIconNoLock(
00230                 CReceiverToInfoMap::const_iterator index, DWORD taskBarMessage)
00231 {
00232     // get receiver
00233     UINT id                        = index->second.m_id;
00234     IArchTaskBarReceiver* receiver = index->first;
00235 
00236     // lock receiver so icon and tool tip are guaranteed to be consistent
00237     receiver->lock();
00238 
00239     // get icon data
00240     HICON icon = reinterpret_cast<HICON>(
00241                 const_cast<IArchTaskBarReceiver::Icon>(receiver->getIcon()));
00242 
00243     // get tool tip
00244     std::string toolTip = receiver->getToolTip();
00245 
00246     // done querying
00247     receiver->unlock();
00248 
00249     // prepare to add icon
00250     NOTIFYICONDATA data;
00251     data.cbSize           = sizeof(NOTIFYICONDATA);
00252     data.hWnd             = m_hwnd;
00253     data.uID              = id;
00254     data.uFlags           = NIF_MESSAGE;
00255     data.uCallbackMessage = kNotifyReceiver;
00256     data.hIcon            = icon;
00257     if (icon != NULL) {
00258         data.uFlags |= NIF_ICON;
00259     }
00260     if (!toolTip.empty()) {
00261         strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip));
00262         data.szTip[sizeof(data.szTip) - 1] = '\0';
00263         data.uFlags                       |= NIF_TIP;
00264     }
00265     else {
00266         data.szTip[0] = '\0';
00267     }
00268 
00269     // add icon
00270     if (Shell_NotifyIcon(taskBarMessage, &data) == 0) {
00271         // failed
00272     }
00273 }
00274 
00275 void
00276 CArchTaskBarWindows::removeIconNoLock(UINT id)
00277 {
00278     NOTIFYICONDATA data;
00279     data.cbSize = sizeof(NOTIFYICONDATA);
00280     data.hWnd   = m_hwnd;
00281     data.uID    = id;
00282     if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) {
00283         // failed
00284     }
00285 }
00286 
00287 void
00288 CArchTaskBarWindows::handleIconMessage(
00289                 IArchTaskBarReceiver* receiver, LPARAM lParam)
00290 {
00291     // process message
00292     switch (lParam) {
00293     case WM_LBUTTONDOWN:
00294         receiver->showStatus();
00295         break;
00296 
00297     case WM_LBUTTONDBLCLK:
00298         receiver->primaryAction();
00299         break;
00300 
00301     case WM_RBUTTONUP: {
00302         POINT p;
00303         GetCursorPos(&p);
00304         receiver->runMenu(p.x, p.y);
00305         break;
00306     }
00307 
00308     case WM_MOUSEMOVE:
00309         // currently unused
00310         break;
00311 
00312     default:
00313         // unused
00314         break;
00315     }
00316 }
00317 
00318 bool
00319 CArchTaskBarWindows::processDialogs(MSG* msg)
00320 {
00321     // only one thread can be in this method on any particular object
00322     // at any given time.  that's not a problem since only our event
00323     // loop calls this method and there's just one of those.
00324 
00325     ARCH->lockMutex(m_mutex);
00326 
00327     // remove removed dialogs
00328     m_dialogs.erase(false);
00329 
00330     // merge added dialogs into the dialog list
00331     for (CDialogs::const_iterator index = m_addedDialogs.begin();
00332                             index != m_addedDialogs.end(); ++index) {
00333         m_dialogs.insert(std::make_pair(index->first, index->second));
00334     }
00335     m_addedDialogs.clear();
00336 
00337     ARCH->unlockMutex(m_mutex);
00338 
00339     // check message against all dialogs until one handles it.
00340     // note that we don't hold a lock while checking because
00341     // the message is processed and may make calls to this
00342     // object.  that's okay because addDialog() and
00343     // removeDialog() don't change the map itself (just the
00344     // values of some elements).
00345     ARCH->lockMutex(m_mutex);
00346     for (CDialogs::const_iterator index = m_dialogs.begin();
00347                             index != m_dialogs.end(); ++index) {
00348         if (index->second) {
00349             ARCH->unlockMutex(m_mutex);
00350             if (IsDialogMessage(index->first, msg)) {
00351                 return true;
00352             }
00353             ARCH->lockMutex(m_mutex);
00354         }
00355     }
00356     ARCH->unlockMutex(m_mutex);
00357 
00358     return false;
00359 }
00360 
00361 LRESULT
00362 CArchTaskBarWindows::wndProc(HWND hwnd,
00363                 UINT msg, WPARAM wParam, LPARAM lParam)
00364 {
00365     switch (msg) {
00366     case kNotifyReceiver: {
00367         // lookup receiver
00368         CIDToReceiverMap::const_iterator index = m_idTable.find((UINT)wParam);
00369         if (index != m_idTable.end()) {
00370             IArchTaskBarReceiver* receiver = index->second->first;
00371             handleIconMessage(receiver, lParam);
00372             return 0;
00373         }
00374         break;
00375     }
00376 
00377     case kAddReceiver:
00378         addIcon((UINT)wParam);
00379         break;
00380 
00381     case kRemoveReceiver:
00382         removeIcon((UINT)wParam);
00383         break;
00384 
00385     case kUpdateReceiver:
00386         updateIcon((UINT)wParam);
00387         break;
00388 
00389     default:
00390         if (msg == m_taskBarRestart) {
00391             // task bar was recreated so re-add our icons
00392             addAllIcons();
00393         }
00394         break;
00395     }
00396 
00397     return DefWindowProc(hwnd, msg, wParam, lParam);
00398 }
00399 
00400 LRESULT CALLBACK
00401 CArchTaskBarWindows::staticWndProc(HWND hwnd, UINT msg,
00402                 WPARAM wParam, LPARAM lParam)
00403 {
00404     // if msg is WM_NCCREATE, extract the CArchTaskBarWindows* and put
00405     // it in the extra window data then forward the call.
00406     CArchTaskBarWindows* self = NULL;
00407     if (msg == WM_NCCREATE) {
00408         CREATESTRUCT* createInfo;
00409         createInfo = reinterpret_cast<CREATESTRUCT*>(lParam);
00410         self       = reinterpret_cast<CArchTaskBarWindows*>(
00411                                                 createInfo->lpCreateParams);
00412         SetWindowLong(hwnd, 0, reinterpret_cast<LONG>(self));
00413     }
00414     else {
00415         // get the extra window data and forward the call
00416         LONG data = GetWindowLong(hwnd, 0);
00417         if (data != 0) {
00418             self = reinterpret_cast<CArchTaskBarWindows*>(
00419                             reinterpret_cast<void*>(data));
00420         }
00421     }
00422 
00423     // forward the message
00424     if (self != NULL) {
00425         return self->wndProc(hwnd, msg, wParam, lParam);
00426     }
00427     else {
00428         return DefWindowProc(hwnd, msg, wParam, lParam);
00429     }
00430 }
00431 
00432 void
00433 CArchTaskBarWindows::threadMainLoop()
00434 {
00435     // register the task bar restart message
00436     m_taskBarRestart        = RegisterWindowMessage(TEXT("TaskbarCreated"));
00437 
00438     // register a window class
00439     WNDCLASSEX classInfo;
00440     classInfo.cbSize        = sizeof(classInfo);
00441     classInfo.style         = CS_NOCLOSE;
00442     classInfo.lpfnWndProc   = &CArchTaskBarWindows::staticWndProc;
00443     classInfo.cbClsExtra    = 0;
00444     classInfo.cbWndExtra    = sizeof(CArchTaskBarWindows*);
00445     classInfo.hInstance     = instanceWin32();
00446     classInfo.hIcon         = NULL;
00447     classInfo.hCursor       = NULL;
00448     classInfo.hbrBackground = NULL;
00449     classInfo.lpszMenuName  = NULL;
00450     classInfo.lpszClassName = TEXT("SynergyTaskBar");
00451     classInfo.hIconSm       = NULL;
00452     ATOM windowClass        = RegisterClassEx(&classInfo);
00453 
00454     // create window
00455     m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
00456                             reinterpret_cast<LPCTSTR>(windowClass),
00457                             TEXT("Synergy Task Bar"),
00458                             WS_POPUP,
00459                             0, 0, 1, 1,
00460                             NULL,
00461                             NULL,
00462                             instanceWin32(),
00463                             reinterpret_cast<void*>(this));
00464 
00465     // signal ready
00466     ARCH->lockMutex(m_mutex);
00467     m_ready = true;
00468     ARCH->broadcastCondVar(m_condVar);
00469     ARCH->unlockMutex(m_mutex);
00470 
00471     // handle failure
00472     if (m_hwnd == NULL) {
00473         UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
00474         return;
00475     }
00476 
00477     // main loop
00478     MSG msg;
00479     while (GetMessage(&msg, NULL, 0, 0)) {
00480         if (!processDialogs(&msg)) {
00481             TranslateMessage(&msg);
00482             DispatchMessage(&msg);
00483         }
00484     }
00485 
00486     // clean up
00487     removeAllIcons();
00488     DestroyWindow(m_hwnd);
00489     UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass), instanceWin32());
00490 }
00491 
00492 void*
00493 CArchTaskBarWindows::threadEntry(void* self)
00494 {
00495     reinterpret_cast<CArchTaskBarWindows*>(self)->threadMainLoop();
00496     return NULL;
00497 }
00498 
00499 HINSTANCE CArchTaskBarWindows::instanceWin32()
00500 {
00501     return CArchMiscWindows::instanceWin32();
00502 }

Generated on Thu May 23 2013 00:00:03 for Synergy by  doxygen 1.7.1