1 /* xlock-gl.c --- xscreensaver compatibility layer for xlockmore GL modules.
2 * xscreensaver, Copyright (c) 1997-2015 Jamie Zawinski <jwz@jwz.org>
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
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.
18 #include "xlockmoreI.h"
22 # define isupper(c) ((c) >= 'A' && (c) <= 'Z')
25 # define _tolower(c) ((c) - 'A' + 'a')
29 /* Gag -- we use this to turn X errors from glXCreateContext() into
30 something that will actually make sense to the user.
32 static XErrorHandler orig_ehandler = 0;
33 static Bool got_error = 0;
36 BadValue_ehandler (Display *dpy, XErrorEvent *error)
38 if (error->error_code == BadValue)
44 return orig_ehandler (dpy, error);
49 init_GL(ModeInfo * mi)
51 Display *dpy = mi->dpy;
52 Window window = mi->window;
53 Screen *screen = mi->xgwa.screen;
54 Visual *visual = mi->xgwa.visual;
55 XVisualInfo vi_in, *vi_out;
58 if (mi->glx_context) {
59 glXMakeCurrent (dpy, window, mi->glx_context);
60 return &mi->glx_context;
64 jwzgles_make_current(jwzgles_make_state(state));
67 vi_in.screen = screen_number (screen);
68 vi_in.visualid = XVisualIDFromVisual (visual);
69 vi_out = XGetVisualInfo (dpy, VisualScreenMask|VisualIDMask,
71 if (! vi_out) abort ();
75 orig_ehandler = XSetErrorHandler (BadValue_ehandler);
76 mi->glx_context = glXCreateContext (dpy, vi_out, 0, GL_TRUE);
78 XSetErrorHandler (orig_ehandler);
83 XFree((char *) vi_out);
87 fprintf(stderr, "%s: couldn't create GL context for visual 0x%x.\n",
88 progname, (unsigned int) XVisualIDFromVisual (visual));
92 glXMakeCurrent (dpy, window, mi->glx_context);
95 GLboolean rgba_mode = 0;
96 glGetBooleanv(GL_RGBA_MODE, &rgba_mode);
99 glIndexi (WhitePixelOfScreen (screen));
100 glClearIndex (BlackPixelOfScreen (screen));
105 /* jwz: the doc for glDrawBuffer says "The initial value is GL_FRONT
106 for single-buffered contexts, and GL_BACK for double-buffered
107 contexts." However, I find that this is not always the case,
108 at least with Mesa 3.4.2 -- sometimes the default seems to be
109 GL_FRONT even when glGet(GL_DOUBLEBUFFER) is true. So, let's
112 Oh, hmm -- maybe this only happens when we are re-using the
113 xscreensaver window, and the previous GL hack happened to die with
114 the other buffer selected? I'm not sure. Anyway, this fixes it.
118 glGetBooleanv (GL_DOUBLEBUFFER, &d);
120 glDrawBuffer (GL_BACK);
122 glDrawBuffer (GL_FRONT);
125 /* Sometimes glDrawBuffer() throws "invalid op". Dunno why. Ignore. */
128 /* Process the -background argument. */
130 char *s = get_string_resource(mi->dpy, "background", "Background");
132 if (! XParseColor (dpy, mi->xgwa.colormap, s, &c))
133 fprintf (stderr, "%s: can't parse color %s; using black.\n",
135 glClearColor (c.red / 65535.0,
141 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
143 /* GLXContext is already a pointer type.
144 Why this function returns a pointer to a pointer, I have no idea...
146 return &mi->glx_context;
151 /* clear away any lingering error codes */
153 clear_gl_error (void)
155 while (glGetError() != GL_NO_ERROR)
159 /* report a GL error. */
161 check_gl_error (const char *type)
166 switch ((i = glGetError())) {
167 case GL_NO_ERROR: return;
168 case GL_INVALID_ENUM: e = "invalid enum"; break;
169 case GL_INVALID_VALUE: e = "invalid value"; break;
170 case GL_INVALID_OPERATION: e = "invalid operation"; break;
171 case GL_STACK_OVERFLOW: e = "stack overflow"; break;
172 case GL_STACK_UNDERFLOW: e = "stack underflow"; break;
173 case GL_OUT_OF_MEMORY: e = "out of memory"; break;
174 #ifdef GL_INVALID_FRAMEBUFFER_OPERATION
175 case GL_INVALID_FRAMEBUFFER_OPERATION:
176 e = "invalid framebuffer operation";
179 #ifdef GL_TABLE_TOO_LARGE_EXT
180 case GL_TABLE_TOO_LARGE_EXT: e = "table too large"; break;
182 #ifdef GL_TEXTURE_TOO_LARGE_EXT
183 case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
186 e = buf; sprintf (buf, "unknown error %d", (int) i); break;
188 fprintf (stderr, "%s: %s error: %s\n", progname, type, e);
193 /* Callback in xscreensaver_function_table, via xlockmore.c.
196 xlockmore_pick_gl_visual (Screen *screen)
198 /* pick the "best" visual by interrogating the GL library instead of
199 by asking Xlib. GL knows better.
202 Display *dpy = DisplayOfScreen (screen);
203 char *string = get_string_resource (dpy, "visualID", "VisualID");
207 for (s = string; *s; s++)
208 if (isupper (*s)) *s = _tolower (*s);
210 if (!string || !*string ||
211 !strcmp (string, "gl") ||
212 !strcmp (string, "best") ||
213 !strcmp (string, "color") ||
214 !strcmp (string, "default"))
215 v = get_gl_visual (screen); /* from ../utils/visual-gl.c */
224 /* Callback in xscreensaver_function_table, via xlockmore.c.
227 xlockmore_validate_gl_visual (Screen *screen, const char *name, Visual *visual)
229 return validate_gl_visual (stderr, screen, name, visual);