add single instance mode
[uwplayer.git] / application / nfont.c
1 /*
2  * Copyright 2022 Olaf Wintermann
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a 
5  * copy of this software and associated documentation files (the "Software"), 
6  * to deal in the Software without restriction, including without limitation 
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
8  * and/or sell copies of the Software, and to permit persons to whom the 
9  * Software is furnished to do so, subject to the following conditions:
10  * 
11  * The above copyright notice and this permission notice shall be included in 
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
17  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
20  * DEALINGS IN THE SOFTWARE.
21  */
22
23 #include "nfont.h"
24
25
26 /* font list functions */
27
28 NFont *FontCreate(Display *dp, FcPattern *pattern)
29 {
30     if(!pattern) {
31         return NULL;
32     }
33     FcResult result;
34     pattern = FcPatternDuplicate(pattern);
35     FcPattern *match = XftFontMatch(dp, DefaultScreen(dp), pattern, &result);
36     
37     double sz = 0;
38     result = FcPatternGetDouble (pattern, FC_SIZE, 0, &sz);
39     if(result != FcResultMatch) {
40         FcPatternGetDouble (match, FC_SIZE, 0, &sz);
41     }
42     
43     XftFont *defaultFont = XftFontOpenPattern(dp, match);
44     if(!defaultFont) {
45         FcPatternDestroy(match);
46         return NULL;
47     }
48     
49     NFont *font = malloc(sizeof(NFont));
50     font->display = dp;
51     font->pattern = pattern;
52     font->fail = NULL;
53     font->size = sz;
54     font->ref = 1;
55
56     NFontList *list = malloc(sizeof(NFontList));
57     list->font = defaultFont;
58     list->next = NULL;
59     font->fonts = list;
60     
61     return font;
62 }
63
64 NFont *FontFromName(Display *dp, const char *name)
65 {
66     FcPattern *pattern = FcNameParse((FcChar8*)name);
67     if(!pattern) {
68         return NULL;
69     }
70     NFont *font = FontCreate(dp, pattern);
71     FcPatternDestroy(pattern);
72     return font;
73 }
74
75 XftFont *FontListAddFontForChar(NFont *f, FcChar32 c)
76 {
77     /* charset for char c */
78     FcCharSet *charset = FcCharSetCreate();
79     FcValue value;
80     value.type = FcTypeCharSet;
81     value.u.c = charset;
82     FcCharSetAddChar(charset, c);
83     if(!FcCharSetHasChar(charset, c)) {
84         FcCharSetDestroy(charset);
85         return f->fonts->font;
86     }
87     
88     /* font lookup based on the NFont pattern */ 
89     FcPattern *pattern = FcPatternDuplicate(f->pattern);
90     FcPatternAdd(pattern, FC_CHARSET, value, 0);
91     FcResult result;
92     FcPattern *match = XftFontMatch (
93             f->display, DefaultScreen(f->display), pattern, &result);
94     if(!match) {
95         FcPatternDestroy(pattern);
96         FontAddFail(f, charset);
97         return f->fonts->font;
98     }
99     
100     XftFont *newFont = XftFontOpenPattern(f->display, match);   
101     if(!newFont || !FcCharSetHasChar(newFont->charset, c)) {
102         FcPatternDestroy(pattern);
103         FcPatternDestroy(match);
104         if(newFont) {
105             XftFontClose(f->display, newFont);
106         }
107         FontAddFail(f, charset);
108         return f->fonts->font;
109     }
110     
111     FcCharSetDestroy(charset);
112     
113     NFontList *newElm = malloc(sizeof(NFontList));
114     newElm->font = newFont;
115     newElm->next = NULL;
116     
117     NFontList *elm = f->fonts;
118     NFontList *last = NULL;
119     while(elm) {
120         last = elm;
121         elm = elm->next;
122     }
123     last->next = newElm;
124     
125     
126     return newFont;
127 }
128
129 XftFont *FindFont(NFont *f, FcChar32 c)
130 {
131     if(c < 128) {
132         return f->fonts->font;
133     }
134     
135     /* make sure the char is not in the fail list, because we don't
136      * want to retry font lookups */
137     NCharSetList *fail = f->fail;
138     while(fail) {
139         if(FcCharSetHasChar(fail->charset, c)) {
140             return f->fonts->font;
141         }
142         fail = fail->next;
143         
144     }
145     
146     /* find a font that has this char */
147     NFontList *elm = f->fonts;
148     while(elm) {
149         if(FcCharSetHasChar(elm->font->charset, c)) {
150             return elm->font;
151         }
152         elm = elm->next;
153     }
154     
155     /* open a new font for this char */
156     return FontListAddFontForChar(f, c);
157 }
158
159 XftFont *FontDefault(NFont *f) {
160     return f->fonts->font;
161 }
162
163 void FontAddFail(NFont *f, FcCharSet *c)
164 {
165     NCharSetList *elm = f->fail;
166     NCharSetList *last = elm;
167     while(elm) {
168         last = elm;
169         elm = elm->next;
170     }
171     
172     NCharSetList *newElm = malloc(sizeof(NCharSetList));
173     newElm->charset = c;
174     newElm->next = NULL;
175     if(last) {
176         last->next = newElm;
177     } else {
178         f->fail = newElm;
179     }
180 }
181
182 void FontDestroy(NFont *f)
183 {
184     NCharSetList *c = f->fail;
185     NCharSetList *nc;
186     while(c) {
187         FcCharSetDestroy(c->charset);
188         nc = c->next;
189         free(c);
190         c = nc;
191     }
192     
193     NFontList *l = f->fonts;
194     NFontList *nl;
195     while(l) {
196         XftFontClose(f->display, l->font);
197         nl = l->next;
198         free(nl);
199         l = nl;
200     }
201     
202     FcPatternDestroy(f->pattern);
203     free(f);
204 }
205
206 NFont *FontRef(NFont *font)  {
207     font->ref++;
208     return font;
209 }
210
211 void FontUnref(NFont *font) {
212     if(--font->ref == 0) {
213         FontDestroy(font);
214     }
215 }