55ee7f515f3cfa0eb5277328f81da6919c39777b
[xscreensaver] / hacks / glx / glxfonts.c
1 /* glxfonts, Copyright (c) 2001-2008 Jamie Zawinski <jwz@jwz.org>
2  * Loads X11 fonts for use with OpenGL.
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 # include "config.h"
15 #endif
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <ctype.h>
21
22 #ifdef HAVE_COCOA
23 # include "jwxyz.h"
24 # include <OpenGL/gl.h>
25 # include <OpenGL/glu.h>
26 # include <AGL/agl.h>
27 #else
28 # include <GL/glx.h>
29 # include <GL/glu.h>
30 #endif
31
32 #include "resources.h"
33 #include "glxfonts.h"
34
35 /* These are in xlock-gl.c */
36 extern void clear_gl_error (void);
37 extern void check_gl_error (const char *type);
38
39 /* screenhack.h */
40 extern char *progname;
41
42
43 #undef DEBUG  /* Defining this causes check_gl_error() to be called inside
44                  time-critical sections, which could slow things down (since
45                  it might result in a round-trip, and stall of the pipeline.)
46                */
47
48
49 /* Loads the font named by the X resource "res".
50    Returns an XFontStruct.
51    Also converts the font to a set of GL lists and returns the first list.
52 */
53 void
54 load_font (Display *dpy, char *res, XFontStruct **font_ret, GLuint *dlist_ret)
55 {
56   XFontStruct *f;
57
58   const char *font = get_string_resource (dpy, res, "Font");
59   const char *def1 = "-*-helvetica-medium-r-normal-*-180-*";
60   const char *def2 = "fixed";
61   Font id;
62   int first, last;
63
64   if (!res || !*res) abort();
65   if (!font_ret && !dlist_ret) abort();
66
67   if (!font) font = def1;
68
69   f = XLoadQueryFont(dpy, font);
70   if (!f && !!strcmp (font, def1))
71     {
72       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
73                progname, font, def1);
74       font = def1;
75       f = XLoadQueryFont(dpy, font);
76     }
77
78   if (!f && !!strcmp (font, def2))
79     {
80       fprintf (stderr, "%s: unable to load font \"%s\", using \"%s\"\n",
81                progname, font, def2);
82       font = def2;
83       f = XLoadQueryFont(dpy, font);
84     }
85
86   if (!f)
87     {
88       fprintf (stderr, "%s: unable to load fallback font \"%s\" either!\n",
89                progname, font);
90       exit (1);
91     }
92
93   id = f->fid;
94   first = f->min_char_or_byte2;
95   last = f->max_char_or_byte2;
96   
97
98 # ifndef HAVE_COCOA /* Xlib version */
99
100   if (dlist_ret)
101     {
102       clear_gl_error ();
103       *dlist_ret = glGenLists ((GLuint) last+1);
104       check_gl_error ("glGenLists");
105       glXUseXFont(id, first, last-first+1, *dlist_ret + first);
106       check_gl_error ("glXUseXFont");
107     }
108
109 # else  /* HAVE_COCOA */
110
111   {
112     int afid, face, size;
113     afid = jwxyz_font_info (id, &size, &face);
114
115     if (dlist_ret)
116       {
117         clear_gl_error ();
118         *dlist_ret = glGenLists ((GLuint) last+1);
119         check_gl_error ("glGenLists");
120
121         AGLContext ctx = aglGetCurrentContext();
122         if (! aglUseFont (ctx, afid, face, size, 
123                           first, last-first+1, *dlist_ret + first)) {
124           check_gl_error ("aglUseFont");
125           abort();
126       }
127     }
128   }
129
130 # endif  /* HAVE_COCOA */
131
132   if (font_ret)
133     *font_ret = f;
134 }
135
136
137 /* Width (and optionally height) of the string in pixels.
138  */
139 int
140 string_width (XFontStruct *f, const char *c, int *height_ret)
141 {
142   int x = 0;
143   int max_w = 0;
144   int h = f->ascent + f->descent;
145   while (*c)
146     {
147       int cc = *((unsigned char *) c);
148       if (*c == '\n')
149         {
150           if (x > max_w) max_w = x;
151           x = 0;
152           h += f->ascent + f->descent;
153         }
154       else
155         x += (f->per_char
156               ? f->per_char[cc-f->min_char_or_byte2].width
157               : f->min_bounds.rbearing);
158       c++;
159     }
160   if (x > max_w) max_w = x;
161   if (height_ret) *height_ret = h;
162
163   return max_w;
164 }
165
166
167 /* Draws the string on the window at the given pixel position.
168    Newlines and tab stops are honored.
169    Any text inside [] will be rendered as a subscript.
170    Assumes the font has been loaded as with load_font().
171  */
172 void
173 print_gl_string (Display *dpy,
174                  XFontStruct *font,
175                  GLuint font_dlist,
176                  int window_width, int window_height,
177                  GLfloat x, GLfloat y,
178                  const char *string,
179                  Bool clear_background_p)
180 {
181   GLfloat line_height = font->ascent + font->descent;
182   GLfloat sub_shift = (line_height * 0.3);
183   int cw = string_width (font, "m", 0);
184   int tabs = cw * 7;
185
186   y -= line_height;
187
188   /* Sadly, this causes a stall of the graphics pipeline (as would the
189      equivalent calls to glGet*.)  But there's no way around this, short
190      of having each caller set up the specific display matrix we need
191      here, which would kind of defeat the purpose of centralizing this
192      code in one file.
193    */
194   glPushAttrib (GL_TRANSFORM_BIT |  /* for matrix contents */
195                 GL_ENABLE_BIT |     /* for various glDisable calls */
196                 GL_CURRENT_BIT |    /* for glColor3f() */
197                 GL_LIST_BIT);       /* for glListBase() */
198   {
199 # ifdef DEBUG
200     check_gl_error ("glPushAttrib");
201 # endif
202
203     /* disable lighting and texturing when drawing bitmaps!
204        (glPopAttrib() restores these.)
205      */
206     glDisable (GL_TEXTURE_2D);
207     glDisable (GL_LIGHTING);
208     glDisable (GL_BLEND);
209     glDisable (GL_DEPTH_TEST);
210     glDisable (GL_CULL_FACE);
211
212     /* glPopAttrib() does not restore matrix changes, so we must
213        push/pop the matrix stacks to be non-intrusive there.
214      */
215     glMatrixMode(GL_PROJECTION);
216     glPushMatrix();
217     {
218 # ifdef DEBUG
219       check_gl_error ("glPushMatrix");
220 # endif
221       glLoadIdentity();
222
223       /* Each matrix mode has its own stack, so we need to push/pop
224          them separately. */
225       glMatrixMode(GL_MODELVIEW);
226       glPushMatrix();
227       {
228         unsigned int i;
229         int x2 = x;
230         Bool sub_p = False;
231
232 # ifdef DEBUG
233         check_gl_error ("glPushMatrix");
234 # endif
235
236         glLoadIdentity();
237         gluOrtho2D (0, window_width, 0, window_height);
238 # ifdef DEBUG
239         check_gl_error ("gluOrtho2D");
240 # endif
241
242         if (clear_background_p)
243           {
244             int w, h;
245             int lh = font->ascent + font->descent;
246             w = string_width (font, string, &h);
247             glColor3f (0, 0, 0);
248             glRecti (x - font->descent,
249                      y + lh, 
250                      x + w + 2*font->descent,
251                      y + lh - h - font->descent);
252             glColor3f (1, 1, 1);
253           }
254
255         /* draw the text */
256         glRasterPos2f (x, y);
257 /*        glListBase (font_dlist);*/
258         for (i = 0; i < strlen(string); i++)
259           {
260             unsigned char c = (unsigned char) string[i];
261             if (c == '\n')
262               {
263                 glRasterPos2f (x, (y -= line_height));
264                 x2 = x;
265               }
266             else if (c == '\t')
267               {
268                 x2 -= x;
269                 x2 = ((x2 + tabs) / tabs) * tabs;  /* tab to tab stop */
270                 x2 += x;
271                 glRasterPos2f (x2, y);
272               }
273             else if (c == '[' && (isdigit (string[i+1])))
274               {
275                 sub_p = True;
276                 glRasterPos2f (x2, (y -= sub_shift));
277               }
278             else if (c == ']' && sub_p)
279               {
280                 sub_p = False;
281                 glRasterPos2f (x2, (y += sub_shift));
282               }
283             else
284               {
285 /*            glCallLists (s - string, GL_UNSIGNED_BYTE, string);*/
286                 glCallList (font_dlist + (int)(c));
287                 x2 += (font->per_char
288                        ? font->per_char[c - font->min_char_or_byte2].width
289                        : font->min_bounds.width);
290               }
291           }
292 # ifdef DEBUG
293         check_gl_error ("print_gl_string");
294 # endif
295       }
296       glPopMatrix();
297     }
298     glMatrixMode(GL_PROJECTION);
299     glPopMatrix();
300   }
301   glPopAttrib();
302 # ifdef DEBUG
303   check_gl_error ("glPopAttrib");
304 # endif
305
306   glMatrixMode(GL_MODELVIEW);
307 }