/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ // // Mac implementation of the nsITimer interface // // nsMacTimerPeriodical idles, #include "nsITimer.h" #include "nsITimerCallback.h" #include "prlog.h" #include #include #include #include #pragma mark class TimerImpl // // TimerImpl implements nsITimer API // class TimerImpl : public nsITimer { private: nsTimerCallbackFunc mCallbackFunc; nsITimerCallback * mCallbackObject; void * mClosure; PRUint32 mDelay; UInt32 mFireTime; // Timer should fire when TickCount >= this number public: // constructors TimerImpl(); virtual ~TimerImpl(){}; NS_DECL_ISUPPORTS UInt32 GetFireTime() { return mFireTime; } void Fire(); // nsITimer overrides virtual nsresult Init(nsTimerCallbackFunc aFunc, void *aClosure, PRUint32 aDelay); virtual nsresult Init(nsITimerCallback *aCallback, PRUint32 aDelay); virtual void Cancel(); virtual PRUint32 GetDelay(); virtual void SetDelay(PRUint32 aDelay); virtual void* GetClosure(); private: // Calculates mFireTime too void SetDelaySelf( PRUint32 aDelay ); }; #pragma mark class TimerPeriodical // // TimerPeriodical is a singleton LPeriodical subclass that fires // off TimerImpl. The firing is done on idle // class TimerPeriodical : public LPeriodical { static TimerPeriodical * gPeriodical; LArray mTimers; // List of TimerImpl * public: // Returns the singleton instance static TimerPeriodical * GetPeriodical(); TimerPeriodical(); virtual ~TimerPeriodical(); nsresult AddTimer( TimerImpl * aTimer); nsresult RemoveTimer( TimerImpl * aTimer); virtual void SpendTime( const EventRecord &inMacEvent); }; #pragma mark class TimerImplComparator // // TimerImplComparator compares two TimerImpl // class TimerImplComparator : public LComparator { virtual Int32 Compare( const void* inItemOne, const void* inItemTwo, Uint32 inSizeOne, Uint32 inSizeTwo) const; }; // // TimerImpl implementation // static NS_DEFINE_IID(kITimerIID, NS_ITIMER_IID); NS_IMPL_ISUPPORTS(TimerImpl, kITimerIID) TimerImpl::TimerImpl() { NS_INIT_REFCNT(); mCallbackFunc = NULL; mCallbackObject = NULL; mClosure = NULL; mDelay = 0; mFireTime = 0; } nsresult TimerImpl::Init(nsTimerCallbackFunc aFunc, void *aClosure, PRUint32 aDelay) { mCallbackFunc = aFunc; mClosure = aClosure; SetDelaySelf(aDelay); return TimerPeriodical::GetPeriodical()->AddTimer(this); } nsresult TimerImpl::Init(nsITimerCallback *aCallback, PRUint32 aDelay) { mCallbackObject = aCallback; SetDelaySelf(aDelay); return TimerPeriodical::GetPeriodical()->AddTimer(this); } void TimerImpl::Cancel() { TimerPeriodical::GetPeriodical()->RemoveTimer(this); } PRUint32 TimerImpl::GetDelay() { return mDelay; } void TimerImpl::SetDelay(PRUint32 aDelay) { SetDelaySelf(aDelay); // Make sure that timer was sorted NS_ADDREF(this); TimerPeriodical::GetPeriodical()->RemoveTimer(this); TimerPeriodical::GetPeriodical()->AddTimer(this); NS_RELEASE(this); } void* TimerImpl::GetClosure() { return mClosure; } void TimerImpl::Fire() { if (mCallbackFunc != NULL) { (*mCallbackFunc)(this, mClosure); } else if (mCallbackObject != NULL) { mCallbackObject->Notify(this); // Fire the timer } } void TimerImpl::SetDelaySelf( PRUint32 aDelay ) { mDelay = aDelay; mFireTime = TickCount() + (mDelay * 3) / 50; // We need mFireTime in ticks (1/60th) // but aDelay is in 1000th (60/1000 = 3/50) } TimerPeriodical * TimerPeriodical::gPeriodical = NULL; TimerPeriodical * TimerPeriodical::GetPeriodical() { if (gPeriodical == NULL) gPeriodical = new TimerPeriodical(); return gPeriodical; } TimerPeriodical::TimerPeriodical() { mTimers.SetComparator( new TimerImplComparator() ); mTimers.SetKeepSorted( true ); } TimerPeriodical::~TimerPeriodical() { PR_ASSERT(mTimers.GetCount() == 0); } nsresult TimerPeriodical::AddTimer( TimerImpl * aTimer) { try { NS_ADDREF(aTimer); mTimers.AddItem( &aTimer ); StartRepeating(); } catch(...) { return NS_ERROR_UNEXPECTED; } return NS_OK; } nsresult TimerPeriodical::RemoveTimer( TimerImpl * aTimer) { mTimers.SetComparator(LLongComparator::GetComparator(), false); mTimers.Remove(&aTimer); mTimers.SetComparator(new TimerImplComparator()); NS_RELEASE( aTimer ); if ( mTimers.GetCount() == 0 ) StopRepeating(); return NS_OK; } // Called through every event loop // Loops through the list of available timers, and // fires off the available ones void TimerPeriodical::SpendTime( const EventRecord &inMacEvent) { LArrayIterator iter(mTimers); TimerImpl * timer; while (iter.Next(&timer)) { if (timer->GetFireTime() <= inMacEvent.when) { //NS_ADDREF(timer); RemoveTimer(timer); timer->Fire(); //NS_RELEASE(timer); } else break; // Items are sorted, so we do not need to iterate until the end } } // // class TimerImplComparator implementation // Int32 TimerImplComparator::Compare( const void* inItemOne, const void* inItemTwo, Uint32 inSizeOne, Uint32 inSizeTwo) const { return (( TimerImpl *) inItemOne)->GetFireTime() - (( TimerImpl *) inItemTwo)->GetFireTime(); } NS_BASE nsresult NS_NewTimer(nsITimer** aInstancePtrResult) { NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr"); if (nsnull == aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } TimerImpl *timer = new TimerImpl(); if (nsnull == timer) { return NS_ERROR_OUT_OF_MEMORY; } return timer->QueryInterface(kITimerIID, (void **) aInstancePtrResult); }