http://www.jwz.org/xscreensaver/xscreensaver-5.13.tar.gz
[xscreensaver] / hacks / glx / xlock-gl-utils.c
1 /* xlock-gl.c --- xscreensaver compatibility layer for xlockmore GL modules.
2  * xscreensaver, Copyright (c) 1997-2008 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 "xlockmoreI.h"
19 #include "glxfonts.h"
20
21 #include <GL/gl.h>
22 #include <GL/glu.h>
23 #include <GL/glx.h>
24
25 #ifndef isupper
26 # define isupper(c)  ((c) >= 'A' && (c) <= 'Z')
27 #endif
28 #ifndef _tolower
29 # define _tolower(c)  ((c) - 'A' + 'a')
30 #endif
31
32
33 /* Gag -- we use this to turn X errors from glXCreateContext() into
34    something that will actually make sense to the user.
35  */
36 static XErrorHandler orig_ehandler = 0;
37 static Bool got_error = 0;
38
39 static int
40 BadValue_ehandler (Display *dpy, XErrorEvent *error)
41 {
42   if (error->error_code == BadValue)
43     {
44       got_error = True;
45       return 0;
46     }
47   else
48     return orig_ehandler (dpy, error);
49 }
50
51
52 GLXContext *
53 init_GL(ModeInfo * mi)
54 {
55   Display *dpy = mi->dpy;
56   Window window = mi->window;
57   Screen *screen = mi->xgwa.screen;
58   Visual *visual = mi->xgwa.visual;
59   GLXContext glx_context = 0;
60   XVisualInfo vi_in, *vi_out;
61   int out_count;
62
63   vi_in.screen = screen_number (screen);
64   vi_in.visualid = XVisualIDFromVisual (visual);
65   vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
66                            &vi_in, &out_count);
67   if (! vi_out) abort ();
68
69   {
70     XSync (dpy, False);
71     orig_ehandler = XSetErrorHandler (BadValue_ehandler);
72     glx_context = glXCreateContext (dpy, vi_out, 0, GL_TRUE);
73     XSync (dpy, False);
74     XSetErrorHandler (orig_ehandler);
75     if (got_error)
76       glx_context = 0;
77   }
78
79   XFree((char *) vi_out);
80
81   if (!glx_context)
82     {
83       fprintf(stderr, "%s: couldn't create GL context for visual 0x%x.\n",
84               progname, (unsigned int) XVisualIDFromVisual (visual));
85       exit(1);
86     }
87
88   glXMakeCurrent (dpy, window, glx_context);
89
90   {
91     GLboolean rgba_mode = 0;
92     glGetBooleanv(GL_RGBA_MODE, &rgba_mode);
93     if (!rgba_mode)
94       {
95         glIndexi (WhitePixelOfScreen (screen));
96         glClearIndex (BlackPixelOfScreen (screen));
97       }
98   }
99
100
101   /* jwz: the doc for glDrawBuffer says "The initial value is GL_FRONT
102      for single-buffered contexts, and GL_BACK for double-buffered
103      contexts."  However, I find that this is not always the case,
104      at least with Mesa 3.4.2 -- sometimes the default seems to be
105      GL_FRONT even when glGet(GL_DOUBLEBUFFER) is true.  So, let's
106      make sure.
107
108      Oh, hmm -- maybe this only happens when we are re-using the
109      xscreensaver window, and the previous GL hack happened to die with
110      the other buffer selected?  I'm not sure.  Anyway, this fixes it.
111    */
112   {
113     GLboolean d = False;
114     glGetBooleanv (GL_DOUBLEBUFFER, &d);
115     if (d)
116       glDrawBuffer (GL_BACK);
117     else
118       glDrawBuffer (GL_FRONT);
119   }
120
121   /* Sometimes glDrawBuffer() throws "invalid op". Dunno why. Ignore. */
122   clear_gl_error ();
123
124   /* Process the -background argument. */
125   {
126     char *s = get_string_resource(mi->dpy, "background", "Background");
127     XColor c = { 0, };
128     if (! XParseColor (dpy, mi->xgwa.colormap, s, &c))
129       fprintf (stderr, "%s: can't parse color %s; using black.\n", 
130                progname, s);
131     glClearColor (c.red   / 65535.0,
132                   c.green / 65535.0,
133                   c.blue  / 65535.0,
134                   1.0);
135   }
136
137   /* GLXContext is already a pointer type.
138      Why this function returns a pointer to a pointer, I have no idea...
139    */
140   {
141     GLXContext *ptr = (GLXContext *) malloc(sizeof(GLXContext));
142     *ptr = glx_context;
143     return ptr;
144   }
145 }
146
147
148 \f
149
150 /* clear away any lingering error codes */
151 void
152 clear_gl_error (void)
153 {
154   while (glGetError() != GL_NO_ERROR)
155     ;
156 }
157
158 /* report a GL error. */
159 void
160 check_gl_error (const char *type)
161 {
162   char buf[100];
163   GLenum i;
164   const char *e;
165   switch ((i = glGetError())) {
166   case GL_NO_ERROR: return;
167   case GL_INVALID_ENUM:          e = "invalid enum";      break;
168   case GL_INVALID_VALUE:         e = "invalid value";     break;
169   case GL_INVALID_OPERATION:     e = "invalid operation"; break;
170   case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
171   case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
172   case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
173 #ifdef GL_TABLE_TOO_LARGE_EXT
174   case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
175 #endif
176 #ifdef GL_TEXTURE_TOO_LARGE_EXT
177   case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
178 #endif
179   default:
180     e = buf; sprintf (buf, "unknown error %d", (int) i); break;
181   }
182   fprintf (stderr, "%s: %s error: %s\n", progname, type, e);
183   exit (1);
184 }
185
186
187 /* Callback in xscreensaver_function_table, via xlockmore.c.
188  */
189 Visual *
190 xlockmore_pick_gl_visual (Screen *screen)
191 {
192   /* pick the "best" visual by interrogating the GL library instead of
193      by asking Xlib.  GL knows better.
194    */
195   Visual *v = 0;
196   Display *dpy = DisplayOfScreen (screen);
197   char *string = get_string_resource (dpy, "visualID", "VisualID");
198   char *s;
199
200   if (string)
201     for (s = string; *s; s++)
202       if (isupper (*s)) *s = _tolower (*s);
203
204   if (!string || !*string ||
205       !strcmp (string, "gl") ||
206       !strcmp (string, "best") ||
207       !strcmp (string, "color") ||
208       !strcmp (string, "default"))
209     v = get_gl_visual (screen);         /* from ../utils/visual-gl.c */
210
211   if (string)
212     free (string);
213
214   return v;
215 }
216
217
218 /* Callback in xscreensaver_function_table, via xlockmore.c.
219  */
220 Bool
221 xlockmore_validate_gl_visual (Screen *screen, const char *name, Visual *visual)
222 {
223   return validate_gl_visual (stderr, screen, name, visual);
224 }