diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp index f348a2a8..47561edd 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.cpp @@ -14,6 +14,8 @@ #include "CMSWindowsClientTaskBarReceiver.h" #include "CClient.h" +#include "CMSWindowsClipboard.h" +#include "LogOutputters.h" #include "BasicTypes.h" #include "CArch.h" #include "CArchTaskBarWindows.h" @@ -32,10 +34,11 @@ static const UINT g_stateToIconID[CMSWindowsClientTaskBarReceiver::kMaxState] = // CMSWindowsClientTaskBarReceiver::CMSWindowsClientTaskBarReceiver( - HINSTANCE appInstance) : + HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CClientTaskBarReceiver(), m_appInstance(appInstance), - m_window(NULL) + m_window(NULL), + m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { m_icon[i] = loadIcon(g_stateToIconID[i]); @@ -149,6 +152,10 @@ CMSWindowsClientTaskBarReceiver::runMenu(int x, int y) showStatus(); break; + case IDC_TASKBAR_LOG: + copyLog(); + break; + case IDC_TASKBAR_QUIT: quit(); break; @@ -167,6 +174,29 @@ CMSWindowsClientTaskBarReceiver::getIcon() const return reinterpret_cast(m_icon[getState()]); } +void +CMSWindowsClientTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.empty(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + void CMSWindowsClientTaskBarReceiver::onStatusChanged() { diff --git a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h index 0fdb2cc5..56a3f161 100644 --- a/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h +++ b/cmd/synergyc/CMSWindowsClientTaskBarReceiver.h @@ -20,10 +20,12 @@ #include "CClientTaskBarReceiver.h" #include +class CBufferedLogOutputter; + //! Implementation of CClientTaskBarReceiver for Microsoft Windows class CMSWindowsClientTaskBarReceiver : public CClientTaskBarReceiver { public: - CMSWindowsClientTaskBarReceiver(HINSTANCE); + CMSWindowsClientTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); virtual ~CMSWindowsClientTaskBarReceiver(); // IArchTaskBarReceiver overrides @@ -33,6 +35,8 @@ public: virtual const Icon getIcon() const; protected: + void copyLog() const; + // CClientTaskBarReceiver overrides virtual void onStatusChanged(); @@ -53,6 +57,7 @@ private: HWND m_window; HMENU m_menu; HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; }; #endif diff --git a/cmd/synergyc/resource.h b/cmd/synergyc/resource.h index d80c2389..1da78601 100644 --- a/cmd/synergyc/resource.h +++ b/cmd/synergyc/resource.h @@ -13,6 +13,7 @@ #define IDC_TASKBAR_STATUS_STATUS 1000 #define IDC_TASKBAR_QUIT 40003 #define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 // Next default values for new objects // diff --git a/cmd/synergyc/synergyc.cpp b/cmd/synergyc/synergyc.cpp index e1aabab0..7acd28bf 100644 --- a/cmd/synergyc/synergyc.cpp +++ b/cmd/synergyc/synergyc.cpp @@ -649,9 +649,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // send PRINT and FATAL output to a message box CLOG->insert(new CMessageBoxOutputter); + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + // make the task bar receiver. the user can control this app // through the task bar. - s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance); + s_taskBarReceiver = new CMSWindowsClientTaskBarReceiver(instance, + &logBuffer); s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); int result; @@ -670,6 +675,9 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // done with task bar receiver delete s_taskBarReceiver; + // done with log buffer + CLOG->remove(&logBuffer); + // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. if (ARG->m_backend && s_hasImportantLogMessages) { diff --git a/cmd/synergyc/synergyc.rc b/cmd/synergyc/synergyc.rc index 156651a4..9fd00206 100644 --- a/cmd/synergyc/synergyc.rc +++ b/cmd/synergyc/synergyc.rc @@ -84,6 +84,7 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp index adcc132d..87f3d298 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.cpp @@ -14,6 +14,8 @@ #include "CMSWindowsServerTaskBarReceiver.h" #include "CServer.h" +#include "CMSWindowsClipboard.h" +#include "LogOutputters.h" #include "BasicTypes.h" #include "CArch.h" #include "CArchTaskBarWindows.h" @@ -32,10 +34,11 @@ static const UINT g_stateToIconID[CMSWindowsServerTaskBarReceiver::kMaxState] = // CMSWindowsServerTaskBarReceiver::CMSWindowsServerTaskBarReceiver( - HINSTANCE appInstance) : + HINSTANCE appInstance, const CBufferedLogOutputter* logBuffer) : CServerTaskBarReceiver(), m_appInstance(appInstance), - m_window(NULL) + m_window(NULL), + m_logBuffer(logBuffer) { for (UInt32 i = 0; i < kMaxState; ++i) { m_icon[i] = loadIcon(g_stateToIconID[i]); @@ -169,6 +172,10 @@ CMSWindowsServerTaskBarReceiver::runMenu(int x, int y) showStatus(); break; + case IDC_TASKBAR_LOG: + copyLog(); + break; + case IDC_TASKBAR_QUIT: quit(); break; @@ -187,6 +194,29 @@ CMSWindowsServerTaskBarReceiver::getIcon() const return reinterpret_cast(m_icon[getState()]); } +void +CMSWindowsServerTaskBarReceiver::copyLog() const +{ + if (m_logBuffer != NULL) { + // collect log buffer + CString data; + for (CBufferedLogOutputter::const_iterator index = m_logBuffer->begin(); + index != m_logBuffer->end(); ++index) { + data += *index; + data += "\n"; + } + + // copy log to clipboard + if (!data.empty()) { + CMSWindowsClipboard clipboard(m_window); + clipboard.open(0); + clipboard.empty(); + clipboard.add(IClipboard::kText, data); + clipboard.close(); + } + } +} + void CMSWindowsServerTaskBarReceiver::onStatusChanged() { diff --git a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h index 372aad0b..d1c3dc65 100644 --- a/cmd/synergys/CMSWindowsServerTaskBarReceiver.h +++ b/cmd/synergys/CMSWindowsServerTaskBarReceiver.h @@ -20,10 +20,12 @@ #include "CServerTaskBarReceiver.h" #include +class CBufferedLogOutputter; + //! Implementation of CServerTaskBarReceiver for Microsoft Windows class CMSWindowsServerTaskBarReceiver : public CServerTaskBarReceiver { public: - CMSWindowsServerTaskBarReceiver(HINSTANCE); + CMSWindowsServerTaskBarReceiver(HINSTANCE, const CBufferedLogOutputter*); virtual ~CMSWindowsServerTaskBarReceiver(); // IArchTaskBarReceiver overrides @@ -33,6 +35,8 @@ public: virtual const Icon getIcon() const; protected: + void copyLog() const; + // CServerTaskBarReceiver overrides virtual void onStatusChanged(); @@ -53,6 +57,7 @@ private: HWND m_window; HMENU m_menu; HICON m_icon[kMaxState]; + const CBufferedLogOutputter* m_logBuffer; }; #endif diff --git a/cmd/synergys/resource.h b/cmd/synergys/resource.h index 2e716a64..5ac24707 100644 --- a/cmd/synergys/resource.h +++ b/cmd/synergys/resource.h @@ -14,13 +14,14 @@ #define IDC_TASKBAR_STATUS_CLIENTS 1001 #define IDC_TASKBAR_QUIT 40003 #define IDC_TASKBAR_STATUS 40004 +#define IDC_TASKBAR_LOG 40005 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 109 -#define _APS_NEXT_COMMAND_VALUE 40005 +#define _APS_NEXT_COMMAND_VALUE 40006 #define _APS_NEXT_CONTROL_VALUE 1003 #define _APS_NEXT_SYMED_VALUE 101 #endif diff --git a/cmd/synergys/synergys.cpp b/cmd/synergys/synergys.cpp index 8cf18f88..e3fb74de 100644 --- a/cmd/synergys/synergys.cpp +++ b/cmd/synergys/synergys.cpp @@ -782,9 +782,14 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // send PRINT and FATAL output to a message box CLOG->insert(new CMessageBoxOutputter); + // save log messages + CBufferedLogOutputter logBuffer(1000); + CLOG->insert(&logBuffer, true); + // make the task bar receiver. the user can control this app // through the task bar. - s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance); + s_taskBarReceiver = new CMSWindowsServerTaskBarReceiver(instance, + &logBuffer); s_taskBarReceiver->setQuitJob(new CQuitJob(CThread::getCurrentThread())); int result; @@ -803,6 +808,9 @@ WinMain(HINSTANCE instance, HINSTANCE, LPSTR, int) // done with task bar receiver delete s_taskBarReceiver; + // done with log buffer + CLOG->remove(&logBuffer); + // let user examine any messages if we're running as a backend // by putting up a dialog box before exiting. if (ARG->m_backend && s_hasImportantLogMessages) { diff --git a/cmd/synergys/synergys.rc b/cmd/synergys/synergys.rc index 27e4484d..9f190754 100644 --- a/cmd/synergys/synergys.rc +++ b/cmd/synergys/synergys.rc @@ -70,6 +70,7 @@ BEGIN POPUP "Synergy" BEGIN MENUITEM "Show Status", IDC_TASKBAR_STATUS + MENUITEM "Copy Log To Clipboard", IDC_TASKBAR_LOG MENUITEM SEPARATOR MENUITEM "Quit", IDC_TASKBAR_QUIT END diff --git a/lib/base/CLog.cpp b/lib/base/CLog.cpp index 6b504993..4df3058b 100644 --- a/lib/base/CLog.cpp +++ b/lib/base/CLog.cpp @@ -91,6 +91,10 @@ CLog::~CLog() index != m_outputters.end(); ++index) { delete *index; } + for (COutputterList::iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); ++index) { + delete *index; + } ARCH->closeMutex(m_mutex); s_log = NULL; } @@ -189,13 +193,18 @@ CLog::printt(const char* file, int line, const char* fmt, ...) const } void -CLog::insert(ILogOutputter* outputter) +CLog::insert(ILogOutputter* outputter, bool alwaysAtHead) { assert(outputter != NULL); assert(outputter->getNewline() != NULL); CLogLock lock(m_mutex); - m_outputters.push_front(outputter); + if (alwaysAtHead) { + m_alwaysOutputters.push_front(outputter); + } + else { + m_outputters.push_front(outputter); + } int newlineLength = strlen(outputter->getNewline()); if (newlineLength > m_maxNewlineLength) { m_maxNewlineLength = newlineLength; @@ -207,15 +216,17 @@ CLog::remove(ILogOutputter* outputter) { CLogLock lock(m_mutex); m_outputters.remove(outputter); + m_alwaysOutputters.remove(outputter); } void -CLog::pop_front() +CLog::pop_front(bool alwaysAtHead) { CLogLock lock(m_mutex); - if (!m_outputters.empty()) { - delete m_outputters.front(); - m_outputters.pop_front(); + COutputterList* list = alwaysAtHead ? &m_alwaysOutputters : &m_outputters; + if (!list->empty()) { + delete list->front(); + list->pop_front(); } } @@ -266,6 +277,22 @@ CLog::output(int priority, char* msg) const // write to each outputter CLogLock lock(m_mutex); + for (COutputterList::const_iterator index = m_alwaysOutputters.begin(); + index != m_alwaysOutputters.end(); + ++index) { + // get outputter + ILogOutputter* outputter = *index; + + // put an appropriate newline at the end + strcat(msg + g_priorityPad, outputter->getNewline()); + + // open the outputter + outputter->open(kApplication); + + // write message + outputter->write(static_cast(priority), + msg + g_maxPriorityLength - n); + } for (COutputterList::const_iterator index = m_outputters.begin(); index != m_outputters.end(); ++index) { // get outputter diff --git a/lib/base/CLog.h b/lib/base/CLog.h index 88feea92..b25e0ba6 100644 --- a/lib/base/CLog.h +++ b/lib/base/CLog.h @@ -61,12 +61,15 @@ public: true then it also goes to the next outputter, as so on until an outputter returns false or there are no more outputters. Outputters still in the outputter list when the log is destroyed will be - deleted. + deleted. If \c alwaysAtHead is true then the outputter is always + called before all outputters with \c alwaysAtHead false and the + return value of the outputter is ignored. By default, the logger has one outputter installed which writes to the console. */ - void insert(ILogOutputter* adopted); + void insert(ILogOutputter* adopted, + bool alwaysAtHead = false); //! Remove an outputter from the list /*! @@ -79,9 +82,10 @@ public: //! Remove the outputter from the head of the list /*! Removes and deletes the outputter at the head of the outputter list. - This does nothing if the outputter list is empty. + This does nothing if the outputter list is empty. Only removes + outputters that were inserted with the matching \c alwaysAtHead. */ - void pop_front(); + void pop_front(bool alwaysAtHead = false); //! Set the minimum priority filter. /*! @@ -132,6 +136,7 @@ private: CArchMutex m_mutex; COutputterList m_outputters; + COutputterList m_alwaysOutputters; int m_maxNewlineLength; int m_maxPriority; }; diff --git a/lib/base/LogOutputters.cpp b/lib/base/LogOutputters.cpp index f8e0b65e..5f7a40ba 100644 --- a/lib/base/LogOutputters.cpp +++ b/lib/base/LogOutputters.cpp @@ -179,3 +179,60 @@ CSystemLogger::~CSystemLogger() delete m_stop; delete m_syslog; } + + +// +// CBufferedLogOutputter +// + +CBufferedLogOutputter::CBufferedLogOutputter(UInt32 maxBufferSize) : + m_maxBufferSize(maxBufferSize) +{ + // do nothing +} + +CBufferedLogOutputter::~CBufferedLogOutputter() +{ + // do nothing +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::begin() const +{ + return m_buffer.begin(); +} + +CBufferedLogOutputter::const_iterator +CBufferedLogOutputter::end() const +{ + return m_buffer.end(); +} + +void +CBufferedLogOutputter::open(const char*) +{ + // do nothing +} + +void +CBufferedLogOutputter::close() +{ + // remove all elements from the buffer + m_buffer.clear(); +} + +bool +CBufferedLogOutputter::write(ELevel, const char* message) +{ + while (m_buffer.size() >= m_maxBufferSize) { + m_buffer.pop_front(); + } + m_buffer.push_back(CString(message)); + return true; +} + +const char* +CBufferedLogOutputter::getNewline() const +{ + return ""; +} diff --git a/lib/base/LogOutputters.h b/lib/base/LogOutputters.h index 21602438..64befe5f 100644 --- a/lib/base/LogOutputters.h +++ b/lib/base/LogOutputters.h @@ -15,7 +15,10 @@ #ifndef LOGOUTPUTTERS_H #define LOGOUTPUTTERS_H +#include "BasicTypes.h" #include "ILogOutputter.h" +#include "CString.h" +#include "stddeque.h" //! Stop traversing log chain outputter /*! @@ -86,4 +89,40 @@ private: ILogOutputter* m_stop; }; +//! Save log history +/*! +This outputter records the last N log messages. +*/ +class CBufferedLogOutputter : public ILogOutputter { +private: + typedef std::deque CBuffer; + +public: + typedef CBuffer::const_iterator const_iterator; + + CBufferedLogOutputter(UInt32 maxBufferSize); + virtual ~CBufferedLogOutputter(); + + //! @name accessors + //@{ + + //! Get start of buffer + const_iterator begin() const; + + //! Get end of buffer + const_iterator end() const; + + //@} + + // ILogOutputter overrides + virtual void open(const char* title); + virtual void close(); + virtual bool write(ELevel level, const char* message); + virtual const char* getNewline() const; + +private: + UInt32 m_maxBufferSize; + CBuffer m_buffer; +}; + #endif diff --git a/lib/common/common.dsp b/lib/common/common.dsp index df6b77b4..38e1a686 100644 --- a/lib/common/common.dsp +++ b/lib/common/common.dsp @@ -107,6 +107,10 @@ SOURCE=.\stdbitset.h # End Source File # Begin Source File +SOURCE=.\stddeque.h +# End Source File +# Begin Source File + SOURCE=.\stdfstream.h # End Source File # Begin Source File diff --git a/lib/common/stddeque.h b/lib/common/stddeque.h new file mode 100644 index 00000000..06bdafd1 --- /dev/null +++ b/lib/common/stddeque.h @@ -0,0 +1,17 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2002 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 "stdpre.h" +#include +#include "stdpost.h"