diff options
Diffstat (limited to 'sdext/source/presenter/PresenterTimer.cxx')
-rw-r--r-- | sdext/source/presenter/PresenterTimer.cxx | 681 |
1 files changed, 681 insertions, 0 deletions
diff --git a/sdext/source/presenter/PresenterTimer.cxx b/sdext/source/presenter/PresenterTimer.cxx new file mode 100644 index 000000000000..f4a5618353c9 --- /dev/null +++ b/sdext/source/presenter/PresenterTimer.cxx @@ -0,0 +1,681 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org 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 Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_sdext.hxx" + +#include "PresenterTimer.hxx" +#include <osl/doublecheckedlocking.h> +#include <osl/thread.hxx> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <set> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +#define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString))) + +namespace sdext { namespace presenter { + +namespace { +class TimerTask +{ +public: + TimerTask ( + const PresenterTimer::Task& rTask, + const TimeValue& rDueTime, + const sal_Int64 nRepeatIntervall, + const sal_Int32 nTaskId); + ~TimerTask (void) {} + + PresenterTimer::Task maTask; + TimeValue maDueTime; + const sal_Int64 mnRepeatIntervall; + const sal_Int32 mnTaskId; + bool mbIsCanceled; +}; + +typedef ::boost::shared_ptr<TimerTask> SharedTimerTask; + + +class TimerTaskComparator +{ +public: + bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const + { + return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds + || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds + && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec); + } +}; + + + + +/** Queue all scheduled tasks and process them when their time has come. +*/ +class TimerScheduler + : public ::boost::enable_shared_from_this<TimerScheduler>, + public ::osl::Thread +{ +public: + static ::boost::shared_ptr<TimerScheduler> Instance (void); + static SharedTimerTask CreateTimerTask ( + const PresenterTimer::Task& rTask, + const TimeValue& rDueTime, + const sal_Int64 nRepeatIntervall); + + void ScheduleTask (const SharedTimerTask& rpTask); + void CancelTask (const sal_Int32 nTaskId); + + static bool GetCurrentTime (TimeValue& rCurrentTime); + static sal_Int64 GetTimeDifference ( + const TimeValue& rTargetTime, + const TimeValue& rCurrentTime); + static void ConvertToTimeValue ( + TimeValue& rTimeValue, + const sal_Int64 nTimeDifference); + static sal_Int64 ConvertFromTimeValue ( + const TimeValue& rTimeValue); + +private: + static ::boost::shared_ptr<TimerScheduler> mpInstance; + static ::osl::Mutex maInstanceMutex; + static sal_Int32 mnTaskId; + + ::osl::Mutex maTaskContainerMutex; + typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer; + TaskContainer maScheduledTasks; + bool mbIsRunning; + ::osl::Mutex maCurrentTaskMutex; + SharedTimerTask mpCurrentTask; + + static void Release (void); + + TimerScheduler (void); + virtual ~TimerScheduler (void); + class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } }; + friend class Deleter; + + virtual void SAL_CALL run (void); + virtual void SAL_CALL onTerminated (void); +}; + + + + +bool GetDateTime (oslDateTime& rDateTime); +} // end of anonymous namespace + + +//===== PresenterTimer ======================================================== + +sal_Int32 PresenterTimer::ScheduleSingleTaskRelative ( + const Task& rTask, + const sal_Int64 nDelay) +{ + return ScheduleRepeatedTask(rTask, nDelay, 0); +} + + + + +sal_Int32 PresenterTimer::ScheduleSingleTaskAbsolute ( + const Task& rTask, + const TimeValue& rDueTime) +{ + SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, rDueTime, 0)); + TimerScheduler::Instance()->ScheduleTask(pTask); + return pTask->mnTaskId; +} + + + + +sal_Int32 PresenterTimer::ScheduleRepeatedTask ( + const Task& rTask, + const sal_Int64 nDelay, + const sal_Int64 nIntervall) +{ + TimeValue aCurrentTime; + if (TimerScheduler::GetCurrentTime(aCurrentTime)) + { + TimeValue aDueTime; + TimerScheduler::ConvertToTimeValue( + aDueTime, + TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay); + SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nIntervall)); + TimerScheduler::Instance()->ScheduleTask(pTask); + return pTask->mnTaskId; + } + + return NotAValidTaskId; +} + + + + +void PresenterTimer::CancelTask (const sal_Int32 nTaskId) +{ + return TimerScheduler::Instance()->CancelTask(nTaskId); +} + + + + +//===== TimerScheduler ======================================================== + +::boost::shared_ptr<TimerScheduler> TimerScheduler::mpInstance; +::osl::Mutex TimerScheduler::maInstanceMutex; +sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId; + +::boost::shared_ptr<TimerScheduler> TimerScheduler::Instance (void) +{ + ::boost::shared_ptr<TimerScheduler> pInstance = mpInstance; + if (pInstance.get() == NULL) + { + ::osl::MutexGuard aGuard (maInstanceMutex); + pInstance = mpInstance; + if (pInstance.get() == NULL) + { + pInstance.reset(new TimerScheduler(), TimerScheduler::Deleter()); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + mpInstance = pInstance; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + return pInstance; +} + + + + +void TimerScheduler::Release (void) +{ + ::osl::MutexGuard aGuard (maInstanceMutex); + mpInstance.reset(); +} + + + + +TimerScheduler::TimerScheduler (void) + : maTaskContainerMutex(), + maScheduledTasks(), + mbIsRunning(false), + maCurrentTaskMutex(), + mpCurrentTask() +{ +} + + + + +TimerScheduler::~TimerScheduler (void) +{ +} + + + +SharedTimerTask TimerScheduler::CreateTimerTask ( + const PresenterTimer::Task& rTask, + const TimeValue& rDueTime, + const sal_Int64 nRepeatIntervall) +{ + return SharedTimerTask(new TimerTask(rTask, rDueTime, nRepeatIntervall, ++mnTaskId)); +} + + + + +void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask) +{ + if (rpTask.get() == NULL) + return; + if (rpTask->mbIsCanceled) + return; + + osl::MutexGuard aGuard (maTaskContainerMutex); + maScheduledTasks.insert(rpTask); + + if ( ! mbIsRunning) + { + mbIsRunning = true; + create(); + } +} + + + + +void TimerScheduler::CancelTask (const sal_Int32 nTaskId) +{ + // Set of scheduled tasks is sorted after their due times, not their + // task ids. Therefore we have to do a linear search for the task to + // cancel. + { + ::osl::MutexGuard aGuard (maTaskContainerMutex); + TaskContainer::iterator iTask (maScheduledTasks.begin()); + TaskContainer::const_iterator iEnd (maScheduledTasks.end()); + for ( ; iTask!=iEnd; ++iTask) + { + if ((*iTask)->mnTaskId == nTaskId) + { + maScheduledTasks.erase(iTask); + break; + } + } + } + + // The task that is to be canceled may be currently about to be + // processed. Mark it with a flag that a) prevents a repeating task + // from being scheduled again and b) tries to prevent its execution. + if (mpCurrentTask.get() != NULL + && mpCurrentTask->mnTaskId == nTaskId) + { + mpCurrentTask->mbIsCanceled = true; + } + + // When the last active task was canceled then the timer can be + // stopped. + if (maScheduledTasks.size() == 0) + { + mbIsRunning = false; + resume(); + // join(); + } +} + + + + +void SAL_CALL TimerScheduler::run (void) +{ + while (mbIsRunning) + { + // Get the current time. + TimeValue aCurrentTime; + if ( ! GetCurrentTime(aCurrentTime)) + { + // We can not get the current time and thus can not schedule anything. + break; + } + + // Restrict access to the maScheduledTasks member to one, mutext + // guarded, block. + SharedTimerTask pTask; + sal_Int64 nDifference = 0; + { + ::osl::MutexGuard aGuard (maTaskContainerMutex); + + // There are no more scheduled task. Leave this loop, function and + // live of the TimerScheduler. + if (maScheduledTasks.empty()) + break; + + nDifference = GetTimeDifference( + (*maScheduledTasks.begin())->maDueTime, + aCurrentTime); + if (nDifference <= 0) + { + pTask = *maScheduledTasks.begin(); + maScheduledTasks.erase(maScheduledTasks.begin()); + } + } + + // Acquire a reference to the current task. + { + ::osl::MutexGuard aGuard (maCurrentTaskMutex); + mpCurrentTask = pTask; + } + + if (mpCurrentTask.get() == NULL) + { + // Wait until the first task becomes due. + TimeValue aTimeValue; + ConvertToTimeValue(aTimeValue, nDifference); + wait(aTimeValue); + } + else + { + // Execute task. + if ( ! mpCurrentTask->maTask.empty() + && ! mpCurrentTask->mbIsCanceled) + { + mpCurrentTask->maTask(aCurrentTime); + + // Re-schedule repeating tasks. + if (mpCurrentTask->mnRepeatIntervall > 0) + { + ConvertToTimeValue( + mpCurrentTask->maDueTime, + ConvertFromTimeValue(mpCurrentTask->maDueTime) + + mpCurrentTask->mnRepeatIntervall); + ScheduleTask(mpCurrentTask); + } + } + + } + + // Release reference to the current task. + { + ::osl::MutexGuard aGuard (maCurrentTaskMutex); + mpCurrentTask.reset(); + } + } +} + + + + +void SAL_CALL TimerScheduler::onTerminated (void) +{ + Release(); +} + + + + +bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime) +{ + TimeValue aSystemTime; + if (osl_getSystemTime(&aSystemTime)) + return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime); + return false; +} + + + + +sal_Int64 TimerScheduler::GetTimeDifference ( + const TimeValue& rTargetTime, + const TimeValue& rCurrentTime) +{ + return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime); +} + + + + +void TimerScheduler::ConvertToTimeValue ( + TimeValue& rTimeValue, + const sal_Int64 nTimeDifference) +{ + rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L); + rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L); +} + + + + +sal_Int64 TimerScheduler::ConvertFromTimeValue ( + const TimeValue& rTimeValue) +{ + return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec; +} + + + + +//===== TimerTask ============================================================= + +namespace { + +TimerTask::TimerTask ( + const PresenterTimer::Task& rTask, + const TimeValue& rDueTime, + const sal_Int64 nRepeatIntervall, + const sal_Int32 nTaskId) + : maTask(rTask), + maDueTime(rDueTime), + mnRepeatIntervall(nRepeatIntervall), + mnTaskId(nTaskId), + mbIsCanceled(false) +{ +} + +} // end of anonymous namespace + + + + +//===== PresenterTimer ======================================================== + + +::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance; + +::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance ( + const css::uno::Reference<css::uno::XComponentContext>& rxContext) +{ + ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex()); + + ::rtl::Reference<PresenterClockTimer> pTimer; + if (mpInstance.is()) + { + pTimer = mpInstance; + } + if ( ! pTimer.is()) + { + pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext)); + mpInstance = pTimer; + } + return pTimer; +} + + + + +PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext) + : PresenterClockTimerInterfaceBase(m_aMutex), + maListeners(), + maDateTime(), + mnTimerTaskId(PresenterTimer::NotAValidTaskId), + mbIsCallbackPending(false), + mxRequestCallback() +{ + Reference<lang::XMultiComponentFactory> xFactory ( + rxContext->getServiceManager(), UNO_QUERY); + if (xFactory.is()) + mxRequestCallback = Reference<awt::XRequestCallback>( + xFactory->createInstanceWithContext( + A2S("com.sun.star.awt.AsyncCallback"), + rxContext), + UNO_QUERY_THROW); +} + + + + +PresenterClockTimer::~PresenterClockTimer (void) +{ + if (mnTimerTaskId != PresenterTimer::NotAValidTaskId) + { + PresenterTimer::CancelTask(mnTimerTaskId); + mnTimerTaskId = PresenterTimer::NotAValidTaskId; + } + + Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + mxRequestCallback = NULL; +} + + + + +void PresenterClockTimer::AddListener (const SharedListener& rListener) +{ + osl::MutexGuard aGuard (maMutex); + + maListeners.push_back(rListener); + + // Create a timer task when the first listener is added. + if (mnTimerTaskId==PresenterTimer::NotAValidTaskId) + { + mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask( + ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1), + 0, + 250000000 /*ns*/); + } +} + + + + +void PresenterClockTimer::RemoveListener (const SharedListener& rListener) +{ + osl::MutexGuard aGuard (maMutex); + + ListenerContainer::iterator iListener (::std::find( + maListeners.begin(), + maListeners.end(), + rListener)); + if (iListener != maListeners.end()) + maListeners.erase(iListener); + if (maListeners.size() == 0) + { + // We have no more clients and therefore are not interested in time changes. + if (mnTimerTaskId != PresenterTimer::NotAValidTaskId) + { + PresenterTimer::CancelTask(mnTimerTaskId); + mnTimerTaskId = PresenterTimer::NotAValidTaskId; + } + mpInstance = NULL; + } +} + + + + +oslDateTime PresenterClockTimer::GetCurrentTime (void) +{ + TimeValue aCurrentTime; + TimerScheduler::GetCurrentTime(aCurrentTime); + oslDateTime aDateTime; + osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime); + return aDateTime; +} + + + + +sal_Int64 PresenterClockTimer::GetTimeDifference ( + const oslDateTime& rNow, + const oslDateTime& rThen) +{ + TimeValue aNow; + TimeValue aThen; + if (osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rNow),&aNow) + && osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rThen),&aThen)) + { + return TimerScheduler::GetTimeDifference(aNow, aThen); + } + else + return -1; +} + + + + +void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime) +{ + css::uno::Reference<css::awt::XRequestCallback> xRequestCallback; + css::uno::Reference<css::awt::XCallback> xCallback; + { + osl::MutexGuard aGuard (maMutex); + + TimeValue aCurrentTime (rCurrentTime); + oslDateTime aDateTime; + if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime)) + { + if (aDateTime.Seconds != maDateTime.Seconds + || aDateTime.Minutes != maDateTime.Minutes + || aDateTime.Seconds != maDateTime.Seconds) + { + // The displayed part of the current time has changed. + // Prepare to call the listeners. + maDateTime = aDateTime; + + // Schedule notification of listeners. + if (mxRequestCallback.is() && ! mbIsCallbackPending) + { + mbIsCallbackPending = true; + xRequestCallback = mxRequestCallback; + xCallback = this; + } + } + } + } + if (mxRequestCallback.is() && xCallback.is()) + xRequestCallback->addCallback(xCallback, Any()); +} + + + + +//----- XCallback ------------------------------------------------------------- + +void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData) + throw (css::uno::RuntimeException) +{ + (void)rUserData; + + ListenerContainer aListenerCopy (maListeners); + + { + osl::MutexGuard aGuard (maMutex); + + mbIsCallbackPending = false; + + ::std::copy( + maListeners.begin(), + maListeners.end(), + ::std::back_inserter(aListenerCopy)); + } + + if (aListenerCopy.size() > 0) + { + ListenerContainer::const_iterator iListener; + ListenerContainer::const_iterator iEnd (aListenerCopy.end()); + for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener) + { + (*iListener)->TimeHasChanged(maDateTime); + } + } +} + + + +} } // end of namespace ::sdext::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |