test/testshader.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 10 Dec 2017 09:17:33 -0800
changeset 11758 c70cf178aacb
parent 10737 3406a0f8b041
permissions -rw-r--r--
Workaround for bug 3931 - spurious SDL_MOUSEMOTION events with SDL_HINT_MOUSE_RELATIVE_MODE_WARP 1 since Windows 10 Fall Creators update

Elisée Maurer

The attached minimal program sets the SDL_HINT_MOUSE_RELATIVE_MODE_WARP to 1, enables relative mouse mode then logs all SDL_MOUSEMOTION xrel values as they happen.

When moving the mouse exclusively to the right:

* On a Windows 10 installation before Fall Creators update (for instance, Version 10.0.15063 Build 15063), only positive values are reported, as expected
* On a Windows 10 installation after Fall Creators update (for instance, Version 10.0.16299 Update 16299), a mix of positive and negative values are reported.

3 different people have reproduced this bug and have confirmed it started to happen after the Fall Creators update was installed. It happens with SDL 2.0.7 as well as latest default branch as of today.

It seems like some obscure (maybe unintended) Windows behavior change? Haven't been able to pin it down more yet.

(To force-upgrade a Windows installation to the Fall Creators update, you can use the update assistant at https://www.microsoft.com/en-us/software-download/windows10)

Eric Wasylishen

Broken GetCursorPos / SetCursorPos based games on Win 10 fall creators are not limited to SDL.. I just tested winquake.exe (original 1997 exe) and it now has "jumps" in the mouse input if you try to look around in a circle. It uses GetCursorPos/SetCursorPos by default. Switching WinQuake to use directinput (-dinput flag) seems to get rid of the jumps.

Daniel Gibson

A friend tested on Win10 1607 (which is before the Fall Creators Update) and the the bug doesn't occur there, so the regression that SetCursorPos() doesn't reliably generate mouse events was indeed introduced with that update.
I even reproduced it in a minimal WinAPI-only application (https://gist.github.com/DanielGibson/b5b033c67b9137f0280af9fc53352c68), the weird thing is that if you don't do anything each "frame" (i.e. the mainloop only polls the events and does nothing else), there are a lot of mouse events with the coordinates you passed to SetCursorPos(), but when sleeping for 10ms in each iteration of the mainloop, those events basically don't happen anymore. Which is bad, because in games the each iteration of the mainloop usually takes 16ms..

I have a patch now that I find acceptable.
It checks for the windows version with RtlGetVersion() (https://msdn.microsoft.com/en-us/library/windows/hardware/ff561910.aspx) and only if it's >= Win10 build 16299, enables the workaround.
All code is in video/windows/SDL_windowsevents.c
and the workaround is, that for each WM_MOUSEMOVE event, "if(isWin10FCUorNewer && mouseID != SDL_TOUCH_MOUSEID && mouse->relative_mode_warp)", an addition mouse move event is generated with the coordinates of the center of the screen
(SDL_SendMouseMotion(data->window, mouseID, 0, center_x, center_y);) - which is exactly what would happen if windows generated those reliably itself.
This will cause SDL_PrivateSendMouseMotion() to set mouse->last_x = center_x; and mouse->last_y = center_y; so the next mouse relative mouse event will be calculated correctly.

If Microsoft ever fixes this bug, the IsWin10FCUorNewer() function would have to
be adjusted to also check for a maximum version, so the workaround is then disabled again.
     1 /*
     2   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     3 
     4   This software is provided 'as-is', without any express or implied
     5   warranty.  In no event will the authors be held liable for any damages
     6   arising from the use of this software.
     7 
     8   Permission is granted to anyone to use this software for any purpose,
     9   including commercial applications, and to alter it and redistribute it
    10   freely.
    11 */
    12 /* This is a simple example of using GLSL shaders with SDL */
    13 
    14 #include "SDL.h"
    15 
    16 #ifdef HAVE_OPENGL
    17 
    18 #include "SDL_opengl.h"
    19 
    20 
    21 static SDL_bool shaders_supported;
    22 static int      current_shader = 0;
    23 
    24 enum {
    25     SHADER_COLOR,
    26     SHADER_TEXTURE,
    27     SHADER_TEXCOORDS,
    28     NUM_SHADERS
    29 };
    30 
    31 typedef struct {
    32     GLhandleARB program;
    33     GLhandleARB vert_shader;
    34     GLhandleARB frag_shader;
    35     const char *vert_source;
    36     const char *frag_source;
    37 } ShaderData;
    38 
    39 static ShaderData shaders[NUM_SHADERS] = {
    40 
    41     /* SHADER_COLOR */
    42     { 0, 0, 0,
    43         /* vertex shader */
    44 "varying vec4 v_color;\n"
    45 "\n"
    46 "void main()\n"
    47 "{\n"
    48 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
    49 "    v_color = gl_Color;\n"
    50 "}",
    51         /* fragment shader */
    52 "varying vec4 v_color;\n"
    53 "\n"
    54 "void main()\n"
    55 "{\n"
    56 "    gl_FragColor = v_color;\n"
    57 "}"
    58     },
    59 
    60     /* SHADER_TEXTURE */
    61     { 0, 0, 0,
    62         /* vertex shader */
    63 "varying vec4 v_color;\n"
    64 "varying vec2 v_texCoord;\n"
    65 "\n"
    66 "void main()\n"
    67 "{\n"
    68 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
    69 "    v_color = gl_Color;\n"
    70 "    v_texCoord = vec2(gl_MultiTexCoord0);\n"
    71 "}",
    72         /* fragment shader */
    73 "varying vec4 v_color;\n"
    74 "varying vec2 v_texCoord;\n"
    75 "uniform sampler2D tex0;\n"
    76 "\n"
    77 "void main()\n"
    78 "{\n"
    79 "    gl_FragColor = texture2D(tex0, v_texCoord) * v_color;\n"
    80 "}"
    81     },
    82 
    83     /* SHADER_TEXCOORDS */
    84     { 0, 0, 0,
    85         /* vertex shader */
    86 "varying vec2 v_texCoord;\n"
    87 "\n"
    88 "void main()\n"
    89 "{\n"
    90 "    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
    91 "    v_texCoord = vec2(gl_MultiTexCoord0);\n"
    92 "}",
    93         /* fragment shader */
    94 "varying vec2 v_texCoord;\n"
    95 "\n"
    96 "void main()\n"
    97 "{\n"
    98 "    vec4 color;\n"
    99 "    vec2 delta;\n"
   100 "    float dist;\n"
   101 "\n"
   102 "    delta = vec2(0.5, 0.5) - v_texCoord;\n"
   103 "    dist = dot(delta, delta);\n"
   104 "\n"
   105 "    color.r = v_texCoord.x;\n"
   106 "    color.g = v_texCoord.x * v_texCoord.y;\n"
   107 "    color.b = v_texCoord.y;\n"
   108 "    color.a = 1.0 - (dist * 4.0);\n"
   109 "    gl_FragColor = color;\n"
   110 "}"
   111     },
   112 };
   113 
   114 static PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
   115 static PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
   116 static PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
   117 static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
   118 static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
   119 static PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
   120 static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
   121 static PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
   122 static PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
   123 static PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
   124 static PFNGLUNIFORM1IARBPROC glUniform1iARB;
   125 static PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
   126 
   127 static SDL_bool CompileShader(GLhandleARB shader, const char *source)
   128 {
   129     GLint status;
   130 
   131     glShaderSourceARB(shader, 1, &source, NULL);
   132     glCompileShaderARB(shader);
   133     glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
   134     if (status == 0) {
   135         GLint length;
   136         char *info;
   137 
   138         glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &length);
   139         info = SDL_stack_alloc(char, length+1);
   140         glGetInfoLogARB(shader, length, NULL, info);
   141         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Failed to compile shader:\n%s\n%s", source, info);
   142         SDL_stack_free(info);
   143 
   144         return SDL_FALSE;
   145     } else {
   146         return SDL_TRUE;
   147     }
   148 }
   149 
   150 static SDL_bool CompileShaderProgram(ShaderData *data)
   151 {
   152     const int num_tmus_bound = 4;
   153     int i;
   154     GLint location;
   155 
   156     glGetError();
   157 
   158     /* Create one program object to rule them all */
   159     data->program = glCreateProgramObjectARB();
   160 
   161     /* Create the vertex shader */
   162     data->vert_shader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
   163     if (!CompileShader(data->vert_shader, data->vert_source)) {
   164         return SDL_FALSE;
   165     }
   166 
   167     /* Create the fragment shader */
   168     data->frag_shader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
   169     if (!CompileShader(data->frag_shader, data->frag_source)) {
   170         return SDL_FALSE;
   171     }
   172 
   173     /* ... and in the darkness bind them */
   174     glAttachObjectARB(data->program, data->vert_shader);
   175     glAttachObjectARB(data->program, data->frag_shader);
   176     glLinkProgramARB(data->program);
   177 
   178     /* Set up some uniform variables */
   179     glUseProgramObjectARB(data->program);
   180     for (i = 0; i < num_tmus_bound; ++i) {
   181         char tex_name[5];
   182         SDL_snprintf(tex_name, SDL_arraysize(tex_name), "tex%d", i);
   183         location = glGetUniformLocationARB(data->program, tex_name);
   184         if (location >= 0) {
   185             glUniform1iARB(location, i);
   186         }
   187     }
   188     glUseProgramObjectARB(0);
   189 
   190     return (glGetError() == GL_NO_ERROR) ? SDL_TRUE : SDL_FALSE;
   191 }
   192 
   193 static void DestroyShaderProgram(ShaderData *data)
   194 {
   195     if (shaders_supported) {
   196         glDeleteObjectARB(data->vert_shader);
   197         glDeleteObjectARB(data->frag_shader);
   198         glDeleteObjectARB(data->program);
   199     }
   200 }
   201 
   202 static SDL_bool InitShaders()
   203 {
   204     int i;
   205 
   206     /* Check for shader support */
   207     shaders_supported = SDL_FALSE;
   208     if (SDL_GL_ExtensionSupported("GL_ARB_shader_objects") &&
   209         SDL_GL_ExtensionSupported("GL_ARB_shading_language_100") &&
   210         SDL_GL_ExtensionSupported("GL_ARB_vertex_shader") &&
   211         SDL_GL_ExtensionSupported("GL_ARB_fragment_shader")) {
   212         glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB");
   213         glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB");
   214         glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB");
   215         glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB");
   216         glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB");
   217         glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB");
   218         glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB");
   219         glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB");
   220         glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB");
   221         glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB");
   222         glUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB");
   223         glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB");
   224         if (glAttachObjectARB &&
   225             glCompileShaderARB &&
   226             glCreateProgramObjectARB &&
   227             glCreateShaderObjectARB &&
   228             glDeleteObjectARB &&
   229             glGetInfoLogARB &&
   230             glGetObjectParameterivARB &&
   231             glGetUniformLocationARB &&
   232             glLinkProgramARB &&
   233             glShaderSourceARB &&
   234             glUniform1iARB &&
   235             glUseProgramObjectARB) {
   236             shaders_supported = SDL_TRUE;
   237         }
   238     }
   239 
   240     if (!shaders_supported) {
   241         return SDL_FALSE;
   242     }
   243 
   244     /* Compile all the shaders */
   245     for (i = 0; i < NUM_SHADERS; ++i) {
   246         if (!CompileShaderProgram(&shaders[i])) {
   247             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to compile shader!\n");
   248             return SDL_FALSE;
   249         }
   250     }
   251 
   252     /* We're done! */
   253     return SDL_TRUE;
   254 }
   255 
   256 static void QuitShaders()
   257 {
   258     int i;
   259 
   260     for (i = 0; i < NUM_SHADERS; ++i) {
   261         DestroyShaderProgram(&shaders[i]);
   262     }
   263 }
   264 
   265 /* Quick utility function for texture creation */
   266 static int
   267 power_of_two(int input)
   268 {
   269     int value = 1;
   270 
   271     while (value < input) {
   272         value <<= 1;
   273     }
   274     return value;
   275 }
   276 
   277 GLuint
   278 SDL_GL_LoadTexture(SDL_Surface * surface, GLfloat * texcoord)
   279 {
   280     GLuint texture;
   281     int w, h;
   282     SDL_Surface *image;
   283     SDL_Rect area;
   284     SDL_BlendMode saved_mode;
   285 
   286     /* Use the surface width and height expanded to powers of 2 */
   287     w = power_of_two(surface->w);
   288     h = power_of_two(surface->h);
   289     texcoord[0] = 0.0f;         /* Min X */
   290     texcoord[1] = 0.0f;         /* Min Y */
   291     texcoord[2] = (GLfloat) surface->w / w;     /* Max X */
   292     texcoord[3] = (GLfloat) surface->h / h;     /* Max Y */
   293 
   294     image = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32,
   295 #if SDL_BYTEORDER == SDL_LIL_ENDIAN     /* OpenGL RGBA masks */
   296                                  0x000000FF,
   297                                  0x0000FF00, 0x00FF0000, 0xFF000000
   298 #else
   299                                  0xFF000000,
   300                                  0x00FF0000, 0x0000FF00, 0x000000FF
   301 #endif
   302         );
   303     if (image == NULL) {
   304         return 0;
   305     }
   306 
   307     /* Save the alpha blending attributes */
   308     SDL_GetSurfaceBlendMode(surface, &saved_mode);
   309     SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_NONE);
   310 
   311     /* Copy the surface into the GL texture image */
   312     area.x = 0;
   313     area.y = 0;
   314     area.w = surface->w;
   315     area.h = surface->h;
   316     SDL_BlitSurface(surface, &area, image, &area);
   317 
   318     /* Restore the alpha blending attributes */
   319     SDL_SetSurfaceBlendMode(surface, saved_mode);
   320 
   321     /* Create an OpenGL texture for the image */
   322     glGenTextures(1, &texture);
   323     glBindTexture(GL_TEXTURE_2D, texture);
   324     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
   325     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
   326     glTexImage2D(GL_TEXTURE_2D,
   327                  0,
   328                  GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, image->pixels);
   329     SDL_FreeSurface(image);     /* No longer needed */
   330 
   331     return texture;
   332 }
   333 
   334 /* A general OpenGL initialization function.    Sets all of the initial parameters. */
   335 void InitGL(int Width, int Height)                    /* We call this right after our OpenGL window is created. */
   336 {
   337     GLdouble aspect;
   338 
   339     glViewport(0, 0, Width, Height);
   340     glClearColor(0.0f, 0.0f, 0.0f, 0.0f);        /* This Will Clear The Background Color To Black */
   341     glClearDepth(1.0);                /* Enables Clearing Of The Depth Buffer */
   342     glDepthFunc(GL_LESS);                /* The Type Of Depth Test To Do */
   343     glEnable(GL_DEPTH_TEST);            /* Enables Depth Testing */
   344     glShadeModel(GL_SMOOTH);            /* Enables Smooth Color Shading */
   345 
   346     glMatrixMode(GL_PROJECTION);
   347     glLoadIdentity();                /* Reset The Projection Matrix */
   348 
   349     aspect = (GLdouble)Width / Height;
   350     glOrtho(-3.0, 3.0, -3.0 / aspect, 3.0 / aspect, 0.0, 1.0);
   351 
   352     glMatrixMode(GL_MODELVIEW);
   353 }
   354 
   355 /* The main drawing function. */
   356 void DrawGLScene(SDL_Window *window, GLuint texture, GLfloat * texcoord)
   357 {
   358     /* Texture coordinate lookup, to make it simple */
   359     enum {
   360         MINX,
   361         MINY,
   362         MAXX,
   363         MAXY
   364     };
   365 
   366     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);        /* Clear The Screen And The Depth Buffer */
   367     glLoadIdentity();                /* Reset The View */
   368 
   369     glTranslatef(-1.5f,0.0f,0.0f);        /* Move Left 1.5 Units */
   370 
   371     /* draw a triangle (in smooth coloring mode) */
   372     glBegin(GL_POLYGON);                /* start drawing a polygon */
   373     glColor3f(1.0f,0.0f,0.0f);            /* Set The Color To Red */
   374     glVertex3f( 0.0f, 1.0f, 0.0f);        /* Top */
   375     glColor3f(0.0f,1.0f,0.0f);            /* Set The Color To Green */
   376     glVertex3f( 1.0f,-1.0f, 0.0f);        /* Bottom Right */
   377     glColor3f(0.0f,0.0f,1.0f);            /* Set The Color To Blue */
   378     glVertex3f(-1.0f,-1.0f, 0.0f);        /* Bottom Left */
   379     glEnd();                    /* we're done with the polygon (smooth color interpolation) */
   380 
   381     glTranslatef(3.0f,0.0f,0.0f);         /* Move Right 3 Units */
   382 
   383     /* Enable blending */
   384     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
   385     glEnable(GL_BLEND);
   386     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   387 
   388     /* draw a textured square (quadrilateral) */
   389     glEnable(GL_TEXTURE_2D);
   390     glBindTexture(GL_TEXTURE_2D, texture);
   391     glColor3f(1.0f,1.0f,1.0f);
   392     if (shaders_supported) {
   393         glUseProgramObjectARB(shaders[current_shader].program);
   394     }
   395 
   396     glBegin(GL_QUADS);                /* start drawing a polygon (4 sided) */
   397     glTexCoord2f(texcoord[MINX], texcoord[MINY]);
   398     glVertex3f(-1.0f, 1.0f, 0.0f);        /* Top Left */
   399     glTexCoord2f(texcoord[MAXX], texcoord[MINY]);
   400     glVertex3f( 1.0f, 1.0f, 0.0f);        /* Top Right */
   401     glTexCoord2f(texcoord[MAXX], texcoord[MAXY]);
   402     glVertex3f( 1.0f,-1.0f, 0.0f);        /* Bottom Right */
   403     glTexCoord2f(texcoord[MINX], texcoord[MAXY]);
   404     glVertex3f(-1.0f,-1.0f, 0.0f);        /* Bottom Left */
   405     glEnd();                    /* done with the polygon */
   406 
   407     if (shaders_supported) {
   408         glUseProgramObjectARB(0);
   409     }
   410     glDisable(GL_TEXTURE_2D);
   411 
   412     /* swap buffers to display, since we're double buffered. */
   413     SDL_GL_SwapWindow(window);
   414 }
   415 
   416 int main(int argc, char **argv)
   417 {
   418     int done;
   419     SDL_Window *window;
   420     SDL_Surface *surface;
   421     GLuint texture;
   422     GLfloat texcoords[4];
   423 
   424     /* Enable standard application logging */
   425     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
   426 
   427     /* Initialize SDL for video output */
   428     if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
   429         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to initialize SDL: %s\n", SDL_GetError());
   430         exit(1);
   431     }
   432 
   433     /* Create a 640x480 OpenGL screen */
   434     window = SDL_CreateWindow( "Shader Demo", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_OPENGL );
   435     if ( !window ) {
   436         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL window: %s\n", SDL_GetError());
   437         SDL_Quit();
   438         exit(2);
   439     }
   440 
   441     if ( !SDL_GL_CreateContext(window)) {
   442         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to create OpenGL context: %s\n", SDL_GetError());
   443         SDL_Quit();
   444         exit(2);
   445     }
   446 
   447     surface = SDL_LoadBMP("icon.bmp");
   448     if ( ! surface ) {
   449         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Unable to load icon.bmp: %s\n", SDL_GetError());
   450         SDL_Quit();
   451         exit(3);
   452     }
   453     texture = SDL_GL_LoadTexture(surface, texcoords);
   454     SDL_FreeSurface(surface);
   455 
   456     /* Loop, drawing and checking events */
   457     InitGL(640, 480);
   458     if (InitShaders()) {
   459         SDL_Log("Shaders supported, press SPACE to cycle them.\n");
   460     } else {
   461         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Shaders not supported!\n");
   462     }
   463     done = 0;
   464     while ( ! done ) {
   465         DrawGLScene(window, texture, texcoords);
   466 
   467         /* This could go in a separate function */
   468         { SDL_Event event;
   469             while ( SDL_PollEvent(&event) ) {
   470                 if ( event.type == SDL_QUIT ) {
   471                     done = 1;
   472                 }
   473                 if ( event.type == SDL_KEYDOWN ) {
   474                     if ( event.key.keysym.sym == SDLK_SPACE ) {
   475                         current_shader = (current_shader + 1) % NUM_SHADERS;
   476                     }
   477                     if ( event.key.keysym.sym == SDLK_ESCAPE ) {
   478                         done = 1;
   479                     }
   480                 }
   481             }
   482         }
   483     }
   484     QuitShaders();
   485     SDL_Quit();
   486     return 1;
   487 }
   488 
   489 #else /* HAVE_OPENGL */
   490 
   491 int
   492 main(int argc, char *argv[])
   493 {
   494     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "No OpenGL support on this system\n");
   495     return 1;
   496 }
   497 
   498 #endif /* HAVE_OPENGL */
   499 
   500 /* vi: set ts=4 sw=4 expandtab: */