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