vcl: allow for overriding the default PDF rendering resolution
[LibreOffice.git] / sdext / source / presenter / PresenterTimer.cxx
blob55916b1bae38a5519880309742f489ac65af2df2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "PresenterTimer.hxx"
22 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
23 #include <com/sun/star/frame/Desktop.hpp>
24 #include <com/sun/star/frame/XTerminateListener.hpp>
26 #include <osl/thread.hxx>
27 #include <osl/conditn.hxx>
29 #include <algorithm>
30 #include <iterator>
31 #include <memory>
32 #include <set>
34 using namespace ::com::sun::star;
35 using namespace ::com::sun::star::uno;
37 namespace sdext { namespace presenter {
39 namespace {
40 class TimerTask
42 public:
43 TimerTask (
44 const PresenterTimer::Task& rTask,
45 const TimeValue& rDueTime,
46 const sal_Int64 nRepeatInterval,
47 const sal_Int32 nTaskId);
49 PresenterTimer::Task const maTask;
50 TimeValue maDueTime;
51 const sal_Int64 mnRepeatInterval;
52 const sal_Int32 mnTaskId;
53 bool mbIsCanceled;
56 typedef std::shared_ptr<TimerTask> SharedTimerTask;
58 class TimerTaskComparator
60 public:
61 bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const
63 return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds
64 || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds
65 && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec);
69 /** Queue all scheduled tasks and process them when their time has come.
71 class TimerScheduler
72 : public std::enable_shared_from_this<TimerScheduler>,
73 public ::osl::Thread
75 public:
76 static std::shared_ptr<TimerScheduler> Instance(
77 uno::Reference<uno::XComponentContext> const& xContext);
78 static SharedTimerTask CreateTimerTask (
79 const PresenterTimer::Task& rTask,
80 const TimeValue& rDueTime,
81 const sal_Int64 nRepeatInterval);
83 void ScheduleTask (const SharedTimerTask& rpTask);
84 void CancelTask (const sal_Int32 nTaskId);
86 static bool GetCurrentTime (TimeValue& rCurrentTime);
87 static sal_Int64 GetTimeDifference (
88 const TimeValue& rTargetTime,
89 const TimeValue& rCurrentTime);
90 static void ConvertToTimeValue (
91 TimeValue& rTimeValue,
92 const sal_Int64 nTimeDifference);
93 static sal_Int64 ConvertFromTimeValue (
94 const TimeValue& rTimeValue);
96 static void NotifyTermination();
97 #if !defined NDEBUG
98 static bool HasInstance() { return mpInstance != nullptr; }
99 #endif
101 private:
102 static std::shared_ptr<TimerScheduler> mpInstance;
103 static ::osl::Mutex maInstanceMutex;
104 std::shared_ptr<TimerScheduler> mpLateDestroy; // for clean exit
105 static sal_Int32 mnTaskId;
107 ::osl::Mutex maTaskContainerMutex;
108 typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer;
109 TaskContainer maScheduledTasks;
110 ::osl::Mutex maCurrentTaskMutex;
111 SharedTimerTask mpCurrentTask;
112 ::osl::Condition m_Shutdown;
114 TimerScheduler(
115 uno::Reference<uno::XComponentContext> const& xContext);
116 public:
117 virtual void SAL_CALL run() override;
118 virtual void SAL_CALL onTerminated() override { mpLateDestroy.reset(); }
121 class TerminateListener
122 : public ::cppu::WeakImplHelper<frame::XTerminateListener>
124 virtual ~TerminateListener() override
126 assert(!TimerScheduler::HasInstance());
129 virtual void SAL_CALL disposing(lang::EventObject const&) override
133 virtual void SAL_CALL queryTermination(lang::EventObject const&) override
137 virtual void SAL_CALL notifyTermination(lang::EventObject const&) override
139 TimerScheduler::NotifyTermination();
143 } // end of anonymous namespace
145 //===== PresenterTimer ========================================================
147 sal_Int32 PresenterTimer::ScheduleRepeatedTask (
148 const uno::Reference<uno::XComponentContext>& xContext,
149 const Task& rTask,
150 const sal_Int64 nDelay,
151 const sal_Int64 nInterval)
153 assert(xContext.is());
154 TimeValue aCurrentTime;
155 if (TimerScheduler::GetCurrentTime(aCurrentTime))
157 TimeValue aDueTime;
158 TimerScheduler::ConvertToTimeValue(
159 aDueTime,
160 TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay);
161 SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nInterval));
162 TimerScheduler::Instance(xContext)->ScheduleTask(pTask);
163 return pTask->mnTaskId;
166 return NotAValidTaskId;
169 void PresenterTimer::CancelTask (const sal_Int32 nTaskId)
171 auto const pInstance(TimerScheduler::Instance(nullptr));
172 if (pInstance)
174 pInstance->CancelTask(nTaskId);
178 //===== TimerScheduler ========================================================
180 std::shared_ptr<TimerScheduler> TimerScheduler::mpInstance;
181 ::osl::Mutex TimerScheduler::maInstanceMutex;
182 sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId;
184 std::shared_ptr<TimerScheduler> TimerScheduler::Instance(
185 uno::Reference<uno::XComponentContext> const& xContext)
187 ::osl::MutexGuard aGuard (maInstanceMutex);
188 if (mpInstance == nullptr)
190 if (!xContext.is())
191 return nullptr;
192 mpInstance.reset(new TimerScheduler(xContext));
193 mpInstance->create();
195 return mpInstance;
198 TimerScheduler::TimerScheduler(
199 uno::Reference<uno::XComponentContext> const& xContext)
200 : maTaskContainerMutex(),
201 maScheduledTasks(),
202 maCurrentTaskMutex(),
203 mpCurrentTask()
205 uno::Reference<frame::XDesktop> const xDesktop(
206 frame::Desktop::create(xContext));
207 uno::Reference<frame::XTerminateListener> const xListener(
208 new TerminateListener);
209 // assuming the desktop can take ownership
210 xDesktop->addTerminateListener(xListener);
213 SharedTimerTask TimerScheduler::CreateTimerTask (
214 const PresenterTimer::Task& rTask,
215 const TimeValue& rDueTime,
216 const sal_Int64 nRepeatInterval)
218 return std::make_shared<TimerTask>(rTask, rDueTime, nRepeatInterval, ++mnTaskId);
221 void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask)
223 if (rpTask.get() == nullptr)
224 return;
225 if (rpTask->mbIsCanceled)
226 return;
229 osl::MutexGuard aTaskGuard (maTaskContainerMutex);
230 maScheduledTasks.insert(rpTask);
234 void TimerScheduler::CancelTask (const sal_Int32 nTaskId)
236 // Set of scheduled tasks is sorted after their due times, not their
237 // task ids. Therefore we have to do a linear search for the task to
238 // cancel.
240 ::osl::MutexGuard aGuard (maTaskContainerMutex);
241 auto iTask = std::find_if(maScheduledTasks.begin(), maScheduledTasks.end(),
242 [nTaskId](const SharedTimerTask& rxTask) { return rxTask->mnTaskId == nTaskId; });
243 if (iTask != maScheduledTasks.end())
244 maScheduledTasks.erase(iTask);
247 // The task that is to be canceled may be currently about to be
248 // processed. Mark it with a flag that a) prevents a repeating task
249 // from being scheduled again and b) tries to prevent its execution.
251 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
252 if (mpCurrentTask.get() != nullptr
253 && mpCurrentTask->mnTaskId == nTaskId)
254 mpCurrentTask->mbIsCanceled = true;
257 // Let the main-loop cleanup in its own time
260 void TimerScheduler::NotifyTermination()
262 std::shared_ptr<TimerScheduler> const pInstance(TimerScheduler::mpInstance);
263 if (!pInstance)
265 return;
269 ::osl::MutexGuard aGuard(pInstance->maTaskContainerMutex);
270 pInstance->maScheduledTasks.clear();
274 ::osl::MutexGuard aGuard(pInstance->maCurrentTaskMutex);
275 if (pInstance->mpCurrentTask)
277 pInstance->mpCurrentTask->mbIsCanceled = true;
281 pInstance->m_Shutdown.set();
283 // rhbz#1425304 join thread before shutdown
284 pInstance->join();
287 void SAL_CALL TimerScheduler::run()
289 osl_setThreadName("sdext::presenter::TimerScheduler");
291 while (true)
293 // Get the current time.
294 TimeValue aCurrentTime;
295 if ( ! GetCurrentTime(aCurrentTime))
297 // We can not get the current time and thus can not schedule anything.
298 break;
301 // Restrict access to the maScheduledTasks member to one, mutex
302 // guarded, block.
303 SharedTimerTask pTask;
304 sal_Int64 nDifference = 0;
306 ::osl::MutexGuard aGuard (maTaskContainerMutex);
308 // There are no more scheduled task. Leave this loop, function and
309 // live of the TimerScheduler.
310 if (maScheduledTasks.empty())
311 break;
313 nDifference = GetTimeDifference(
314 (*maScheduledTasks.begin())->maDueTime,
315 aCurrentTime);
316 if (nDifference <= 0)
318 pTask = *maScheduledTasks.begin();
319 maScheduledTasks.erase(maScheduledTasks.begin());
323 // Acquire a reference to the current task.
325 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
326 mpCurrentTask = pTask;
329 if (pTask.get() == nullptr)
331 // Wait until the first task becomes due.
332 TimeValue aTimeValue;
333 ConvertToTimeValue(aTimeValue, nDifference);
334 // wait on condition variable, so the thread can be stopped
335 m_Shutdown.wait(&aTimeValue);
337 else
339 // Execute task.
340 if (pTask->maTask && !pTask->mbIsCanceled)
342 pTask->maTask(aCurrentTime);
344 // Re-schedule repeating tasks.
345 if (pTask->mnRepeatInterval > 0)
347 ConvertToTimeValue(
348 pTask->maDueTime,
349 ConvertFromTimeValue(pTask->maDueTime)
350 + pTask->mnRepeatInterval);
351 ScheduleTask(pTask);
357 // Release reference to the current task.
359 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
360 mpCurrentTask.reset();
364 // While holding maInstanceMutex
365 osl::Guard< osl::Mutex > aInstance( maInstanceMutex );
366 mpLateDestroy = mpInstance;
367 mpInstance.reset();
370 bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
372 TimeValue aSystemTime;
373 if (osl_getSystemTime(&aSystemTime))
374 return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
375 return false;
378 sal_Int64 TimerScheduler::GetTimeDifference (
379 const TimeValue& rTargetTime,
380 const TimeValue& rCurrentTime)
382 return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
385 void TimerScheduler::ConvertToTimeValue (
386 TimeValue& rTimeValue,
387 const sal_Int64 nTimeDifference)
389 rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
390 rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
393 sal_Int64 TimerScheduler::ConvertFromTimeValue (
394 const TimeValue& rTimeValue)
396 return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
399 //===== TimerTask =============================================================
401 namespace {
403 TimerTask::TimerTask (
404 const PresenterTimer::Task& rTask,
405 const TimeValue& rDueTime,
406 const sal_Int64 nRepeatInterval,
407 const sal_Int32 nTaskId)
408 : maTask(rTask),
409 maDueTime(rDueTime),
410 mnRepeatInterval(nRepeatInterval),
411 mnTaskId(nTaskId),
412 mbIsCanceled(false)
416 } // end of anonymous namespace
418 //===== PresenterTimer ========================================================
420 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance;
422 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance (
423 const css::uno::Reference<css::uno::XComponentContext>& rxContext)
425 ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
427 ::rtl::Reference<PresenterClockTimer> pTimer;
428 if (mpInstance.is())
430 pTimer = mpInstance;
432 if ( ! pTimer.is())
434 pTimer.set(new PresenterClockTimer(rxContext));
435 mpInstance = pTimer;
437 return pTimer;
440 PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
441 : PresenterClockTimerInterfaceBase(m_aMutex),
442 maListeners(),
443 maDateTime(),
444 mnTimerTaskId(PresenterTimer::NotAValidTaskId),
445 mbIsCallbackPending(false),
446 mxRequestCallback()
447 , m_xContext(rxContext)
449 assert(m_xContext.is());
450 Reference<lang::XMultiComponentFactory> xFactory =
451 rxContext->getServiceManager();
452 if (xFactory.is())
453 mxRequestCallback.set(
454 xFactory->createInstanceWithContext(
455 "com.sun.star.awt.AsyncCallback",
456 rxContext),
457 UNO_QUERY_THROW);
460 PresenterClockTimer::~PresenterClockTimer()
462 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
464 PresenterTimer::CancelTask(mnTimerTaskId);
465 mnTimerTaskId = PresenterTimer::NotAValidTaskId;
468 Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
469 if (xComponent.is())
470 xComponent->dispose();
471 mxRequestCallback = nullptr;
474 void PresenterClockTimer::AddListener (const SharedListener& rListener)
476 osl::MutexGuard aGuard (maMutex);
478 maListeners.push_back(rListener);
480 // Create a timer task when the first listener is added.
481 if (mnTimerTaskId==PresenterTimer::NotAValidTaskId)
483 mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask(
484 m_xContext,
485 [this] (TimeValue const& rTime) { return this->CheckCurrentTime(rTime); },
487 250000000 /*ns*/);
491 void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
493 osl::MutexGuard aGuard (maMutex);
495 ListenerContainer::iterator iListener (::std::find(
496 maListeners.begin(),
497 maListeners.end(),
498 rListener));
499 if (iListener != maListeners.end())
500 maListeners.erase(iListener);
501 if (maListeners.empty())
503 // We have no more clients and therefore are not interested in time changes.
504 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
506 PresenterTimer::CancelTask(mnTimerTaskId);
507 mnTimerTaskId = PresenterTimer::NotAValidTaskId;
509 mpInstance = nullptr;
513 oslDateTime PresenterClockTimer::GetCurrentTime()
515 TimeValue aCurrentTime;
516 TimerScheduler::GetCurrentTime(aCurrentTime);
517 oslDateTime aDateTime;
518 osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
519 return aDateTime;
522 void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
524 css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
525 css::uno::Reference<css::awt::XCallback> xCallback;
527 osl::MutexGuard aGuard (maMutex);
529 TimeValue aCurrentTime (rCurrentTime);
530 oslDateTime aDateTime;
531 if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
533 if (aDateTime.Seconds != maDateTime.Seconds
534 || aDateTime.Minutes != maDateTime.Minutes
535 || aDateTime.Hours != maDateTime.Hours)
537 // The displayed part of the current time has changed.
538 // Prepare to call the listeners.
539 maDateTime = aDateTime;
541 // Schedule notification of listeners.
542 if (mxRequestCallback.is() && ! mbIsCallbackPending)
544 mbIsCallbackPending = true;
545 xRequestCallback = mxRequestCallback;
546 xCallback = this;
551 if (xRequestCallback.is() && xCallback.is())
552 xRequestCallback->addCallback(xCallback, Any());
555 //----- XCallback -------------------------------------------------------------
557 void SAL_CALL PresenterClockTimer::notify (const css::uno::Any&)
559 ListenerContainer aListenerCopy (maListeners);
562 osl::MutexGuard aGuard (maMutex);
564 mbIsCallbackPending = false;
566 ::std::copy(
567 maListeners.begin(),
568 maListeners.end(),
569 ::std::back_inserter(aListenerCopy));
572 for (const auto& rxListener : aListenerCopy)
574 rxListener->TimeHasChanged(maDateTime);
578 } } // end of namespace ::sdext::presenter
580 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */