From http://www.jwz.org/xscreensaver/xscreensaver-5.30.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-2014 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 # ifdef HAVE_JWZGLES
60   jwzgles_reset();
61 # endif
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 \f
148
149 /* clear away any lingering error codes */
150 void
151 clear_gl_error (void)
152 {
153   while (glGetError() != GL_NO_ERROR)
154     ;
155 }
156
157 /* report a GL error. */
158 void
159 check_gl_error (const char *type)
160 {
161   char buf[100];
162   GLenum i;
163   const char *e;
164   switch ((i = glGetError())) {
165   case GL_NO_ERROR: return;
166   case GL_INVALID_ENUM:          e = "invalid enum";      break;
167   case GL_INVALID_VALUE:         e = "invalid value";     break;
168   case GL_INVALID_OPERATION:     e = "invalid operation"; break;
169   case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
170   case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
171   case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
172 #ifdef GL_TABLE_TOO_LARGE_EXT
173   case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
174 #endif
175 #ifdef GL_TEXTURE_TOO_LARGE_EXT
176   case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
177 #endif
178   default:
179     e = buf; sprintf (buf, "unknown error %d", (int) i); break;
180   }
181   fprintf (stderr, "%s: %s error: %s\n", progname, type, e);
182   exit (1);
183 }
184
185
186 /* Callback in xscreensaver_function_table, via xlockmore.c.
187  */
188 Visual *
189 xlockmore_pick_gl_visual (Screen *screen)
190 {
191   /* pick the "best" visual by interrogating the GL library instead of
192      by asking Xlib.  GL knows better.
193    */
194   Visual *v = 0;
195   Display *dpy = DisplayOfScreen (screen);
196   char *string = get_string_resource (dpy, "visualID", "VisualID");
197   char *s;
198
199   if (string)
200     for (s = string; *s; s++)
201       if (isupper (*s)) *s = _tolower (*s);
202
203   if (!string || !*string ||
204       !strcmp (string, "gl") ||
205       !strcmp (string, "best") ||
206       !strcmp (string, "color") ||
207       !strcmp (string, "default"))
208     v = get_gl_visual (screen);         /* from ../utils/visual-gl.c */
209
210   if (string)
211     free (string);
212
213   return v;
214 }
215
216
217 /* Callback in xscreensaver_function_table, via xlockmore.c.
218  */
219 Bool
220 xlockmore_validate_gl_visual (Screen *screen, const char *name, Visual *visual)
221 {
222   return validate_gl_visual (stderr, screen, name, visual);
223 }