/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2003 Chris Schoeneman * * This package is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * found in the file COPYING that should have accompanied this file. * * This package is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "CArchTaskBarOS2.h" #include "IArchTaskBarReceiver.h" #include "CArch.h" #include "XArch.h" #include /** @todo xwp */ static const ULONG kAddReceiver = WM_USER + 10; static const ULONG kRemoveReceiver = WM_USER + 11; static const ULONG kUpdateReceiver = WM_USER + 12; static const ULONG kNotifyReceiver = WM_USER + 13; static const ULONG kFirstReceiverID = WM_USER + 14; // // CArchTaskBarWindows // CArchTaskBarOS2* CArchTaskBarOS2::s_instance = NULL; CArchTaskBarOS2::CArchTaskBarOS2(void* appInstance) : m_nextID(kFirstReceiverID) { // we need a mutex m_mutex = ARCH->newMutex(); // and a condition variable which uses the above mutex m_ready = false; m_condVar = ARCH->newCondVar(); // we're going to want to get a result from the thread we're // about to create to know if it initialized successfully. // so we lock the condition variable. ARCH->lockMutex(m_mutex); #if 0 /* later */ // open a window and run an event loop in a separate thread. // this has to happen in a separate thread because if we // create a window on the current desktop with the current // thread then the current thread won't be able to switch // desktops if it needs to. m_thread = ARCH->newThread(&CArchTaskBarOS2::threadEntry, this); // wait for child thread while (!m_ready) { ARCH->waitCondVar(m_condVar, m_mutex, -1.0); } #else m_thread = NULL; #endif /* later */ // ready ARCH->unlockMutex(m_mutex); } CArchTaskBarOS2::~CArchTaskBarOS2() { if (m_thread != NULL) { WinPostMsg(m_hwnd, WM_QUIT, 0, 0); ARCH->wait(m_thread, -1.0); ARCH->closeThread(m_thread); } ARCH->closeCondVar(m_condVar); ARCH->closeMutex(m_mutex); } void CArchTaskBarOS2::addDialog(HWND hwnd) { //later CArchMiscWindows::addDialog(hwnd); } void CArchTaskBarOS2::removeDialog(HWND hwnd) { //later CArchMiscWindows::removeDialog(hwnd); } void CArchTaskBarOS2::addReceiver(IArchTaskBarReceiver* receiver) { // ignore bogus receiver if (receiver == NULL) { return; } // add receiver if necessary CReceiverToInfoMap::iterator index = m_receivers.find(receiver); if (index == m_receivers.end()) { // add it, creating a new message ID for it CReceiverInfo info; info.m_id = getNextID(); index = m_receivers.insert(std::make_pair(receiver, info)).first; // add ID to receiver mapping m_idTable.insert(std::make_pair(info.m_id, index)); } // add receiver WinPostMsg(m_hwnd, kAddReceiver, (MPARAM)index->second.m_id, 0); } void CArchTaskBarOS2::removeReceiver(IArchTaskBarReceiver* receiver) { // find receiver CReceiverToInfoMap::iterator index = m_receivers.find(receiver); if (index == m_receivers.end()) { return; } // remove icon. wait for this to finish before returning. WinSendMsg(m_hwnd, kRemoveReceiver, (MPARAM)index->second.m_id, 0); // recycle the ID recycleID(index->second.m_id); // discard m_idTable.erase(index->second.m_id); m_receivers.erase(index); } void CArchTaskBarOS2::updateReceiver(IArchTaskBarReceiver* receiver) { // find receiver CReceiverToInfoMap::const_iterator index = m_receivers.find(receiver); if (index == m_receivers.end()) { return; } // update icon and tool tip WinPostMsg(m_hwnd, kUpdateReceiver, (MPARAM)index->second.m_id, 0); } ULONG CArchTaskBarOS2::getNextID() { if (m_oldIDs.empty()) { return m_nextID++; } ULONG id = m_oldIDs.back(); m_oldIDs.pop_back(); return id; } void CArchTaskBarOS2::recycleID(ULONG id) { m_oldIDs.push_back(id); } void CArchTaskBarOS2::addIcon(ULONG id) { ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { //later modifyIconNoLock(index->second, NIM_ADD); } ARCH->unlockMutex(m_mutex); } void CArchTaskBarOS2::removeIcon(ULONG id) { ARCH->lockMutex(m_mutex); removeIconNoLock(id); ARCH->unlockMutex(m_mutex); } void CArchTaskBarOS2::updateIcon(ULONG id) { ARCH->lockMutex(m_mutex); CIDToReceiverMap::const_iterator index = m_idTable.find(id); if (index != m_idTable.end()) { //later modifyIconNoLock(index->second, NIM_MODIFY); } ARCH->unlockMutex(m_mutex); } void CArchTaskBarOS2::addAllIcons() { ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { //later modifyIconNoLock(index, NIM_ADD); } ARCH->unlockMutex(m_mutex); } void CArchTaskBarOS2::removeAllIcons() { ARCH->lockMutex(m_mutex); for (CReceiverToInfoMap::const_iterator index = m_receivers.begin(); index != m_receivers.end(); ++index) { removeIconNoLock(index->second.m_id); } ARCH->unlockMutex(m_mutex); } void CArchTaskBarOS2::modifyIconNoLock( CReceiverToInfoMap::const_iterator index, ULONG taskBarMessage) { #if 0 // get receiver ULONG id = index->second.m_id; IArchTaskBarReceiver* receiver = index->first; // lock receiver so icon and tool tip are guaranteed to be consistent receiver->lock(); // get icon data HICON icon = reinterpret_cast( const_cast(receiver->getIcon())); // get tool tip std::string toolTip = receiver->getToolTip(); // done querying receiver->unlock(); // prepare to add icon NOTIFYICONDATA data; data.cbSize = sizeof(NOTIFYICONDATA); data.hWnd = m_hwnd; data.uID = id; data.uFlags = NIF_MESSAGE; data.uCallbackMessage = kNotifyReceiver; data.hIcon = icon; if (icon != NULL) { data.uFlags |= NIF_ICON; } if (!toolTip.empty()) { strncpy(data.szTip, toolTip.c_str(), sizeof(data.szTip)); data.szTip[sizeof(data.szTip) - 1] = '\0'; data.uFlags |= NIF_TIP; } else { data.szTip[0] = '\0'; } // add icon if (Shell_NotifyIcon(taskBarMessage, &data) == 0) { // failed } #endif } void CArchTaskBarOS2::removeIconNoLock(ULONG id) { #if 0 /*later*/ NOTIFYICONDATA data; data.cbSize = sizeof(NOTIFYICONDATA); data.hWnd = m_hwnd; data.uID = id; if (Shell_NotifyIcon(NIM_DELETE, &data) == 0) { // failed } #endif } void CArchTaskBarOS2::handleIconMessage( IArchTaskBarReceiver* receiver, MPARAM lParam) { // process message switch ((ULONG)lParam) { case WM_BUTTON2DOWN: receiver->showStatus(); break; case WM_BUTTON2DBLCLK: receiver->primaryAction(); break; case WM_BUTTON2UP: { CURSORINFO info; if (!WinQueryCursorInfo(HWND_DESKTOP, &info)) { info.x = 10; info.y = 10; } receiver->runMenu(info.x, info.y); break; } case WM_MOUSEMOVE: // currently unused break; default: // unused break; } } bool CArchTaskBarOS2::processDialogs(QMSG * msg) { #if 0 /* later */ // only one thread can be in this method on any particular object // at any given time. that's not a problem since only our event // loop calls this method and there's just one of those. ARCH->lockMutex(m_mutex); // remove removed dialogs m_dialogs.erase(false); // merge added dialogs into the dialog list for (CDialogs::const_iterator index = m_addedDialogs.begin(); index != m_addedDialogs.end(); ++index) { m_dialogs.insert(std::make_pair(index->first, index->second)); } m_addedDialogs.clear(); ARCH->unlockMutex(m_mutex); // check message against all dialogs until one handles it. // note that we don't hold a lock while checking because // the message is processed and may make calls to this // object. that's okay because addDialog() and // removeDialog() don't change the map itself (just the // values of some elements). ARCH->lockMutex(m_mutex); for (CDialogs::const_iterator index = m_dialogs.begin(); index != m_dialogs.end(); ++index) { if (index->second) { ARCH->unlockMutex(m_mutex); if (IsDialogMessage(index->first, msg)) { return true; } ARCH->lockMutex(m_mutex); } } ARCH->unlockMutex(m_mutex); #endif /* later */ return false; } MRESULT CArchTaskBarOS2::wndProc(HWND hwnd, ULONG msg, MPARAM wParam, MPARAM lParam) { switch (msg) { case kNotifyReceiver: { // lookup receiver CIDToReceiverMap::const_iterator index = m_idTable.find((ULONG)wParam); if (index != m_idTable.end()) { IArchTaskBarReceiver* receiver = index->second->first; handleIconMessage(receiver, lParam); return 0; } break; } case kAddReceiver: addIcon((ULONG)wParam); break; case kRemoveReceiver: removeIcon((ULONG)wParam); break; case kUpdateReceiver: updateIcon((ULONG)wParam); break; default: if (msg == m_taskBarRestart) { // task bar was recreated so re-add our icons addAllIcons(); } break; } return WinDefWindowProc(hwnd, msg, wParam, lParam); } MRESULT EXPENTRY CArchTaskBarOS2::staticWndProc(HWND hwnd, ULONG msg, MPARAM wParam, MPARAM lParam) { #if 0 /*later*/ // if msg is WM_NCCREATE, extract the CArchTaskBarOS2* and put // it in the extra window data then forward the call. CArchTaskBarOS2* self = NULL; if (msg == WM_CREATE) { CREATESTRUCT* createInfo; createInfo = reinterpret_cast(lParam); self = reinterpret_cast( createInfo->lpCreateParams); SetWindowLong(hwnd, 0, reinterpret_cast(self)); } else { // get the extra window data and forward the call LONG data = GetWindowLong(hwnd, 0); if (data != 0) { self = reinterpret_cast( reinterpret_cast(data)); } } // forward the message if (self != NULL) { return self->wndProc(hwnd, msg, wParam, lParam); } else { return DefWindowProc(hwnd, msg, wParam, lParam); } #endif } void CArchTaskBarOS2::threadMainLoop() { #if 0 /*later*/ // register the task bar restart message m_taskBarRestart = RegisterWindowMessage(TEXT("TaskbarCreated")); // register a window class WNDCLASSEX classInfo; classInfo.cbSize = sizeof(classInfo); classInfo.style = CS_NOCLOSE; classInfo.lpfnWndProc = &CArchTaskBarOS2::staticWndProc; classInfo.cbClsExtra = 0; classInfo.cbWndExtra = sizeof(CArchTaskBarOS2*); classInfo.hInstance = s_appInstance; classInfo.hIcon = NULL; classInfo.hCursor = NULL; classInfo.hbrBackground = NULL; classInfo.lpszMenuName = NULL; classInfo.lpszClassName = TEXT("SynergyTaskBar"); classInfo.hIconSm = NULL; ATOM windowClass = RegisterClassEx(&classInfo); // create window m_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW, reinterpret_cast(windowClass), TEXT("Synergy Task Bar"), WS_POPUP, 0, 0, 1, 1, NULL, NULL, s_appInstance, reinterpret_cast(this)); // signal ready ARCH->lockMutex(m_mutex); m_ready = true; ARCH->broadcastCondVar(m_condVar); ARCH->unlockMutex(m_mutex); // handle failure if (m_hwnd == NULL) { UnregisterClass(reinterpret_cast(windowClass), s_appInstance); return; } // main loop MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { if (!processDialogs(&msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } // clean up removeAllIcons(); DestroyWindow(m_hwnd); UnregisterClass(reinterpret_cast(windowClass), s_appInstance); #endif /* later */ } void* CArchTaskBarOS2::threadEntry(void* self) { reinterpret_cast(self)->threadMainLoop(); return NULL; }