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