Back to index

lightning-sunbird  0.9+nobinonly
nsGCCache.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Stuart Parmenter <pavlov@netscape.com>
00025  *   Mike Shaver <shaver@zeroknowledge.com>
00026  *   Tomi Leppikangas <Tomi.Leppikangas@oulu.fi>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include <stdio.h>
00043 #include "nsGCCache.h"
00044 #include "nsISupportsUtils.h"
00045 #include <gdk/gdkx.h>
00046 #include <gdk/gdkprivate.h>
00047 #include <X11/Xlib.h>
00048 /* The GC cache is shared among all windows, since it doesn't hog
00049    any scarce resources (like colormap entries.) */
00050 
00051 GdkRegion *nsGCCache::copyRegion = NULL;
00052 
00053 MOZ_DECL_CTOR_COUNTER(nsGCCache)
00054 
00055 nsGCCache::nsGCCache()
00056 {
00057   MOZ_COUNT_CTOR(nsGCCache);
00058   PR_INIT_CLIST(&GCCache);
00059   PR_INIT_CLIST(&GCFreeList);
00060   for (int i = 0; i < GC_CACHE_SIZE; i++) {
00061     GCCacheEntry *entry = new GCCacheEntry();
00062     entry->gc=NULL;
00063     PR_INSERT_LINK(&entry->clist, &GCFreeList);
00064   }
00065   DEBUG_METER(memset(&GCCacheStats, 0, sizeof(GCCacheStats));)
00066 }
00067 
00068 /* static */ void
00069 nsGCCache::Shutdown()
00070 {
00071     if (copyRegion) {
00072         gdk_region_destroy(copyRegion);
00073         copyRegion = nsnull;
00074     }
00075 }
00076 
00077 void
00078 nsGCCache::move_cache_entry(PRCList *clist)
00079 {
00080   /* thread on the freelist, at the front */
00081   PR_REMOVE_LINK(clist);
00082   PR_INSERT_LINK(clist, &GCFreeList);
00083 }
00084 
00085 void
00086 nsGCCache::free_cache_entry(PRCList *clist)
00087 {
00088   GCCacheEntry *entry = (GCCacheEntry *)clist;
00089   gdk_gc_unref(entry->gc);
00090   if (entry->clipRegion)
00091     gdk_region_destroy(entry->clipRegion);
00092   
00093   /* thread on the freelist, at the front */
00094   PR_REMOVE_LINK(clist);
00095   memset(entry, 0, sizeof(*entry));
00096   PR_INSERT_LINK(clist, &GCFreeList);
00097 }
00098 
00099 nsGCCache::~nsGCCache()
00100 {
00101   PRCList *head;
00102 
00103   MOZ_COUNT_DTOR(nsGCCache);
00104 
00105   ReportStats();
00106 
00107   while (!PR_CLIST_IS_EMPTY(&GCCache)) {
00108     head = PR_LIST_HEAD(&GCCache);
00109     if (head == &GCCache)
00110       break;
00111     free_cache_entry(head);
00112   }
00113 
00114   while (!PR_CLIST_IS_EMPTY(&GCFreeList)) {
00115     head = PR_LIST_HEAD(&GCFreeList);
00116     if (head == &GCFreeList)
00117       break;
00118     PR_REMOVE_LINK(head);
00119     delete (GCCacheEntry *)head;
00120   }
00121 }
00122 
00123 void
00124 nsGCCache::ReportStats() { 
00125   DEBUG_METER(
00126               fprintf(stderr, "GC Cache:\n\thits:");
00127               int hits = 0;
00128               for (int i = 0; i < GC_CACHE_SIZE; i++) {
00129                 fprintf(stderr, " %4d", GCCacheStats.hits[i]);
00130                 hits+=GCCacheStats.hits[i];
00131               }
00132               int total = hits + GCCacheStats.misses;
00133               float percent = float(float(hits) / float(total));
00134               percent *= 100;
00135               fprintf(stderr, "\n\thits: %d, misses: %d, hit percent: %f%%\n", 
00136                       hits, GCCacheStats.misses, percent);
00137               );
00138 }
00139 
00140 #ifdef MOZ_WIDGET_GTK
00141 
00142 GdkRegion *
00143 nsGCCache::gdk_region_copy(GdkRegion *region)
00144 {
00145   if (!copyRegion) copyRegion = gdk_region_new();
00146 
00147   return gdk_regions_union(region, copyRegion);
00148 }
00149 
00150 #endif
00151 
00152 /* Dispose of entries matching the given flags, compressing the GC cache */
00153 void nsGCCache::Flush(unsigned long flags)
00154 {
00155   while (!PR_CLIST_IS_EMPTY(&GCCache)) {
00156     PRCList *head = PR_LIST_HEAD(&GCCache);
00157     if (head == &GCCache)
00158       break;
00159     GCCacheEntry *entry = (GCCacheEntry *)head;
00160     if (entry->flags & flags)
00161       free_cache_entry(head);
00162   }
00163 }
00164 
00165 GdkGC *nsGCCache::GetGC(GdkWindow *window, GdkGCValues *gcv, GdkGCValuesMask flags, GdkRegion *clipRegion)
00166 {
00167 
00168   PRCList *iter;
00169   GCCacheEntry *entry;
00170   DEBUG_METER(int i = 0;)
00171   
00172   for (iter = PR_LIST_HEAD(&GCCache); iter != &GCCache;
00173        iter = PR_NEXT_LINK(iter)) {
00174 
00175     entry = (GCCacheEntry *)iter;
00176     if (flags == entry->flags && 
00177         !memcmp (gcv, &entry->gcv, sizeof (*gcv))) {
00178       /* if there's a clipRegion, we have to match */
00179 
00180       if ((clipRegion && entry->clipRegion &&
00181            gdk_region_equal(clipRegion, entry->clipRegion)) ||
00182           /* and if there isn't, we can't have one */
00183           (!clipRegion && !entry->clipRegion)) {
00184 
00185         /* move to the front of the list, if needed */
00186         if (iter != PR_LIST_HEAD(&GCCache)) {
00187           PR_REMOVE_LINK(iter);
00188           PR_INSERT_LINK(iter, &GCCache);
00189         }
00190         DEBUG_METER(GCCacheStats.hits[i]++;)
00191         return gdk_gc_ref(entry->gc);
00192       }
00193     }
00194     DEBUG_METER(++i;)
00195   }
00196     
00197   /* might need to forcibly free the LRU cache entry */
00198   if (PR_CLIST_IS_EMPTY(&GCFreeList)) {
00199     DEBUG_METER(GCCacheStats.reclaim++);
00200     move_cache_entry(PR_LIST_TAIL(&GCCache));
00201   }
00202 
00203   DEBUG_METER(GCCacheStats.misses++;)
00204   
00205   iter = PR_LIST_HEAD(&GCFreeList);
00206   PR_REMOVE_LINK(iter);
00207   PR_INSERT_LINK(iter, &GCCache);
00208   entry = (GCCacheEntry *)iter;
00209 
00210   if (!entry->gc) {
00211     // No old GC, greate new
00212     entry->gc = gdk_gc_new_with_values(window, gcv, flags);
00213     entry->flags = flags;
00214     entry->gcv = *gcv;
00215     entry->clipRegion = NULL;
00216     //printf("creating new gc=%X\n",entry->gc); 
00217   }
00218 #ifdef MOZ_WIDGET_GTK
00219   else if ( ((GdkGCPrivate*)entry->gc)->ref_count > 1 ) {
00220 #endif /* MOZ_WIDGET_GTK */
00221 #ifdef MOZ_WIDGET_GTK2
00222   else if ( G_OBJECT(entry->gc)->ref_count > 1 ) {
00223 #endif /* MOZ_WIDGET_GTK2 */
00224     // Old GC still in use, create new
00225     gdk_gc_unref(entry->gc);
00226     entry->gc=gdk_gc_new_with_values(window, gcv, flags);
00227     entry->flags = flags;
00228     entry->gcv = *gcv;
00229     entry->clipRegion = NULL;
00230     //printf("creating new (use)gc=%X\n",entry->gc); 
00231   }
00232   else {
00233     ReuseGC(entry, gcv, flags);
00234   }
00235 
00236   if (clipRegion) {
00237     entry->clipRegion = gdk_region_copy(clipRegion);
00238     if (entry->clipRegion)
00239       gdk_gc_set_clip_region(entry->gc, entry->clipRegion);
00240     /* XXX what if it fails? */
00241   }
00242   
00243   return gdk_gc_ref(entry->gc);
00244 }
00245 
00246 #ifdef MOZ_WIDGET_GTK2
00247 
00248 void nsGCCache::ReuseGC(GCCacheEntry *entry, GdkGCValues *gcv, GdkGCValuesMask flags)
00249 {
00250   // We have old GC, reuse it and check what
00251   // we have to change
00252 
00253   GdkGCValues xvalues;
00254   int xvalues_mask = 0;
00255 
00256   if (entry->clipRegion) {
00257     // set it to none here and then set the clip region with
00258     // gdk_gc_set_clip_region in GetGC()
00259     xvalues.clip_mask = None;
00260     xvalues_mask |= GDK_GC_CLIP_MASK;
00261     gdk_region_destroy(entry->clipRegion);
00262     entry->clipRegion = NULL;
00263   }
00264 
00265   if (entry->gcv.foreground.pixel != gcv->foreground.pixel) {
00266     xvalues.foreground.pixel = gcv->foreground.pixel;
00267     xvalues_mask |= GDK_GC_FOREGROUND;
00268   }
00269 
00270   if (entry->gcv.function != gcv->function) {
00271     xvalues.function = gcv->function;
00272     xvalues_mask |= GDK_GC_FUNCTION;
00273   }
00274 
00275   if(entry->gcv.font != gcv->font && flags & GDK_GC_FONT) {
00276     xvalues.font = gcv->font;
00277     xvalues_mask |= GDK_GC_FONT;
00278   }
00279 
00280   if (entry->gcv.line_style != gcv->line_style) {
00281     xvalues.line_style = gcv->line_style;
00282     xvalues_mask |= GDK_GC_LINE_STYLE;
00283   }
00284 
00285   if (xvalues_mask != 0) {
00286     gdk_gc_set_values(entry->gc, &xvalues, (GdkGCValuesMask)xvalues_mask);
00287   }
00288 
00289   entry->flags = flags;
00290   entry->gcv = *gcv;
00291 }
00292 
00293 #else /* MOZ_WIDGET_GTK and friends */
00294 
00295 void nsGCCache::ReuseGC(GCCacheEntry *entry, GdkGCValues *gcv, GdkGCValuesMask flags)
00296 {
00297   // We have old GC, reuse it and check what
00298   // we have to change
00299 
00300   XGCValues xvalues;
00301   unsigned long xvalues_mask=0;
00302 
00303   if (entry->clipRegion) {
00304     // set it to none here and then set the clip region with
00305     // gdk_gc_set_clip_region in GetGC()
00306     xvalues.clip_mask = None;
00307     xvalues_mask |= GCClipMask;
00308     gdk_region_destroy(entry->clipRegion);
00309     entry->clipRegion = NULL;
00310   }
00311 
00312   if (entry->gcv.foreground.pixel != gcv->foreground.pixel) {
00313     xvalues.foreground = gcv->foreground.pixel;
00314     xvalues_mask |= GCForeground;
00315   }
00316 
00317   if (entry->gcv.function != gcv->function) {
00318     switch (gcv->function) {
00319     case GDK_COPY:
00320       xvalues.function = GXcopy;
00321       break;
00322     case GDK_INVERT:
00323       xvalues.function = GXinvert;
00324       break;
00325     case GDK_XOR:
00326       xvalues.function = GXxor;
00327       break;
00328     case GDK_CLEAR:
00329       xvalues.function = GXclear;
00330       break;
00331     case GDK_AND:
00332       xvalues.function = GXand;
00333       break;
00334     case GDK_AND_REVERSE:
00335       xvalues.function = GXandReverse;
00336       break;
00337     case GDK_AND_INVERT:
00338       xvalues.function = GXandInverted;
00339       break;
00340     case GDK_NOOP:
00341       xvalues.function = GXnoop;
00342       break;
00343     case GDK_OR:
00344       xvalues.function = GXor;
00345       break;
00346     case GDK_EQUIV:
00347       xvalues.function = GXequiv;
00348       break;
00349     case GDK_OR_REVERSE:
00350       xvalues.function = GXorReverse;
00351       break;
00352     case GDK_COPY_INVERT:
00353       xvalues.function = GXcopyInverted;
00354       break;
00355     case GDK_OR_INVERT:
00356       xvalues.function = GXorInverted;
00357       break;
00358     case GDK_NAND:
00359       xvalues.function = GXnand;
00360       break;
00361     case GDK_SET:
00362       xvalues.function = GXset;
00363       break;
00364     }
00365     xvalues_mask |= GCFunction;
00366   }
00367 
00368   if(entry->gcv.font != gcv->font && flags & GDK_GC_FONT) {
00369     xvalues.font =  ((XFontStruct *)GDK_FONT_XFONT(gcv->font))->fid;
00370     xvalues_mask |= GCFont;
00371   }
00372 
00373   if (entry->gcv.line_style != gcv->line_style) {
00374     switch (gcv->line_style) {
00375     case GDK_LINE_SOLID:
00376       xvalues.line_style = LineSolid;
00377       break;
00378     case GDK_LINE_ON_OFF_DASH:
00379       xvalues.line_style = LineOnOffDash;
00380       break;
00381     case GDK_LINE_DOUBLE_DASH:
00382       xvalues.line_style = LineDoubleDash;
00383       break;
00384     }
00385     xvalues_mask |= GCLineStyle;
00386   }
00387 
00388   if (xvalues_mask != 0) {
00389     XChangeGC(GDK_GC_XDISPLAY(entry->gc), GDK_GC_XGC(entry->gc),
00390               xvalues_mask, &xvalues);
00391   }
00392   entry->flags = flags;
00393   entry->gcv = *gcv;
00394 }
00395 
00396 #endif /* MOZ_WIDGET_GTK */