Back to index

lightning-sunbird  0.9+nobinonly
nsQDFlushManager.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is supposed to avoid excessive QuickDraw flushes.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Mark Mentovai <mark@moxienet.com>.
00019  * Portions created by the Initial Developer are Copyright (C) 2005
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsQDFlushManager.h"
00039 
00040 #include "nsComponentManagerUtils.h"
00041 #include "nsIServiceManager.h"
00042 #include "nsCRT.h"
00043 
00044 // nsQDFlushManager
00045 
00046 nsQDFlushManager::nsQDFlushManager()
00047 : mPortList(nsnull)
00048 {
00049 }
00050 
00051 nsQDFlushManager::~nsQDFlushManager()
00052 {
00053   nsQDFlushPort* port = mPortList;
00054   while (port)
00055   {
00056     nsQDFlushPort* next = port->mNext;
00057     port->Destroy();
00058     NS_RELEASE(port);
00059     port = next;
00060   }
00061 }
00062 
00063 // CreateOrGetPort(aPort)
00064 //
00065 // Walks through the list of port objects and returns the one corresponding to
00066 // aPort if it exists.  If it doesn't exist, but an unused existing port
00067 // object can be adapted to aPort, it will be adapted and returned.  If no
00068 // suitable port object exists, a new port object is created, added to the
00069 // list, and returned.  Any port objects created here will be destroyed when
00070 // the ~nsQDFlushManager destructor runs or when RemovePort is called.
00071 //
00072 // protected
00073 nsQDFlushPort*
00074 nsQDFlushManager::CreateOrGetPort(CGrafPtr aPort)
00075 {
00076   AbsoluteTime now = ::UpTime();
00077 
00078   // First, go through the list and see if an object is already associated
00079   // with this port.
00080   nsQDFlushPort* port = mPortList;
00081   while (port)
00082   {
00083     if (aPort == port->mPort)
00084     {
00085       return port;
00086     }
00087     port = port->mNext;
00088   }
00089 
00090   // If no port object exists yet, try to find an object that's not in use.
00091   // If there is one, repurpose it.
00092   // Don't be frightened.  The pointer-pointer business isn't so confusing,
00093   // and it eases maintenance of the linked list.
00094   nsQDFlushPort** portPtr = &mPortList;
00095   while ((port = *portPtr))
00096   {
00097     if (!port->mFlushTimerRunning && port->TimeUntilFlush(now) < 0)
00098     {
00099       // If the flush timer is not running and it's past the time during which
00100       // a flush would be postponed, the object is no longer needed.  Future
00101       // flushes for port->mPort would occur immediately.  Since there's no
00102       // longer any state to track, the object can be reused for another port
00103       // This keeps the size of the list manageable.
00104       port->Init(aPort);
00105       return port;
00106     }
00107     portPtr = &port->mNext;
00108   }
00109 
00110   // portPtr points to mNext of the last port object in the list, or if none,
00111   // to mPortList.  That makes it easy to hook the new object up.
00112   *portPtr = port = new nsQDFlushPort(aPort);
00113   NS_IF_ADDREF(port);
00114   return port;
00115 }
00116 
00117 NS_IMPL_ISUPPORTS1(nsQDFlushManager, nsIQDFlushManager)
00118 
00119 // nsIQDFlushManager implementation
00120 
00121 // FlushPortBuffer(aPort, aRegion)
00122 //
00123 // The public entry point for object-based calls.  Calls
00124 // QDFlushPortBuffer(aPort, aRegion) if aPort hasn't been flushed too
00125 // recently.  If it has been, calls QDAddRegionToDirtyRegion(aPort, aRegion)
00126 // and if no flush has been scheduled, schedules a flush for the appropriate
00127 // time.
00128 //
00129 // public
00130 NS_IMETHODIMP
00131 nsQDFlushManager::FlushPortBuffer(CGrafPtr aPort, RgnHandle aRegion)
00132 {
00133   CreateOrGetPort(aPort)->FlushPortBuffer(aRegion);
00134   return NS_OK;
00135 }
00136 
00137 // RemovePort(aPort)
00138 //
00139 // Walks through the list of port objects and removes the one corresponding to
00140 // aPort, if it exists.
00141 //
00142 // public
00143 NS_IMETHODIMP
00144 nsQDFlushManager::RemovePort(CGrafPtr aPort)
00145 {
00146   // Traversal is as in CreateOrGetPort.
00147   nsQDFlushPort** portPtr = &mPortList;
00148   while (nsQDFlushPort* port = *portPtr)
00149   {
00150     if (aPort == port->mPort)
00151     {
00152       nsQDFlushPort* next = port->mNext;
00153       port->Destroy();
00154       NS_RELEASE(port);
00155 
00156       // portPtr points to mNext of the previous object, or if none,
00157       // mPortList.  That makes it easy to snip the old object out by
00158       // setting it to the follower.
00159       *portPtr = next;
00160       return NS_OK;
00161     }
00162     portPtr = &port->mNext;
00163   }
00164   return NS_OK;
00165 }
00166 
00167 // nsQDFlushPort
00168 
00169 nsQDFlushPort::nsQDFlushPort(CGrafPtr aPort)
00170 : mNext(nsnull)
00171 , mPort(aPort)
00172 , mLastFlushTime((AbsoluteTime){0, 0})
00173 , mFlushTimer(nsnull)
00174 , mFlushTimerRunning(PR_FALSE)
00175 {
00176 }
00177 
00178 nsQDFlushPort::~nsQDFlushPort()
00179 {
00180   // Everything should have been taken care of by Destroy().
00181 }
00182 
00183 // Init(aPort)
00184 //
00185 // (Re)initialize object.
00186 //
00187 // protected
00188 void
00189 nsQDFlushPort::Init(CGrafPtr aPort)
00190 {
00191   mPort = aPort;
00192 }
00193 
00194 // Destroy()
00195 //
00196 // Prepare object for destruction.
00197 //
00198 // protected
00199 void
00200 nsQDFlushPort::Destroy()
00201 {
00202   if (mFlushTimer)
00203   {
00204     mFlushTimer->Cancel();
00205   }
00206   mFlushTimer = nsnull;
00207   mFlushTimerRunning = PR_FALSE;
00208   mNext = nsnull;
00209 }
00210 
00211 // FlushPortBuffer(aRegion)
00212 //
00213 // Flushes, dirties, and schedules, as appropriate.  Public access is from
00214 // nsQDFlushManager::FlushPortBuffer(CGrafPtr aPort, RgnHandle aRegion).
00215 //
00216 // protected
00217 void
00218 nsQDFlushPort::FlushPortBuffer(RgnHandle aRegion)
00219 {
00220   AbsoluteTime now = ::UpTime();
00221   PRInt64 timeUntilFlush = TimeUntilFlush(now);
00222 
00223   if (!mFlushTimerRunning && timeUntilFlush < 0)
00224   {
00225     // If past the time for the next acceptable flush, flush now.
00226     ::QDFlushPortBuffer(mPort, aRegion);
00227     mLastFlushTime = now;
00228   }
00229   else
00230   {
00231     // If it's not time for the next flush yet, or if the timer is running
00232     // indicating that an update is pending, just mark the dirty region.
00233     ::QDAddRegionToDirtyRegion(mPort, aRegion);
00234 
00235     if (!mFlushTimerRunning)
00236     {
00237       // No flush scheduled?  No problem.
00238       if (!mFlushTimer)
00239       {
00240         // No timer object?  No problem.
00241         nsresult err;
00242         mFlushTimer = do_CreateInstance("@mozilla.org/timer;1", &err);
00243         NS_ASSERTION(NS_SUCCEEDED(err), "Could not instantiate flush timer.");
00244       }
00245       if (mFlushTimer)
00246       {
00247         // Start the clock, with the timer firing at the already-calculated
00248         // time until the next flush.  Nanoseconds (1E-9) were used above,
00249         // but nsITimer is big on milliseconds (1E-3), so divide by 1E6.
00250         // Any time that was consumed between the ::UpTime call and now
00251         // will be lost.  That's not so bad in the usual case, it's a tiny
00252         // bit less not so bad if a timer object didn't exist yet and was
00253         // created.  It's better to update slightly less frequently than
00254         // the target than slightly more frequently.
00255         mFlushTimer->InitWithCallback(this, (PRUint32)(timeUntilFlush/1E6),
00256                                       nsITimer::TYPE_ONE_SHOT);
00257         mFlushTimerRunning = PR_TRUE;
00258       }
00259     }
00260   }
00261 }
00262 
00263 // protected
00264 PRInt64
00265 nsQDFlushPort::TimeUntilFlush(AbsoluteTime aNow)
00266 {
00267   Nanoseconds elapsed = ::AbsoluteDeltaToNanoseconds(aNow, mLastFlushTime);
00268 
00269   // nano = 1E-9 and the desired refresh rate is in Hz, so 1E9/kRefreshRateHz
00270   // gives the interval between updates in nanoseconds.
00271   return S64Subtract(U64SetU(1E9/kRefreshRateHz),
00272                      UnsignedWideToUInt64(elapsed));
00273 }
00274 
00275 // nsITimer implementation
00276 
00277 NS_IMPL_ISUPPORTS1(nsQDFlushPort, nsITimerCallback)
00278 
00279 // Notify(aTimer)
00280 //
00281 // Timer callback.  Flush the dirty port buffer to the screen.
00282 NS_IMETHODIMP
00283 nsQDFlushPort::Notify(nsITimer* aTimer)
00284 {
00285   NS_ASSERTION(aTimer == mFlushTimer, "Callback called by wrong timer");
00286 
00287   // Flush the dirty region.
00288   ::QDFlushPortBuffer(mPort, NULL);
00289 
00290   mLastFlushTime = ::UpTime();
00291   mFlushTimerRunning = PR_FALSE;
00292 
00293   // This shouldn't be necessary, nsITimer.idl
00294   // aTimer->Cancel();
00295 
00296   return NS_OK;
00297 }