Git init
[external/pango1.0.git] / pango / pangox-fontcache.c
1 /* Pango
2  * pango-fontcache.c: Cache of XFontStructs by XLFD
3  *
4  * Copyright (C) 2000 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23 #include "pangox.h"
24
25 /* Font cache
26  */
27
28 /* Number of fonts to retain after they are not otherwise referenced.
29  */
30 #define CACHE_SIZE 16
31
32 typedef struct _CacheEntry CacheEntry;
33
34 struct _PangoXFontCache
35 {
36   Display *display;
37
38   GHashTable *forward;
39   GHashTable *back;
40
41   GList *mru;
42   GList *mru_tail;
43   int mru_count;
44 };
45
46 struct _CacheEntry
47 {
48   char *xlfd;
49   XFontStruct *fs;
50
51   gint ref_count;
52   GList *mru;
53 };
54
55 static void
56 free_cache_entry (char            *xlfd G_GNUC_UNUSED,
57                   CacheEntry      *entry,
58                   PangoXFontCache *cache)
59 {
60   g_free (entry->xlfd);
61   XFreeFont (cache->display, entry->fs);
62
63   g_slice_free (CacheEntry, entry);
64 }
65
66 /**
67  * pango_x_font_cache_free:
68  * @cache: a #PangoXFontCache
69  *
70  * Frees a #PangoXFontCache and all associated memory. All fonts loaded
71  * through this font cache will be freed along with the cache.
72  **/
73 void
74 pango_x_font_cache_free (PangoXFontCache *cache)
75 {
76   g_return_if_fail (cache != NULL);
77
78   g_hash_table_foreach (cache->forward, (GHFunc)free_cache_entry, cache);
79
80   g_hash_table_destroy (cache->forward);
81   g_hash_table_destroy (cache->back);
82
83   g_list_free (cache->mru);
84
85   g_slice_free (PangoXFontCache, cache);
86 }
87
88 /**
89  * pango_x_font_cache_new:
90  * @display: an X display.
91  *
92  * Creates a font cache for the specified display.
93  *
94  * Return value: The newly allocated #PangoXFontCache, which should be
95  *               freed with pango_x_font_cache_free().
96  **/
97 PangoXFontCache *
98 pango_x_font_cache_new (Display *display)
99 {
100   PangoXFontCache *cache;
101
102   g_return_val_if_fail (display != NULL, NULL);
103
104   cache = g_slice_new (PangoXFontCache);
105
106   cache->display = display;
107
108   cache->forward = g_hash_table_new (g_str_hash, g_str_equal);
109   cache->back = g_hash_table_new (g_direct_hash, g_direct_equal);
110
111   cache->mru = NULL;
112   cache->mru_tail = NULL;
113   cache->mru_count = 0;
114
115   return cache;
116 }
117
118 static void
119 cache_entry_unref (PangoXFontCache *cache, CacheEntry *entry)
120 {
121   if (g_atomic_int_dec_and_test (&entry->ref_count))
122     {
123       g_hash_table_remove (cache->forward, entry->xlfd);
124       g_hash_table_remove (cache->back, entry->fs);
125
126       free_cache_entry (NULL, entry, cache);
127     }
128 }
129
130 /**
131  * pango_x_font_cache_load:
132  * @cache: a #PangoXFontCache
133  * @xlfd: the X Logical Font Description to load.
134  *
135  * Loads a #XFontStruct from a X Logical Font Description. The
136  * result may be newly loaded, or it may have been previously
137  * stored.
138  *
139  * Return value: The font structure, or %NULL if the font could
140  * not be loaded. In order to free this structure, you must call
141  * pango_x_font_cache_unload().
142  **/
143 XFontStruct *
144 pango_x_font_cache_load (PangoXFontCache *cache,
145                          const char      *xlfd)
146 {
147   CacheEntry *entry;
148
149   g_return_val_if_fail (cache != NULL, NULL);
150   g_return_val_if_fail (xlfd != NULL, NULL);
151
152   entry = g_hash_table_lookup (cache->forward, xlfd);
153
154   if (entry)
155     {
156       g_atomic_int_inc (&entry->ref_count);
157     }
158   else
159     {
160       XFontStruct *fs = XLoadQueryFont (cache->display, xlfd);
161
162       if (!fs)
163         return NULL;
164
165       entry = g_slice_new (CacheEntry);
166
167       entry->xlfd = g_strdup (xlfd);
168       entry->fs = fs;
169
170       entry->ref_count = 1;
171       entry->mru = NULL;
172
173       g_hash_table_insert (cache->forward, entry->xlfd, entry);
174       g_hash_table_insert (cache->back, entry->fs, entry);
175     }
176
177   if (entry->mru)
178     {
179       if (cache->mru_count > 1 && entry->mru->prev)
180         {
181           /* Move to the head of the mru list */
182
183           if (entry->mru == cache->mru_tail)
184             {
185               cache->mru_tail = cache->mru_tail->prev;
186               cache->mru_tail->next = NULL;
187             }
188           else
189             {
190               entry->mru->prev->next = entry->mru->next;
191               entry->mru->next->prev = entry->mru->prev;
192             }
193
194           entry->mru->next = cache->mru;
195           entry->mru->prev = NULL;
196           cache->mru->prev = entry->mru;
197           cache->mru = entry->mru;
198         }
199     }
200   else
201     {
202       g_atomic_int_inc (&entry->ref_count);
203
204       /* Insert into the mru list */
205
206       if (cache->mru_count == CACHE_SIZE)
207         {
208           CacheEntry *old_entry = cache->mru_tail->data;
209
210           cache->mru_tail = cache->mru_tail->prev;
211           cache->mru_tail->next = NULL;
212
213           g_list_free_1 (old_entry->mru);
214           old_entry->mru = NULL;
215           cache_entry_unref (cache, old_entry);
216         }
217       else
218         cache->mru_count++;
219
220       cache->mru = g_list_prepend (cache->mru, entry);
221       if (!cache->mru_tail)
222         cache->mru_tail = cache->mru;
223       entry->mru = cache->mru;
224     }
225
226   return entry->fs;
227 }
228
229 /**
230  * pango_x_font_cache_unload:
231  * @cache: a #PangoXFontCache
232  * @fs: the font structure to unload
233  *
234  * Frees a font structure previously loaded with pango_x_font_cache_load().
235  **/
236 void
237 pango_x_font_cache_unload (PangoXFontCache *cache,
238                            XFontStruct     *fs)
239 {
240   CacheEntry *entry;
241
242   g_return_if_fail (cache != NULL);
243   g_return_if_fail (fs != NULL);
244
245   entry = g_hash_table_lookup (cache->back, fs);
246   g_return_if_fail (entry != NULL);
247
248   cache_entry_unref (cache, entry);
249 }