http://packetstormsecurity.org/UNIX/admin/xscreensaver-3.29.tar.gz
[xscreensaver] / hacks / glx / xlock-gl.c
1 /* xlock-gl.c --- xscreensaver compatibility layer for xlockmore GL modules.
2  * xscreensaver, Copyright (c) 1997, 1998, 1999 Jamie Zawinski <jwz@jwz.org>
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  * This file, along with xlockmore.h, make it possible to compile an xlockmore
13  * GL module into a standalone program, and thus use it with xscreensaver.
14  * By Jamie Zawinski <jwz@jwz.org> on 31-May-97.
15  */
16
17 #include <stdio.h>
18 #include "screenhack.h"
19 #include "xlockmoreI.h"
20
21 #include <GL/gl.h>
22 #include <GL/glu.h>
23 #include <GL/glx.h>
24
25 /* Gag -- we use this to turn X errors from glXCreateContext() into
26    something that will actually make sense to the user.
27  */
28 static XErrorHandler orig_ehandler = 0;
29 static Bool got_error = 0;
30
31 static int
32 BadValue_ehandler (Display *dpy, XErrorEvent *error)
33 {
34   if (error->error_code == BadValue)
35     {
36       got_error = True;
37       return 0;
38     }
39   else
40     return orig_ehandler (dpy, error);
41 }
42
43
44 GLXContext *
45 init_GL(ModeInfo * mi)
46 {
47   Display *dpy = mi->dpy;
48   Window window = mi->window;
49   Screen *screen = mi->xgwa.screen;
50   Visual *visual = mi->xgwa.visual;
51   GLXContext glx_context = 0;
52   XVisualInfo vi_in, *vi_out;
53   int out_count;
54
55   vi_in.screen = screen_number (screen);
56   vi_in.visualid = XVisualIDFromVisual (visual);
57   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
58                            &vi_in, &out_count);
59   if (! vi_out) abort ();
60
61   {
62     XSync (dpy, False);
63     orig_ehandler = XSetErrorHandler (BadValue_ehandler);
64     glx_context = glXCreateContext (dpy, vi_out, 0, GL_TRUE);
65     XSync (dpy, False);
66     XSetErrorHandler (orig_ehandler);
67     if (got_error)
68       glx_context = 0;
69   }
70
71   XFree((char *) vi_out);
72
73   if (!glx_context)
74     {
75       fprintf(stderr, "%s: couldn't create GL context for visual 0x%x.\n",
76               progname, (unsigned int) XVisualIDFromVisual (visual));
77       exit(1);
78     }
79
80   glXMakeCurrent (dpy, window, glx_context);
81
82   {
83     GLboolean rgba_mode = 0;
84     glGetBooleanv(GL_RGBA_MODE, &rgba_mode);
85     if (!rgba_mode)
86       {
87         glIndexi (WhitePixelOfScreen (screen));
88         glClearIndex (BlackPixelOfScreen (screen));
89       }
90   }
91
92   /* GLXContext is already a pointer type.
93      Why this function returns a pointer to a pointer, I have no idea...
94    */
95   {
96     GLXContext *ptr = (GLXContext *) malloc(sizeof(GLXContext));
97     *ptr = glx_context;
98     return ptr;
99   }
100 }
101
102
103 \f
104
105 /* clear away any lingering error codes */
106 void
107 clear_gl_error (void)
108 {
109   while (glGetError() != GL_NO_ERROR)
110     ;
111 }
112
113 /* report a GL error. */
114 void
115 check_gl_error (const char *type)
116 {
117   char buf[100];
118   GLenum i;
119   const char *e;
120   switch ((i = glGetError())) {
121   case GL_NO_ERROR: return;
122   case GL_INVALID_ENUM:          e = "invalid enum";      break;
123   case GL_INVALID_VALUE:         e = "invalid value";     break;
124   case GL_INVALID_OPERATION:     e = "invalid operation"; break;
125   case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
126   case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
127   case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
128 #ifdef GL_TABLE_TOO_LARGE_EXT
129   case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
130 #endif
131 #ifdef GL_TEXTURE_TOO_LARGE_EXT
132   case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
133 #endif
134   default:
135     e = buf; sprintf (buf, "unknown error %d", (int) i); break;
136   }
137   fprintf (stderr, "%s: %s error: %s\n", progname, type, e);
138   exit (1);
139 }
140
141
142 \f
143
144 /* Frames-per-second statistics */
145
146 static int fps_text_x = 10;
147 static int fps_text_y = 10;
148 static int fps_sample_frames = 10;
149 static GLuint font_dlist;
150
151 static void
152 fps_init (ModeInfo *mi)
153 {
154   const char *font = get_string_resource ("fpsFont", "Font");
155   XFontStruct *f;
156   Font id;
157   int first, last;
158
159   if (!font) font = "-*-courier-bold-r-normal-*-180-*";
160   f = XLoadQueryFont(mi->dpy, font);
161   if (!f) f = XLoadQueryFont(mi->dpy, "fixed");
162
163   id = f->fid;
164   first = f->min_char_or_byte2;
165   last = f->max_char_or_byte2;
166   
167   clear_gl_error ();
168   font_dlist = glGenLists ((GLuint) last+1);
169   check_gl_error ("glGenLists");
170
171   if (get_boolean_resource ("fpsTop", "FPSTop"))
172     fps_text_y = - (f->ascent + 10);
173
174   glXUseXFont(id, first, last-first+1, font_dlist + first);
175   check_gl_error ("glXUseXFont");
176 }
177
178
179 static void
180 fps_print_string (ModeInfo *mi, GLfloat x, GLfloat y, const char *string)
181 {
182   int i;
183   /* save the current state */
184   /* note: could be expensive! */
185
186   if (y < 0)
187     y = mi->xgwa.height + y;
188
189   clear_gl_error ();
190   glPushAttrib(GL_ALL_ATTRIB_BITS);
191   {
192     check_gl_error ("glPushAttrib");
193
194     /* disable lighting and texturing when drawing bitmaps!
195        (glPopAttrib() restores these, I believe.)
196      */
197     glDisable(GL_TEXTURE_2D);
198     glDisable(GL_LIGHTING);
199     glDisable(GL_BLEND);
200
201     /* glPopAttrib() does not restore matrix changes, so we must
202        push/pop the matrix stacks to be non-intrusive there.
203      */
204     glMatrixMode(GL_PROJECTION);
205     glPushMatrix();
206     {
207       check_gl_error ("glPushMatrix");
208       glLoadIdentity();
209
210       /* Each matrix mode has its own stack, so we need to push/pop
211          them separately. */
212       glMatrixMode(GL_MODELVIEW);
213       glPushMatrix();
214       {
215         check_gl_error ("glPushMatrix");
216         glLoadIdentity();
217
218         gluOrtho2D(0, mi->xgwa.width, 0, mi->xgwa.height);
219         check_gl_error ("gluOrtho2D");
220
221         /* draw the text */
222         glColor3f (1, 1, 1);
223         glRasterPos2f (x, y);
224         for (i = 0; i < strlen(string); i++)
225           glCallList (font_dlist + (int)string[i]);
226
227         check_gl_error ("fps_print_string");
228       }
229       glPopMatrix();
230     }
231     glMatrixMode(GL_PROJECTION);
232     glPopMatrix();
233
234   }
235   /* clean up after our state changes */
236   glPopAttrib();
237   check_gl_error ("glPopAttrib");
238 }
239
240
241 void
242 do_fps (ModeInfo *mi)
243 {
244   /* every N frames, get the time and use it to get the frames per second */
245   static int frame_counter = -1;
246   static double oldtime = 0; /* time in usecs, as a double */
247   static double newtime = 0;
248
249   static char msg [1024] = { 0, };
250
251   if (frame_counter == -1)
252     {
253       fps_init (mi);
254       frame_counter = fps_sample_frames;
255     }
256
257   if (frame_counter++ == fps_sample_frames)
258     {
259       double fps;
260       struct timeval now;
261 # ifdef GETTIMEOFDAY_TWO_ARGS
262       struct timezone tzp;
263       gettimeofday(&now, &tzp);
264 # else
265       gettimeofday(&now);
266 # endif
267
268       oldtime = newtime;
269       newtime = now.tv_sec + ((double) now.tv_usec * 0.000001);
270
271       fps = fps_sample_frames / (newtime - oldtime);
272
273       if (fps < 0.0001)
274         {
275           strcpy(msg, "FPS: (accumulating...)");
276         }
277       else
278         {
279           sprintf(msg, "FPS: %.02f", fps);
280
281           if (mi->pause != 0)
282             {
283               char buf[40];
284               sprintf(buf, "%f", mi->pause / 1000000.0); /* FTSO C */
285               while(*buf && buf[strlen(buf)-1] == '0')
286                 buf[strlen(buf)-1] = 0;
287               if (buf[strlen(buf)-1] == '.')
288                 buf[strlen(buf)-1] = 0;
289               sprintf(msg + strlen(msg), " (including %s sec/frame delay)",
290                       buf);
291             }
292         }
293
294       frame_counter = 0;
295     }
296
297   fps_print_string (mi, fps_text_x, fps_text_y, msg);
298 }