Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

CArchTaskBarWindows.cpp

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

Generated on Fri Nov 6 00:21:13 2009 for synergy-plus by  doxygen 1.3.9.1