1*70f497fbSAndrew Rist /************************************************************** 2cdf0e10cSrcweir * 3*70f497fbSAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one 4*70f497fbSAndrew Rist * or more contributor license agreements. See the NOTICE file 5*70f497fbSAndrew Rist * distributed with this work for additional information 6*70f497fbSAndrew Rist * regarding copyright ownership. The ASF licenses this file 7*70f497fbSAndrew Rist * to you under the Apache License, Version 2.0 (the 8*70f497fbSAndrew Rist * "License"); you may not use this file except in compliance 9*70f497fbSAndrew Rist * with the License. You may obtain a copy of the License at 10*70f497fbSAndrew Rist * 11*70f497fbSAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0 12*70f497fbSAndrew Rist * 13*70f497fbSAndrew Rist * Unless required by applicable law or agreed to in writing, 14*70f497fbSAndrew Rist * software distributed under the License is distributed on an 15*70f497fbSAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16*70f497fbSAndrew Rist * KIND, either express or implied. See the License for the 17*70f497fbSAndrew Rist * specific language governing permissions and limitations 18*70f497fbSAndrew Rist * under the License. 19*70f497fbSAndrew Rist * 20*70f497fbSAndrew Rist *************************************************************/ 21*70f497fbSAndrew Rist 22*70f497fbSAndrew Rist 23cdf0e10cSrcweir 24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove 25cdf0e10cSrcweir #include "precompiled_slideshow.hxx" 26cdf0e10cSrcweir 27cdf0e10cSrcweir // must be first 28cdf0e10cSrcweir #include <canvas/debug.hxx> 29cdf0e10cSrcweir #include <tools/diagnose_ex.h> 30cdf0e10cSrcweir #include <canvas/verbosetrace.hxx> 31cdf0e10cSrcweir #include "debug.hxx" 32cdf0e10cSrcweir 33cdf0e10cSrcweir #include <comphelper/anytostring.hxx> 34cdf0e10cSrcweir #include <cppuhelper/exc_hlp.hxx> 35cdf0e10cSrcweir 36cdf0e10cSrcweir #include <event.hxx> 37cdf0e10cSrcweir #include <eventqueue.hxx> 38cdf0e10cSrcweir #include <slideshowexceptions.hxx> 39cdf0e10cSrcweir 40cdf0e10cSrcweir #include <boost/shared_ptr.hpp> 41cdf0e10cSrcweir #include <limits> 42cdf0e10cSrcweir 43cdf0e10cSrcweir 44cdf0e10cSrcweir using namespace ::com::sun::star; 45cdf0e10cSrcweir 46cdf0e10cSrcweir namespace slideshow 47cdf0e10cSrcweir { 48cdf0e10cSrcweir namespace internal 49cdf0e10cSrcweir { 50cdf0e10cSrcweir bool EventQueue::EventEntry::operator<( const EventEntry& rEvent ) const 51cdf0e10cSrcweir { 52cdf0e10cSrcweir // negate comparison, we want priority queue to be sorted 53cdf0e10cSrcweir // in increasing order of activation times 54cdf0e10cSrcweir return this->nTime > rEvent.nTime; 55cdf0e10cSrcweir } 56cdf0e10cSrcweir 57cdf0e10cSrcweir 58cdf0e10cSrcweir EventQueue::EventQueue( 59cdf0e10cSrcweir boost::shared_ptr<canvas::tools::ElapsedTime> const & pPresTimer ) 60cdf0e10cSrcweir : maMutex(), 61cdf0e10cSrcweir maEvents(), 62cdf0e10cSrcweir maNextEvents(), 63cdf0e10cSrcweir maNextNextEvents(), 64cdf0e10cSrcweir mpTimer( pPresTimer ) 65cdf0e10cSrcweir { 66cdf0e10cSrcweir } 67cdf0e10cSrcweir 68cdf0e10cSrcweir EventQueue::~EventQueue() 69cdf0e10cSrcweir { 70cdf0e10cSrcweir // add in all that have been added explicitly for this round: 71cdf0e10cSrcweir EventEntryVector::const_iterator const iEnd( maNextEvents.end() ); 72cdf0e10cSrcweir for ( EventEntryVector::const_iterator iPos( maNextEvents.begin() ); 73cdf0e10cSrcweir iPos != iEnd; ++iPos ) 74cdf0e10cSrcweir { 75cdf0e10cSrcweir maEvents.push(*iPos); 76cdf0e10cSrcweir } 77cdf0e10cSrcweir EventEntryVector().swap( maNextEvents ); 78cdf0e10cSrcweir 79cdf0e10cSrcweir // dispose event queue 80cdf0e10cSrcweir while( !maEvents.empty() ) 81cdf0e10cSrcweir { 82cdf0e10cSrcweir try 83cdf0e10cSrcweir { 84cdf0e10cSrcweir maEvents.top().pEvent->dispose(); 85cdf0e10cSrcweir } 86cdf0e10cSrcweir catch (uno::Exception &) 87cdf0e10cSrcweir { 88cdf0e10cSrcweir OSL_ENSURE( false, rtl::OUStringToOString( 89cdf0e10cSrcweir comphelper::anyToString( 90cdf0e10cSrcweir cppu::getCaughtException() ), 91cdf0e10cSrcweir RTL_TEXTENCODING_UTF8 ).getStr() ); 92cdf0e10cSrcweir } 93cdf0e10cSrcweir maEvents.pop(); 94cdf0e10cSrcweir } 95cdf0e10cSrcweir } 96cdf0e10cSrcweir 97cdf0e10cSrcweir bool EventQueue::addEvent( const EventSharedPtr& rEvent ) 98cdf0e10cSrcweir { 99cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 100cdf0e10cSrcweir 101cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) 102cdf0e10cSrcweir OSL_TRACE("adding at %f event [%s] at %x with delay %f\r", 103cdf0e10cSrcweir mpTimer->getElapsedTime(), 104cdf0e10cSrcweir OUStringToOString(rEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(), 105cdf0e10cSrcweir rEvent.get(), 106cdf0e10cSrcweir rEvent->getActivationTime(0.0)); 107cdf0e10cSrcweir #endif 108cdf0e10cSrcweir ENSURE_OR_RETURN_FALSE( rEvent, 109cdf0e10cSrcweir "EventQueue::addEvent: event ptr NULL" ); 110cdf0e10cSrcweir 111cdf0e10cSrcweir // prepare entry 112cdf0e10cSrcweir 113cdf0e10cSrcweir // A seemingly obvious optimization cannot be used here, 114cdf0e10cSrcweir // because it breaks assumed order of notification: zero 115cdf0e10cSrcweir // timeout events could be fired() immediately, but that 116cdf0e10cSrcweir // would not unwind the stack and furthermore changes 117cdf0e10cSrcweir // order of notification 118cdf0e10cSrcweir 119cdf0e10cSrcweir // add entry 120cdf0e10cSrcweir maEvents.push( EventEntry( rEvent, rEvent->getActivationTime( 121cdf0e10cSrcweir mpTimer->getElapsedTime()) ) ); 122cdf0e10cSrcweir return true; 123cdf0e10cSrcweir } 124cdf0e10cSrcweir 125cdf0e10cSrcweir bool EventQueue::addEventForNextRound( EventSharedPtr const& rEvent ) 126cdf0e10cSrcweir { 127cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 128cdf0e10cSrcweir 129cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) 130cdf0e10cSrcweir OSL_TRACE("adding at %f event [%s] at %x for next round with delay %f\r", 131cdf0e10cSrcweir mpTimer->getElapsedTime(), 132cdf0e10cSrcweir OUStringToOString(rEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(), 133cdf0e10cSrcweir rEvent.get(), 134cdf0e10cSrcweir rEvent->getActivationTime(0.0)); 135cdf0e10cSrcweir #endif 136cdf0e10cSrcweir 137cdf0e10cSrcweir ENSURE_OR_RETURN_FALSE( rEvent.get() != NULL, 138cdf0e10cSrcweir "EventQueue::addEvent: event ptr NULL" ); 139cdf0e10cSrcweir maNextEvents.push_back( 140cdf0e10cSrcweir EventEntry( rEvent, rEvent->getActivationTime( 141cdf0e10cSrcweir mpTimer->getElapsedTime()) ) ); 142cdf0e10cSrcweir return true; 143cdf0e10cSrcweir } 144cdf0e10cSrcweir 145cdf0e10cSrcweir bool EventQueue::addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent) 146cdf0e10cSrcweir { 147cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 148cdf0e10cSrcweir 149cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) 150cdf0e10cSrcweir OSL_TRACE("adding at %f event [%s] at %x for execution when queue is empty with delay %f\r", 151cdf0e10cSrcweir mpTimer->getElapsedTime(), 152cdf0e10cSrcweir OUStringToOString(rpEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(), 153cdf0e10cSrcweir rpEvent.get(), 154cdf0e10cSrcweir rpEvent->getActivationTime(0.0)); 155cdf0e10cSrcweir #endif 156cdf0e10cSrcweir 157cdf0e10cSrcweir ENSURE_OR_RETURN_FALSE( 158cdf0e10cSrcweir rpEvent.get() != NULL, 159cdf0e10cSrcweir "EventQueue::addEvent: event ptr NULL"); 160cdf0e10cSrcweir 161cdf0e10cSrcweir maNextNextEvents.push( 162cdf0e10cSrcweir EventEntry( 163cdf0e10cSrcweir rpEvent, 164cdf0e10cSrcweir rpEvent->getActivationTime(mpTimer->getElapsedTime()))); 165cdf0e10cSrcweir 166cdf0e10cSrcweir return true; 167cdf0e10cSrcweir } 168cdf0e10cSrcweir 169cdf0e10cSrcweir void EventQueue::forceEmpty() 170cdf0e10cSrcweir { 171cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 172cdf0e10cSrcweir 173cdf0e10cSrcweir process_(true); 174cdf0e10cSrcweir } 175cdf0e10cSrcweir 176cdf0e10cSrcweir void EventQueue::process() 177cdf0e10cSrcweir { 178cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 179cdf0e10cSrcweir 180cdf0e10cSrcweir process_(false); 181cdf0e10cSrcweir } 182cdf0e10cSrcweir 183cdf0e10cSrcweir void EventQueue::process_( bool bFireAllEvents ) 184cdf0e10cSrcweir { 185cdf0e10cSrcweir VERBOSE_TRACE( "EventQueue: heartbeat" ); 186cdf0e10cSrcweir 187cdf0e10cSrcweir // add in all that have been added explicitly for this round: 188cdf0e10cSrcweir EventEntryVector::const_iterator const iEnd( maNextEvents.end() ); 189cdf0e10cSrcweir for ( EventEntryVector::const_iterator iPos( maNextEvents.begin() ); 190cdf0e10cSrcweir iPos != iEnd; ++iPos ) { 191cdf0e10cSrcweir maEvents.push(*iPos); 192cdf0e10cSrcweir } 193cdf0e10cSrcweir EventEntryVector().swap( maNextEvents ); 194cdf0e10cSrcweir 195cdf0e10cSrcweir // perform topmost, ready-to-execute event 196cdf0e10cSrcweir // ======================================= 197cdf0e10cSrcweir 198cdf0e10cSrcweir const double nCurrTime( mpTimer->getElapsedTime() ); 199cdf0e10cSrcweir 200cdf0e10cSrcweir // When maEvents does not contain any events that are due now 201cdf0e10cSrcweir // then process one event from maNextNextEvents. 202cdf0e10cSrcweir if (!maNextNextEvents.empty() 203cdf0e10cSrcweir && !bFireAllEvents 204cdf0e10cSrcweir && (maEvents.empty() || maEvents.top().nTime > nCurrTime)) 205cdf0e10cSrcweir { 206cdf0e10cSrcweir const EventEntry aEvent (maNextNextEvents.top()); 207cdf0e10cSrcweir maNextNextEvents.pop(); 208cdf0e10cSrcweir maEvents.push(aEvent); 209cdf0e10cSrcweir } 210cdf0e10cSrcweir 211cdf0e10cSrcweir // process ready/elapsed events. Note that the 'perceived' 212cdf0e10cSrcweir // current time remains constant for this loop, thus we're 213cdf0e10cSrcweir // processing only those events which where ready when we 214cdf0e10cSrcweir // entered this method. 215cdf0e10cSrcweir while( !maEvents.empty() && 216cdf0e10cSrcweir (bFireAllEvents || maEvents.top().nTime <= nCurrTime) ) 217cdf0e10cSrcweir { 218cdf0e10cSrcweir EventEntry event( maEvents.top() ); 219cdf0e10cSrcweir maEvents.pop(); 220cdf0e10cSrcweir 221cdf0e10cSrcweir // only process event, if it is still 'charged', 222cdf0e10cSrcweir // i.e. the fire() call effects something. This is 223cdf0e10cSrcweir // used when e.g. having events registered at multiple 224cdf0e10cSrcweir // places, which should fire only once: after the 225cdf0e10cSrcweir // initial fire() call, those events become inactive 226cdf0e10cSrcweir // and return false on isCharged. This frees us from 227cdf0e10cSrcweir // the need to prune queues of those inactive shells. 228cdf0e10cSrcweir if( event.pEvent->isCharged() ) 229cdf0e10cSrcweir { 230cdf0e10cSrcweir try 231cdf0e10cSrcweir { 232cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 0 233cdf0e10cSrcweir VERBOSE_TRACE( "Firing event: unknown (0x%X), timeout was: %f", 234cdf0e10cSrcweir event.pEvent.get(), 235cdf0e10cSrcweir event.pEvent->getActivationTime(0.0) ); 236cdf0e10cSrcweir #endif 237cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS) 238cdf0e10cSrcweir OSL_TRACE("firing at %f event [%s] at %x with delay %f\r", 239cdf0e10cSrcweir mpTimer->getElapsedTime(), 240cdf0e10cSrcweir OUStringToOString(event.pEvent->GetDescription(), 241cdf0e10cSrcweir RTL_TEXTENCODING_UTF8).getStr(), 242cdf0e10cSrcweir event.pEvent.get(), 243cdf0e10cSrcweir event.pEvent->getActivationTime(0.0)); 244cdf0e10cSrcweir #endif 245cdf0e10cSrcweir 246cdf0e10cSrcweir event.pEvent->fire(); 247cdf0e10cSrcweir } 248cdf0e10cSrcweir catch( uno::RuntimeException& ) 249cdf0e10cSrcweir { 250cdf0e10cSrcweir throw; 251cdf0e10cSrcweir } 252cdf0e10cSrcweir catch( uno::Exception& ) 253cdf0e10cSrcweir { 254cdf0e10cSrcweir // catch anything here, we don't want 255cdf0e10cSrcweir // to leave this scope under _any_ 256cdf0e10cSrcweir // circumstance. Although, do _not_ 257cdf0e10cSrcweir // reinsert an activity that threw 258cdf0e10cSrcweir // once. 259cdf0e10cSrcweir 260cdf0e10cSrcweir // NOTE: we explicitely don't catch(...) here, 261cdf0e10cSrcweir // since this will also capture segmentation 262cdf0e10cSrcweir // violations and the like. In such a case, we 263cdf0e10cSrcweir // still better let our clients now... 264cdf0e10cSrcweir OSL_ENSURE( false, 265cdf0e10cSrcweir rtl::OUStringToOString( 266cdf0e10cSrcweir comphelper::anyToString( cppu::getCaughtException() ), 267cdf0e10cSrcweir RTL_TEXTENCODING_UTF8 ).getStr() ); 268cdf0e10cSrcweir } 269cdf0e10cSrcweir catch( SlideShowException& ) 270cdf0e10cSrcweir { 271cdf0e10cSrcweir // catch anything here, we don't want 272cdf0e10cSrcweir // to leave this scope under _any_ 273cdf0e10cSrcweir // circumstance. Although, do _not_ 274cdf0e10cSrcweir // reinsert an activity that threw 275cdf0e10cSrcweir // once. 276cdf0e10cSrcweir 277cdf0e10cSrcweir // NOTE: we explicitely don't catch(...) here, 278cdf0e10cSrcweir // since this will also capture segmentation 279cdf0e10cSrcweir // violations and the like. In such a case, we 280cdf0e10cSrcweir // still better let our clients now... 281cdf0e10cSrcweir OSL_TRACE( "::presentation::internal::EventQueue: Event threw a SlideShowException, action might not have been fully performed" ); 282cdf0e10cSrcweir } 283cdf0e10cSrcweir } 284cdf0e10cSrcweir else 285cdf0e10cSrcweir { 286cdf0e10cSrcweir #if OSL_DEBUG_LEVEL > 0 287cdf0e10cSrcweir VERBOSE_TRACE( "Ignoring discharged event: unknown (0x%X), timeout was: %f", 288cdf0e10cSrcweir event.pEvent.get(), 289cdf0e10cSrcweir event.pEvent->getActivationTime(0.0) ); 290cdf0e10cSrcweir #endif 291cdf0e10cSrcweir } 292cdf0e10cSrcweir } 293cdf0e10cSrcweir } 294cdf0e10cSrcweir 295cdf0e10cSrcweir bool EventQueue::isEmpty() const 296cdf0e10cSrcweir { 297cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 298cdf0e10cSrcweir 299cdf0e10cSrcweir return maEvents.empty() && maNextEvents.empty() && maNextNextEvents.empty(); 300cdf0e10cSrcweir } 301cdf0e10cSrcweir 302cdf0e10cSrcweir double EventQueue::nextTimeout() const 303cdf0e10cSrcweir { 304cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 305cdf0e10cSrcweir 306cdf0e10cSrcweir // return time for next entry (if any) 307cdf0e10cSrcweir double nTimeout (::std::numeric_limits<double>::max()); 308cdf0e10cSrcweir const double nCurrentTime (mpTimer->getElapsedTime()); 309cdf0e10cSrcweir if ( ! maEvents.empty()) 310cdf0e10cSrcweir nTimeout = maEvents.top().nTime - nCurrentTime; 311cdf0e10cSrcweir if ( ! maNextEvents.empty()) 312cdf0e10cSrcweir nTimeout = ::std::min(nTimeout, maNextEvents.front().nTime - nCurrentTime); 313cdf0e10cSrcweir if ( ! maNextNextEvents.empty()) 314cdf0e10cSrcweir nTimeout = ::std::min(nTimeout, maNextNextEvents.top().nTime - nCurrentTime); 315cdf0e10cSrcweir 316cdf0e10cSrcweir return nTimeout; 317cdf0e10cSrcweir } 318cdf0e10cSrcweir 319cdf0e10cSrcweir void EventQueue::clear() 320cdf0e10cSrcweir { 321cdf0e10cSrcweir ::osl::MutexGuard aGuard( maMutex ); 322cdf0e10cSrcweir 323cdf0e10cSrcweir // TODO(P1): Maybe a plain vector and vector.swap will 324cdf0e10cSrcweir // be faster here. Profile. 325cdf0e10cSrcweir maEvents = ImplQueueType(); 326cdf0e10cSrcweir 327cdf0e10cSrcweir maNextEvents.clear(); 328cdf0e10cSrcweir maNextNextEvents = ImplQueueType(); 329cdf0e10cSrcweir } 330cdf0e10cSrcweir } 331cdf0e10cSrcweir } 332