d72b4ebdd4aa4ad0d4884893441d05c3e4aecec0
[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
122   /* GLXContext is already a pointer type.
123      Why this function returns a pointer to a pointer, I have no idea...
124    */
125   {
126     GLXContext *ptr = (GLXContext *) malloc(sizeof(GLXContext));
127     *ptr = glx_context;
128     return ptr;
129   }
130 }
131
132
133 \f
134
135 /* clear away any lingering error codes */
136 void
137 clear_gl_error (void)
138 {
139   while (glGetError() != GL_NO_ERROR)
140     ;
141 }
142
143 /* report a GL error. */
144 void
145 check_gl_error (const char *type)
146 {
147   char buf[100];
148   GLenum i;
149   const char *e;
150   switch ((i = glGetError())) {
151   case GL_NO_ERROR: return;
152   case GL_INVALID_ENUM:          e = "invalid enum";      break;
153   case GL_INVALID_VALUE:         e = "invalid value";     break;
154   case GL_INVALID_OPERATION:     e = "invalid operation"; break;
155   case GL_STACK_OVERFLOW:        e = "stack overflow";    break;
156   case GL_STACK_UNDERFLOW:       e = "stack underflow";   break;
157   case GL_OUT_OF_MEMORY:         e = "out of memory";     break;
158 #ifdef GL_TABLE_TOO_LARGE_EXT
159   case GL_TABLE_TOO_LARGE_EXT:   e = "table too large";   break;
160 #endif
161 #ifdef GL_TEXTURE_TOO_LARGE_EXT
162   case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
163 #endif
164   default:
165     e = buf; sprintf (buf, "unknown error %d", (int) i); break;
166   }
167   fprintf (stderr, "%s: %s error: %s\n", progname, type, e);
168   exit (1);
169 }
170
171
172 /* Callback in xscreensaver_function_table, via xlockmore.c.
173  */
174 Visual *
175 xlockmore_pick_gl_visual (Screen *screen)
176 {
177   /* pick the "best" visual by interrogating the GL library instead of
178      by asking Xlib.  GL knows better.
179    */
180   Visual *v = 0;
181   Display *dpy = DisplayOfScreen (screen);
182   char *string = get_string_resource (dpy, "visualID", "VisualID");
183   char *s;
184
185   if (string)
186     for (s = string; *s; s++)
187       if (isupper (*s)) *s = _tolower (*s);
188
189   if (!string || !*string ||
190       !strcmp (string, "gl") ||
191       !strcmp (string, "best") ||
192       !strcmp (string, "color") ||
193       !strcmp (string, "default"))
194     v = get_gl_visual (screen);         /* from ../utils/visual-gl.c */
195
196   if (string)
197     free (string);
198
199   return v;
200 }
201
202
203 /* Callback in xscreensaver_function_table, via xlockmore.c.
204  */
205 Bool
206 xlockmore_validate_gl_visual (Screen *screen, const char *name, Visual *visual)
207 {
208   return validate_gl_visual (stderr, screen, name, visual);
209 }