1 
2 #if USE_SDL_VIDEO
3 
4 /*
5  * SDL implementation for Quake 3: Arena's GPL source release.
6  *
7  * I wrote such a beast originally for Loki's port of Heavy Metal: FAKK2,
8  *  and then wrote it again for the Linux client of Medal of Honor: Allied
9  *  Assault. Third time's a charm, so I'm rewriting this once more for the
10  *  GPL release of Quake 3.
11  *
12  * Written by Ryan C. Gordon (icculus@icculus.org). Please refer to
13  *    http://icculus.org/quake3/ for the latest version of this code.
14  *
15  *  Patches and comments are welcome at the above address.
16  *
17  * I cut-and-pasted this from linux_glimp.c, and moved it to SDL line-by-line.
18  *  There is probably some cruft that could be removed here.
19  *
20  * You should define USE_SDL=1 and then add this to the makefile.
21  *  USE_SDL will disable the X11 target.
22  */
23 
24 /*
25 Original copyright on Q3A sources:
26 ===========================================================================
27 Copyright (C) 1999-2005 Id Software, Inc.
28 Copyright (C) 2000-2006 Tim Angus
29 
30 This file is part of Tremulous.
31 
32 Tremulous is free software; you can redistribute it
33 and/or modify it under the terms of the GNU General Public License as
34 published by the Free Software Foundation; either version 2 of the License,
35 or (at your option) any later version.
36 
37 Tremulous is distributed in the hope that it will be
38 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
39 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
40 GNU General Public License for more details.
41 
42 You should have received a copy of the GNU General Public License
43 along with Tremulous; if not, write to the Free Software
44 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
45 ===========================================================================
46 */
47 /*
48 ** GLW_IMP.C
49 **
50 ** This file contains ALL Linux specific stuff having to do with the
51 ** OpenGL refresh.  When a port is being made the following functions
52 ** must be implemented by the port:
53 **
54 ** GLimp_EndFrame
55 ** GLimp_Init
56 ** GLimp_Shutdown
57 ** GLimp_SwitchFullscreen
58 ** GLimp_SetGamma
59 **
60 */
61 
62 #include "SDL.h"
63 
64 #ifdef SMP
65 #include "SDL_thread.h"
66 #endif
67 
68 #include <stdarg.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <dlfcn.h>
72 
73 #include "../renderer/tr_local.h"
74 #include "../client/client.h"
75 #include "linux_local.h" // bk001130
76 
77 #include "unix_glw.h"
78 
79 
80 /* Just hack it for now. */
81 #ifdef MACOS_X
82 typedef CGLContextObj QGLContext;
83 #define GLimp_GetCurrentContext() CGLGetCurrentContext()
84 #define GLimp_SetCurrentContext(ctx) CGLSetCurrentContext(ctx)
85 #else
86 typedef void *QGLContext;
87 #define GLimp_GetCurrentContext() (NULL)
88 #define GLimp_SetCurrentContext(ctx)
89 #endif
90 
91 static QGLContext opengl_context;
92 
93 #define	WINDOW_CLASS_NAME	"Tremulous"
94 #define	WINDOW_CLASS_NAME_BRIEF	"Tremulous"
95 
96 //#define KBD_DBG
97 
98 typedef enum
99 {
100   RSERR_OK,
101 
102   RSERR_INVALID_FULLSCREEN,
103   RSERR_INVALID_MODE,
104 
105   RSERR_UNKNOWN
106 } rserr_t;
107 
108 glwstate_t glw_state;
109 
110 static SDL_Surface *screen = NULL;
111 static SDL_Joystick *stick = NULL;
112 
113 static qboolean mouse_avail = qfalse;
114 static qboolean mouse_active = qfalse;
115 static qboolean sdlrepeatenabled = qfalse;
116 
117 static cvar_t *in_mouse;
118 cvar_t *in_subframe;
119 cvar_t *in_nograb; // this is strictly for developers
120 
121 // bk001130 - from cvs1.17 (mkv), but not static
122 cvar_t   *in_joystick      = NULL;
123 cvar_t   *in_joystickDebug = NULL;
124 cvar_t   *joy_threshold    = NULL;
125 
126 cvar_t  *r_allowSoftwareGL;   // don't abort out if the pixelformat claims software
127 cvar_t  *r_previousglDriver;
128 
GLimp_sdl_init_video(void)129 qboolean GLimp_sdl_init_video(void)
130 {
131   if (!SDL_WasInit(SDL_INIT_VIDEO))
132   {
133     ri.Printf( PRINT_ALL, "Calling SDL_Init(SDL_INIT_VIDEO)...\n");
134     if (SDL_Init(SDL_INIT_VIDEO) == -1)
135     {
136 		ri.Printf( PRINT_ALL, "SDL_Init(SDL_INIT_VIDEO) failed: %s\n", SDL_GetError());
137         return qfalse;
138     }
139     ri.Printf( PRINT_ALL, "SDL_Init(SDL_INIT_VIDEO) passed.\n");
140   }
141 
142   return qtrue;
143 }
144 
145 
146 /*
147 * Find the first occurrence of find in s.
148 */
149 // bk001130 - from cvs1.17 (mkv), const
150 // bk001130 - made first argument const
Q_stristr(const char * s,const char * find)151 static const char *Q_stristr( const char *s, const char *find)
152 {
153   register char c, sc;
154   register size_t len;
155 
156   if ((c = *find++) != 0)
157   {
158     if (c >= 'a' && c <= 'z')
159     {
160       c -= ('a' - 'A');
161     }
162     len = strlen(find);
163     do
164     {
165       do
166       {
167         if ((sc = *s++) == 0)
168           return NULL;
169         if (sc >= 'a' && sc <= 'z')
170         {
171           sc -= ('a' - 'A');
172         }
173       } while (sc != c);
174     } while (Q_stricmpn(s, find, len) != 0);
175     s--;
176   }
177   return s;
178 }
179 
XLateKey(SDL_keysym * keysym,int * key)180 static const char *XLateKey(SDL_keysym *keysym, int *key)
181 {
182   static char buf[2] = { '\0', '\0' };
183   *key = 0;
184 
185   *buf = '\0';
186 
187   // these happen to match the ASCII chars.
188   if ((keysym->sym >= ' ') && (keysym->sym <= '~'))
189   {
190      *key = (int) keysym->sym;
191   }
192   else
193   switch (keysym->sym)
194   {
195   case SDLK_PAGEUP: *key = K_PGUP; break;
196   case SDLK_KP9:  *key = K_KP_PGUP; break;
197   case SDLK_PAGEDOWN: *key = K_PGDN; break;
198   case SDLK_KP3: *key = K_KP_PGDN; break;
199   case SDLK_KP7: *key = K_KP_HOME; break;
200   case SDLK_HOME:  *key = K_HOME; break;
201   case SDLK_KP1:   *key = K_KP_END; break;
202   case SDLK_END:   *key = K_END; break;
203   case SDLK_KP4: *key = K_KP_LEFTARROW; break;
204   case SDLK_LEFT:  *key = K_LEFTARROW; break;
205   case SDLK_KP6: *key = K_KP_RIGHTARROW; break;
206   case SDLK_RIGHT:  *key = K_RIGHTARROW;    break;
207   case SDLK_KP2:    *key = K_KP_DOWNARROW; break;
208   case SDLK_DOWN:  *key = K_DOWNARROW; break;
209   case SDLK_KP8:    *key = K_KP_UPARROW; break;
210   case SDLK_UP:    *key = K_UPARROW;   break;
211   case SDLK_ESCAPE: *key = K_ESCAPE;    break;
212   case SDLK_KP_ENTER: *key = K_KP_ENTER;  break;
213   case SDLK_RETURN: *key = K_ENTER;    break;
214   case SDLK_TAB:    *key = K_TAB;      break;
215   case SDLK_F1:    *key = K_F1;       break;
216   case SDLK_F2:    *key = K_F2;       break;
217   case SDLK_F3:    *key = K_F3;       break;
218   case SDLK_F4:    *key = K_F4;       break;
219   case SDLK_F5:    *key = K_F5;       break;
220   case SDLK_F6:    *key = K_F6;       break;
221   case SDLK_F7:    *key = K_F7;       break;
222   case SDLK_F8:    *key = K_F8;       break;
223   case SDLK_F9:    *key = K_F9;       break;
224   case SDLK_F10:    *key = K_F10;      break;
225   case SDLK_F11:    *key = K_F11;      break;
226   case SDLK_F12:    *key = K_F12;      break;
227 
228     // bk001206 - from Ryan's Fakk2
229   case SDLK_BACKSPACE: *key = K_BACKSPACE; break; // ctrl-h
230   case SDLK_KP_PERIOD: *key = K_KP_DEL; break;
231   case SDLK_DELETE: *key = K_DEL; break;
232   case SDLK_PAUSE:  *key = K_PAUSE;    break;
233 
234   case SDLK_LSHIFT:
235   case SDLK_RSHIFT: *key = K_SHIFT;   break;
236 
237   case SDLK_LCTRL:
238   case SDLK_RCTRL:  *key = K_CTRL;  break;
239 
240   case SDLK_RMETA:
241   case SDLK_LMETA:
242   case SDLK_RALT:
243   case SDLK_LALT: *key = K_ALT;     break;
244 
245   case SDLK_KP5: *key = K_KP_5;  break;
246   case SDLK_INSERT:   *key = K_INS; break;
247   case SDLK_KP0: *key = K_KP_INS; break;
248   case SDLK_KP_MULTIPLY: *key = '*'; break;
249   case SDLK_KP_PLUS:  *key = K_KP_PLUS; break;
250   case SDLK_KP_MINUS: *key = K_KP_MINUS; break;
251   case SDLK_KP_DIVIDE: *key = K_KP_SLASH; break;
252 
253   default: break;
254   }
255 
256   if( keysym->unicode <= 127 )  // maps to ASCII?
257   {
258     char ch = (char) keysym->unicode;
259     if (ch == '~')
260       *key = '~'; // console HACK
261 
262     // The X11 driver converts to lowercase, but apparently we shouldn't.
263     //  There's possibly somewhere else where they covert back. Passing
264     //  uppercase to the engine works fine and fixes all-lower input.
265     //  (https://bugzilla.icculus.org/show_bug.cgi?id=2364)  --ryan.
266     //else if (ch >= 'A' && ch <= 'Z')
267     //  ch = ch - 'A' + 'a';
268 
269     buf[0] = ch;
270   }
271 
272   return buf;
273 }
274 
install_grabs(void)275 static void install_grabs(void)
276 {
277     SDL_WM_GrabInput(SDL_GRAB_ON);
278     SDL_ShowCursor(0);
279 
280     // This is a bug in the current SDL/macosx...have to toggle it a few
281     //  times to get the cursor to hide.
282 #if defined(MACOS_X)
283     SDL_ShowCursor(1);
284     SDL_ShowCursor(0);
285 #endif
286 }
287 
uninstall_grabs(void)288 static void uninstall_grabs(void)
289 {
290     SDL_ShowCursor(1);
291     SDL_WM_GrabInput(SDL_GRAB_OFF);
292 }
293 
printkey(const SDL_Event * event)294 static void printkey(const SDL_Event* event)
295 {
296 #ifdef KBD_DBG
297   printf("key name: %s", SDL_GetKeyName(event->key.keysym.sym));
298   if(event->key.keysym.unicode)
299   {
300     printf(" unicode: %hx", event->key.keysym.unicode);
301     if (event->key.keysym.unicode >= '0'
302     && event->key.keysym.unicode <= '~')  // printable?
303       printf(" (%c)", (unsigned char)(event->key.keysym.unicode));
304   }
305   puts("");
306 #endif
307 }
308 
HandleEvents(void)309 static void HandleEvents(void)
310 {
311   const int t = 0;  // always just use the current time.
312   SDL_Event e;
313   const char *p = NULL;
314   int key = 0;
315 
316   if (screen == NULL)
317     return;  // no SDL context.
318 
319   if (cls.keyCatchers == 0)
320   {
321     if (sdlrepeatenabled)
322     {
323         SDL_EnableKeyRepeat(0, 0);
324         sdlrepeatenabled = qfalse;
325     }
326   }
327   else
328   {
329     if (!sdlrepeatenabled)
330     {
331         SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
332         sdlrepeatenabled = qtrue;
333     }
334   }
335 
336   while (SDL_PollEvent(&e))
337   {
338     switch (e.type)
339     {
340     case SDL_KEYDOWN:
341       printkey(&e);
342       p = XLateKey(&e.key.keysym, &key);
343       if (key)
344       {
345         Sys_QueEvent( t, SE_KEY, key, qtrue, 0, NULL );
346       }
347       if (p)
348       {
349         while (*p)
350         {
351           Sys_QueEvent( t, SE_CHAR, *p++, 0, 0, NULL );
352         }
353       }
354       break;
355 
356     case SDL_KEYUP:
357       XLateKey(&e.key.keysym, &key);
358       Sys_QueEvent( t, SE_KEY, key, qfalse, 0, NULL );
359       break;
360 
361     case SDL_MOUSEMOTION:
362       if (mouse_active)
363       {
364         Sys_QueEvent( t, SE_MOUSE, e.motion.xrel, e.motion.yrel, 0, NULL );
365       }
366       break;
367 
368     case SDL_MOUSEBUTTONDOWN:
369     case SDL_MOUSEBUTTONUP:
370       {
371 	unsigned char b;
372 	switch (e.button.button)
373 	{
374 	  case  1: b = K_MOUSE1; break;
375 	  case  2: b = K_MOUSE3; break;
376 	  case  3: b = K_MOUSE2; break;
377 	  case  4: b = K_MWHEELUP; break;
378 	  case  5: b = K_MWHEELDOWN; break;
379 	  case  6: b = K_MOUSE4; break;
380 	  case  7: b = K_MOUSE5; break;
381 	  default: b = K_AUX1 + (e.button.button - 8)%16; break;
382 	}
383 	Sys_QueEvent( t, SE_KEY, b, (e.type == SDL_MOUSEBUTTONDOWN?qtrue:qfalse), 0, NULL );
384       }
385       break;
386 
387     case SDL_QUIT:
388       Sys_Quit();
389       break;
390     }
391   }
392 }
393 
394 // NOTE TTimo for the tty console input, we didn't rely on those ..
395 //   it's not very surprising actually cause they are not used otherwise
KBD_Init(void)396 void KBD_Init(void)
397 {
398 }
399 
KBD_Close(void)400 void KBD_Close(void)
401 {
402 }
403 
IN_ActivateMouse(void)404 void IN_ActivateMouse( void )
405 {
406   if (!mouse_avail || !screen)
407      return;
408 
409   if (!mouse_active)
410   {
411     if (!in_nograb->value)
412       install_grabs();
413     mouse_active = qtrue;
414   }
415 }
416 
IN_DeactivateMouse(void)417 void IN_DeactivateMouse( void )
418 {
419   if (!mouse_avail || !screen)
420     return;
421 
422   if (mouse_active)
423   {
424     if (!in_nograb->value)
425       uninstall_grabs();
426     mouse_active = qfalse;
427   }
428 }
429 /*****************************************************************************/
430 
431 /*
432 ** GLimp_SetGamma
433 **
434 ** This routine should only be called if glConfig.deviceSupportsGamma is TRUE
435 */
GLimp_SetGamma(unsigned char red[256],unsigned char green[256],unsigned char blue[256])436 void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] )
437 {
438   // NOTE TTimo we get the gamma value from cvar, because we can't work with the s_gammatable
439   //   the API wasn't changed to avoid breaking other OSes
440   float g;
441 
442   if ( r_ignorehwgamma->integer )
443     return;
444 
445   g  = Cvar_Get("r_gamma", "1.0", 0)->value;
446   SDL_SetGamma(g, g, g);
447 }
448 
449 /*
450 ** GLimp_Shutdown
451 **
452 ** This routine does all OS specific shutdown procedures for the OpenGL
453 ** subsystem.  Under OpenGL this means NULLing out the current DC and
454 ** HGLRC, deleting the rendering context, and releasing the DC acquired
455 ** for the window.  The state structure is also nulled out.
456 **
457 */
GLimp_Shutdown(void)458 void GLimp_Shutdown( void )
459 {
460   IN_Shutdown();
461   screen = NULL;
462 
463   memset( &glConfig, 0, sizeof( glConfig ) );
464   memset( &glState, 0, sizeof( glState ) );
465 
466   QGL_Shutdown();
467 }
468 
469 /*
470 ** GLimp_LogComment
471 */
GLimp_LogComment(char * comment)472 void GLimp_LogComment( char *comment )
473 {
474   if ( glw_state.log_fp )
475   {
476     fprintf( glw_state.log_fp, "%s", comment );
477   }
478 }
479 
480 /*
481 ** GLW_StartDriverAndSetMode
482 */
483 // bk001204 - prototype needed
484 static int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen );
GLW_StartDriverAndSetMode(const char * drivername,int mode,qboolean fullscreen)485 static qboolean GLW_StartDriverAndSetMode( const char *drivername,
486                                            int mode,
487                                            qboolean fullscreen )
488 {
489   rserr_t err;
490 
491   if (GLimp_sdl_init_video() == qfalse)
492     return qfalse;
493 
494   // don't ever bother going into fullscreen with a voodoo card
495 #if 1	// JDC: I reenabled this
496   if ( Q_stristr( drivername, "Voodoo" ) )
497   {
498     ri.Cvar_Set( "r_fullscreen", "0" );
499     r_fullscreen->modified = qfalse;
500     fullscreen = qfalse;
501   }
502 #endif
503 
504 	if (fullscreen && in_nograb->value)
505 	{
506 		ri.Printf( PRINT_ALL, "Fullscreen not allowed with in_nograb 1\n");
507     ri.Cvar_Set( "r_fullscreen", "0" );
508     r_fullscreen->modified = qfalse;
509     fullscreen = qfalse;
510 	}
511 
512   err = GLW_SetMode( drivername, mode, fullscreen );
513 
514   switch ( err )
515   {
516   case RSERR_INVALID_FULLSCREEN:
517     ri.Printf( PRINT_ALL, "...WARNING: fullscreen unavailable in this mode\n" );
518     return qfalse;
519   case RSERR_INVALID_MODE:
520     ri.Printf( PRINT_ALL, "...WARNING: could not set the given mode (%d)\n", mode );
521     return qfalse;
522   default:
523     break;
524   }
525   return qtrue;
526 }
527 
528 /*
529 ** GLW_SetMode
530 */
GLW_SetMode(const char * drivername,int mode,qboolean fullscreen)531 static int GLW_SetMode( const char *drivername, int mode, qboolean fullscreen )
532 {
533   const char*   glstring; // bk001130 - from cvs1.17 (mkv)
534   int sdlcolorbits = 4;
535   int colorbits, depthbits, stencilbits;
536   int tcolorbits, tdepthbits, tstencilbits;
537   int i = 0;
538   SDL_Surface *vidscreen = NULL;
539 
540   ri.Printf( PRINT_ALL, "Initializing OpenGL display\n");
541 
542   ri.Printf (PRINT_ALL, "...setting mode %d:", mode );
543 
544   if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, mode ) )
545   {
546     ri.Printf( PRINT_ALL, " invalid mode\n" );
547     return RSERR_INVALID_MODE;
548   }
549   ri.Printf( PRINT_ALL, " %d %d\n", glConfig.vidWidth, glConfig.vidHeight);
550 
551   Uint32 flags = SDL_OPENGL;
552   if (fullscreen)
553     flags |= SDL_FULLSCREEN;
554 
555   if (!r_colorbits->value)
556     colorbits = 24;
557   else
558     colorbits = r_colorbits->value;
559 
560   if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) )
561     colorbits = 16;
562 
563   if (!r_depthbits->value)
564     depthbits = 24;
565   else
566     depthbits = r_depthbits->value;
567   stencilbits = r_stencilbits->value;
568 
569   for (i = 0; i < 16; i++)
570   {
571     // 0 - default
572     // 1 - minus colorbits
573     // 2 - minus depthbits
574     // 3 - minus stencil
575     if ((i % 4) == 0 && i)
576     {
577       // one pass, reduce
578       switch (i / 4)
579       {
580       case 2 :
581         if (colorbits == 24)
582           colorbits = 16;
583         break;
584       case 1 :
585         if (depthbits == 24)
586           depthbits = 16;
587         else if (depthbits == 16)
588           depthbits = 8;
589       case 3 :
590         if (stencilbits == 24)
591           stencilbits = 16;
592         else if (stencilbits == 16)
593           stencilbits = 8;
594       }
595     }
596 
597     tcolorbits = colorbits;
598     tdepthbits = depthbits;
599     tstencilbits = stencilbits;
600 
601     if ((i % 4) == 3)
602     { // reduce colorbits
603       if (tcolorbits == 24)
604         tcolorbits = 16;
605     }
606 
607     if ((i % 4) == 2)
608     { // reduce depthbits
609       if (tdepthbits == 24)
610         tdepthbits = 16;
611       else if (tdepthbits == 16)
612         tdepthbits = 8;
613     }
614 
615     if ((i % 4) == 1)
616     { // reduce stencilbits
617       if (tstencilbits == 24)
618         tstencilbits = 16;
619       else if (tstencilbits == 16)
620         tstencilbits = 8;
621       else
622         tstencilbits = 0;
623     }
624 
625     sdlcolorbits = 4;
626     if (tcolorbits == 24)
627         sdlcolorbits = 8;
628 
629     SDL_GL_SetAttribute( SDL_GL_RED_SIZE, sdlcolorbits );
630     SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, sdlcolorbits );
631     SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, sdlcolorbits );
632     SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, tdepthbits );
633     SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, tstencilbits );
634     SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
635 
636     SDL_WM_SetCaption(WINDOW_CLASS_NAME, WINDOW_CLASS_NAME_BRIEF);
637     SDL_ShowCursor(0);
638     SDL_EnableUNICODE(1);
639     SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
640     sdlrepeatenabled = qtrue;
641 
642     if (!(vidscreen = SDL_SetVideoMode(glConfig.vidWidth, glConfig.vidHeight, colorbits, flags)))
643     {
644         fprintf(stderr, "SDL_SetVideoMode failed: %s\n", SDL_GetError());
645         continue;
646     }
647 
648     opengl_context = GLimp_GetCurrentContext();
649 
650     ri.Printf( PRINT_ALL, "Using %d/%d/%d Color bits, %d depth, %d stencil display.\n",
651                sdlcolorbits, sdlcolorbits, sdlcolorbits,
652                tdepthbits, tstencilbits);
653 
654     glConfig.colorBits = tcolorbits;
655     glConfig.depthBits = tdepthbits;
656     glConfig.stencilBits = tstencilbits;
657     break;
658   }
659 
660   if (!vidscreen)
661   {
662     ri.Printf( PRINT_ALL, "Couldn't get a visual\n" );
663     return RSERR_INVALID_MODE;
664   }
665 
666   screen = vidscreen;
667 
668   // bk001130 - from cvs1.17 (mkv)
669   glstring = (char *) qglGetString (GL_RENDERER);
670   ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glstring );
671 
672   // bk010122 - new software token (Indirect)
673   if ( !Q_stricmp( glstring, "Mesa X11")
674        || !Q_stricmp( glstring, "Mesa GLX Indirect") )
675   {
676     if ( !r_allowSoftwareGL->integer )
677     {
678       ri.Printf( PRINT_ALL, "\n\n***********************************************************\n" );
679       ri.Printf( PRINT_ALL, " You are using software Mesa (no hardware acceleration)!   \n" );
680       ri.Printf( PRINT_ALL, " Driver DLL used: %s\n", drivername );
681       ri.Printf( PRINT_ALL, " If this is intentional, add\n" );
682       ri.Printf( PRINT_ALL, "       \"+set r_allowSoftwareGL 1\"\n" );
683       ri.Printf( PRINT_ALL, " to the command line when starting the game.\n" );
684       ri.Printf( PRINT_ALL, "***********************************************************\n");
685       GLimp_Shutdown( );
686       return RSERR_INVALID_MODE;
687     } else
688     {
689       ri.Printf( PRINT_ALL, "...using software Mesa (r_allowSoftwareGL==1).\n" );
690     }
691   }
692 
693   return RSERR_OK;
694 }
695 
696 /*
697 ** GLW_InitExtensions
698 */
GLW_InitExtensions(void)699 static void GLW_InitExtensions( void )
700 {
701   if ( !r_allowExtensions->integer )
702   {
703     ri.Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" );
704     return;
705   }
706 
707   ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
708 
709   // GL_S3_s3tc
710   if ( Q_stristr( glConfig.extensions_string, "GL_S3_s3tc" ) )
711   {
712     if ( r_ext_compressed_textures->value )
713     {
714       glConfig.textureCompression = TC_S3TC;
715       ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
716     } else
717     {
718       glConfig.textureCompression = TC_NONE;
719       ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
720     }
721   } else
722   {
723     glConfig.textureCompression = TC_NONE;
724     ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
725   }
726 
727   // GL_EXT_texture_env_add
728   glConfig.textureEnvAddAvailable = qfalse;
729   if ( Q_stristr( glConfig.extensions_string, "EXT_texture_env_add" ) )
730   {
731     if ( r_ext_texture_env_add->integer )
732     {
733       glConfig.textureEnvAddAvailable = qtrue;
734       ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
735     } else
736     {
737       glConfig.textureEnvAddAvailable = qfalse;
738       ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
739     }
740   } else
741   {
742     ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
743   }
744 
745   // GL_ARB_multitexture
746   qglMultiTexCoord2fARB = NULL;
747   qglActiveTextureARB = NULL;
748   qglClientActiveTextureARB = NULL;
749   if ( Q_stristr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
750   {
751     if ( r_ext_multitexture->value )
752     {
753       qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) SDL_GL_GetProcAddress( "glMultiTexCoord2fARB" );
754       qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) SDL_GL_GetProcAddress( "glActiveTextureARB" );
755       qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) SDL_GL_GetProcAddress( "glClientActiveTextureARB" );
756 
757       if ( qglActiveTextureARB )
758       {
759         GLint glint = 0;
760         qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, &glint );
761         glConfig.maxActiveTextures = (int) glint;
762         if ( glConfig.maxActiveTextures > 1 )
763         {
764           ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
765         } else
766         {
767           qglMultiTexCoord2fARB = NULL;
768           qglActiveTextureARB = NULL;
769           qglClientActiveTextureARB = NULL;
770           ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
771         }
772       }
773     } else
774     {
775       ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
776     }
777   } else
778   {
779     ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
780   }
781 
782   // GL_EXT_compiled_vertex_array
783   if ( Q_stristr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) )
784   {
785     if ( r_ext_compiled_vertex_array->value )
786     {
787       ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
788       qglLockArraysEXT = ( void ( APIENTRY * )( GLint, GLint ) ) SDL_GL_GetProcAddress( "glLockArraysEXT" );
789       qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) SDL_GL_GetProcAddress( "glUnlockArraysEXT" );
790       if (!qglLockArraysEXT || !qglUnlockArraysEXT)
791       {
792         ri.Error (ERR_FATAL, "bad getprocaddress");
793       }
794     } else
795     {
796       ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
797     }
798   } else
799   {
800     ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
801   }
802 
803 }
804 
GLW_InitGamma(void)805 static void GLW_InitGamma( void )
806 {
807     glConfig.deviceSupportsGamma = qtrue;
808 }
809 
810 /*
811 ** GLW_LoadOpenGL
812 **
813 ** GLimp_win.c internal function that that attempts to load and use
814 ** a specific OpenGL DLL.
815 */
GLW_LoadOpenGL(const char * name)816 static qboolean GLW_LoadOpenGL( const char *name )
817 {
818   qboolean fullscreen;
819 
820   ri.Printf( PRINT_ALL, "...loading %s:\n", name );
821 
822   // disable the 3Dfx splash screen and set gamma
823   // we do this all the time, but it shouldn't hurt anything
824   // on non-3Dfx stuff
825   putenv("FX_GLIDE_NO_SPLASH=0");
826 
827   // Mesa VooDoo hacks
828   putenv("MESA_GLX_FX=fullscreen\n");
829 
830   // load the QGL layer
831   if ( QGL_Init( name ) )
832   {
833     fullscreen = r_fullscreen->integer;
834 
835     // create the window and set up the context
836     if ( !GLW_StartDriverAndSetMode( name, r_mode->integer, fullscreen ) )
837     {
838       if (r_mode->integer != 3)
839       {
840         if ( !GLW_StartDriverAndSetMode( name, 3, fullscreen ) )
841         {
842           goto fail;
843         }
844       } else
845         goto fail;
846     }
847 
848     return qtrue;
849   } else
850   {
851     ri.Printf( PRINT_ALL, "failed\n" );
852   }
853   fail:
854 
855   QGL_Shutdown();
856 
857   return qfalse;
858 }
859 
860 
861 /*
862 ** GLimp_Init
863 **
864 ** This routine is responsible for initializing the OS specific portions
865 ** of OpenGL.
866 */
GLimp_Init(void)867 void GLimp_Init( void )
868 {
869   qboolean attemptedlibGL = qfalse;
870   qboolean attempted3Dfx = qfalse;
871   qboolean success = qfalse;
872 
873   r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
874 
875   r_previousglDriver = ri.Cvar_Get( "r_previousglDriver", "", CVAR_ROM );
876 
877   InitSig();
878 
879   IN_Init();   // rcg08312005 moved into glimp.
880 
881   // Hack here so that if the UI
882   if ( *r_previousglDriver->string )
883   {
884     // The UI changed it on us, hack it back
885     // This means the renderer can't be changed on the fly
886     ri.Cvar_Set( "r_glDriver", r_previousglDriver->string );
887   }
888 
889   //
890   // load and initialize the specific OpenGL driver
891   //
892   if ( !GLW_LoadOpenGL( r_glDriver->string ) )
893   {
894     if ( !Q_stricmp( r_glDriver->string, OPENGL_DRIVER_NAME ) )
895     {
896       attemptedlibGL = qtrue;
897     } else if ( !Q_stricmp( r_glDriver->string, _3DFX_DRIVER_NAME ) )
898     {
899       attempted3Dfx = qtrue;
900     }
901 
902     #if 0
903     // TTimo
904     // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=455
905     // old legacy load code, was confusing people who had a bad OpenGL setup
906     if ( !attempted3Dfx && !success )
907     {
908       attempted3Dfx = qtrue;
909       if ( GLW_LoadOpenGL( _3DFX_DRIVER_NAME ) )
910       {
911         ri.Cvar_Set( "r_glDriver", _3DFX_DRIVER_NAME );
912         r_glDriver->modified = qfalse;
913         success = qtrue;
914       }
915     }
916     #endif
917 
918     // try ICD before trying 3Dfx standalone driver
919     if ( !attemptedlibGL && !success )
920     {
921       attemptedlibGL = qtrue;
922       if ( GLW_LoadOpenGL( OPENGL_DRIVER_NAME ) )
923       {
924         ri.Cvar_Set( "r_glDriver", OPENGL_DRIVER_NAME );
925         r_glDriver->modified = qfalse;
926         success = qtrue;
927       }
928     }
929 
930     if (!success)
931       ri.Error( ERR_FATAL, "GLimp_Init() - could not load OpenGL subsystem\n" );
932 
933   }
934 
935   // Save it in case the UI stomps it
936   ri.Cvar_Set( "r_previousglDriver", r_glDriver->string );
937 
938   // This values force the UI to disable driver selection
939   glConfig.driverType = GLDRV_ICD;
940   glConfig.hardwareType = GLHW_GENERIC;
941 
942   // get our config strings
943   Q_strncpyz( glConfig.vendor_string, (char *) qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
944   Q_strncpyz( glConfig.renderer_string, (char *) qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
945   if (*glConfig.renderer_string && glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] == '\n')
946     glConfig.renderer_string[strlen(glConfig.renderer_string) - 1] = 0;
947   Q_strncpyz( glConfig.version_string, (char *) qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
948   Q_strncpyz( glConfig.extensions_string, (char *) qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
949 
950   GL_ResolveHardwareType( );
951 
952   ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string );
953 
954   // initialize extensions
955   GLW_InitExtensions();
956   GLW_InitGamma();
957 
958   InitSig(); // not clear why this is at begin & end of function
959 }
960 
961 
962 /*
963 ** GLimp_EndFrame
964 **
965 ** Responsible for doing a swapbuffers and possibly for other stuff
966 ** as yet to be determined.  Probably better not to make this a GLimp
967 ** function and instead do a call to GLimp_SwapBuffers.
968 */
GLimp_EndFrame(void)969 void GLimp_EndFrame (void)
970 {
971   // don't flip if drawing to front buffer
972   if ( Q_stricmp( r_drawBuffer->string, "GL_FRONT" ) != 0 )
973   {
974     SDL_GL_SwapBuffers();
975   }
976 
977   if( r_fullscreen->modified )
978   {
979     qboolean    fullscreen;
980     qboolean    sdlToggled = qfalse;
981     SDL_Surface *s = SDL_GetVideoSurface( );
982 
983     if( s )
984     {
985       // Find out the current state
986       if( s->flags & SDL_FULLSCREEN )
987         fullscreen = qtrue;
988       else
989         fullscreen = qfalse;
990 
991       // Is the state we want different from the current state?
992       if( !!r_fullscreen->integer != fullscreen )
993         sdlToggled = SDL_WM_ToggleFullScreen( s );
994       else
995         sdlToggled = qtrue;
996     }
997 
998     // SDL_WM_ToggleFullScreen didn't work, so do it the slow way
999     if( !sdlToggled )
1000       Cbuf_AddText( "vid_restart" );
1001 
1002     r_fullscreen->modified = qfalse;
1003   }
1004 
1005   // check logging
1006   QGL_EnableLogging( (qboolean)r_logFile->integer ); // bk001205 - was ->value
1007 }
1008 
1009 
1010 
1011 #ifdef SMP
1012 /*
1013 ===========================================================
1014 
1015 SMP acceleration
1016 
1017 ===========================================================
1018 */
1019 
1020 /*
1021  * I have no idea if this will even work...most platforms don't offer
1022  *  thread-safe OpenGL libraries, and it looks like the original Linux
1023  *  code counted on each thread claiming the GL context with glXMakeCurrent(),
1024  *  which you can't currently do in SDL. We'll just have to hope for the best.
1025  */
1026 
1027 static SDL_mutex *smpMutex = NULL;
1028 static SDL_cond *renderCommandsEvent = NULL;
1029 static SDL_cond *renderCompletedEvent = NULL;
1030 static void (*glimpRenderThread)( void ) = NULL;
1031 static SDL_Thread *renderThread = NULL;
1032 
GLimp_ShutdownRenderThread(void)1033 static void GLimp_ShutdownRenderThread(void)
1034 {
1035 	if (smpMutex != NULL)
1036 	{
1037 		SDL_DestroyMutex(smpMutex);
1038 		smpMutex = NULL;
1039 	}
1040 
1041 	if (renderCommandsEvent != NULL)
1042 	{
1043 		SDL_DestroyCond(renderCommandsEvent);
1044 		renderCommandsEvent = NULL;
1045 	}
1046 
1047 	if (renderCompletedEvent != NULL)
1048 	{
1049 		SDL_DestroyCond(renderCompletedEvent);
1050 		renderCompletedEvent = NULL;
1051 	}
1052 
1053 	glimpRenderThread = NULL;
1054 }
1055 
GLimp_RenderThreadWrapper(void * arg)1056 static int GLimp_RenderThreadWrapper( void *arg )
1057 {
1058 	Com_Printf( "Render thread starting\n" );
1059 
1060 	glimpRenderThread();
1061 
1062 	GLimp_SetCurrentContext(NULL);
1063 
1064 	Com_Printf( "Render thread terminating\n" );
1065 
1066 	return 0;
1067 }
1068 
GLimp_SpawnRenderThread(void (* function)(void))1069 qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
1070 {
1071 	static qboolean warned = qfalse;
1072 	if (!warned)
1073 	{
1074 		Com_Printf("WARNING: You enable r_smp at your own risk!\n");
1075 		warned = qtrue;
1076 	}
1077 
1078 #ifndef MACOS_X
1079 	return qfalse;  /* better safe than sorry for now. */
1080 #endif
1081 
1082 	if (renderThread != NULL)  /* hopefully just a zombie at this point... */
1083 	{
1084 		Com_Printf("Already a render thread? Trying to clean it up...\n");
1085 		SDL_WaitThread(renderThread, NULL);
1086 		renderThread = NULL;
1087 		GLimp_ShutdownRenderThread();
1088 	}
1089 
1090 	smpMutex = SDL_CreateMutex();
1091 	if (smpMutex == NULL)
1092 	{
1093 		Com_Printf( "smpMutex creation failed: %s\n", SDL_GetError() );
1094 		GLimp_ShutdownRenderThread();
1095 		return qfalse;
1096 	}
1097 
1098 	renderCommandsEvent = SDL_CreateCond();
1099 	if (renderCommandsEvent == NULL)
1100 	{
1101 		Com_Printf( "renderCommandsEvent creation failed: %s\n", SDL_GetError() );
1102 		GLimp_ShutdownRenderThread();
1103 		return qfalse;
1104 	}
1105 
1106 	renderCompletedEvent = SDL_CreateCond();
1107 	if (renderCompletedEvent == NULL)
1108 	{
1109 		Com_Printf( "renderCompletedEvent creation failed: %s\n", SDL_GetError() );
1110 		GLimp_ShutdownRenderThread();
1111 		return qfalse;
1112 	}
1113 
1114 	glimpRenderThread = function;
1115 	renderThread = SDL_CreateThread(GLimp_RenderThreadWrapper, NULL);
1116 	if ( renderThread == NULL ) {
1117 		ri.Printf( PRINT_ALL, "SDL_CreateThread() returned %s", SDL_GetError() );
1118 		GLimp_ShutdownRenderThread();
1119 		return qfalse;
1120 	} else {
1121 		// !!! FIXME: No detach API available in SDL!
1122 		//ret = pthread_detach( renderThread );
1123 		//if ( ret ) {
1124 			//ri.Printf( PRINT_ALL, "pthread_detach returned %d: %s", ret, strerror( ret ) );
1125 		//}
1126 	}
1127 
1128 	return qtrue;
1129 }
1130 
1131 static volatile void    *smpData = NULL;
1132 static volatile qboolean smpDataReady;
1133 
GLimp_RendererSleep(void)1134 void *GLimp_RendererSleep( void )
1135 {
1136 	void  *data = NULL;
1137 
1138 	GLimp_SetCurrentContext(NULL);
1139 
1140 	SDL_LockMutex(smpMutex);
1141 	{
1142 		smpData = NULL;
1143 		smpDataReady = qfalse;
1144 
1145 		// after this, the front end can exit GLimp_FrontEndSleep
1146 		SDL_CondSignal(renderCompletedEvent);
1147 
1148 		while ( !smpDataReady ) {
1149 			SDL_CondWait(renderCommandsEvent, smpMutex);
1150 		}
1151 
1152 		data = (void *)smpData;
1153 	}
1154 	SDL_UnlockMutex(smpMutex);
1155 
1156 	GLimp_SetCurrentContext(opengl_context);
1157 
1158 	return data;
1159 }
1160 
GLimp_FrontEndSleep(void)1161 void GLimp_FrontEndSleep( void )
1162 {
1163 	SDL_LockMutex(smpMutex);
1164 	{
1165 		while ( smpData ) {
1166 			SDL_CondWait(renderCompletedEvent, smpMutex);
1167 		}
1168 	}
1169 	SDL_UnlockMutex(smpMutex);
1170 
1171 	GLimp_SetCurrentContext(opengl_context);
1172 }
1173 
GLimp_WakeRenderer(void * data)1174 void GLimp_WakeRenderer( void *data )
1175 {
1176 	GLimp_SetCurrentContext(NULL);
1177 
1178 	SDL_LockMutex(smpMutex);
1179 	{
1180 		assert( smpData == NULL );
1181 		smpData = data;
1182 		smpDataReady = qtrue;
1183 
1184 		// after this, the renderer can continue through GLimp_RendererSleep
1185 		SDL_CondSignal(renderCommandsEvent);
1186 	}
1187 	SDL_UnlockMutex(smpMutex);
1188 }
1189 
1190 #else
1191 
GLimp_RenderThreadWrapper(void * stub)1192 void GLimp_RenderThreadWrapper( void *stub ) {}
GLimp_SpawnRenderThread(void (* function)(void))1193 qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) {
1194 	ri.Printf( PRINT_WARNING, "ERROR: SMP support was disabled at compile time\n");
1195   return qfalse;
1196 }
GLimp_RendererSleep(void)1197 void *GLimp_RendererSleep( void ) {
1198   return NULL;
1199 }
GLimp_FrontEndSleep(void)1200 void GLimp_FrontEndSleep( void ) {}
GLimp_WakeRenderer(void * data)1201 void GLimp_WakeRenderer( void *data ) {}
1202 
1203 #endif
1204 
1205 /*****************************************************************************/
1206 /* MOUSE                                                                     */
1207 /*****************************************************************************/
1208 
IN_Init(void)1209 void IN_Init(void) {
1210 	Com_Printf ("\n------- Input Initialization -------\n");
1211   // mouse variables
1212   in_mouse = Cvar_Get ("in_mouse", "1", CVAR_ARCHIVE);
1213 
1214 	// turn on-off sub-frame timing of X events
1215 	in_subframe = Cvar_Get ("in_subframe", "1", CVAR_ARCHIVE);
1216 
1217 	// developer feature, allows to break without loosing mouse pointer
1218 	in_nograb = Cvar_Get ("in_nograb", "0", 0);
1219 
1220   // bk001130 - from cvs.17 (mkv), joystick variables
1221   in_joystick = Cvar_Get ("in_joystick", "0", CVAR_ARCHIVE|CVAR_LATCH);
1222   // bk001130 - changed this to match win32
1223   in_joystickDebug = Cvar_Get ("in_debugjoystick", "0", CVAR_TEMP);
1224   joy_threshold = Cvar_Get ("joy_threshold", "0.15", CVAR_ARCHIVE); // FIXME: in_joythreshold
1225 
1226 #ifdef MACOS_X
1227   Cvar_Set( "cl_platformSensitivity", "1.0" );
1228 #else
1229   Cvar_Set( "cl_platformSensitivity", "2.0" );
1230 #endif
1231 
1232   if (in_mouse->value)
1233     mouse_avail = qtrue;
1234   else
1235     mouse_avail = qfalse;
1236 
1237   IN_StartupJoystick( ); // bk001130 - from cvs1.17 (mkv)
1238 	Com_Printf ("------------------------------------\n");
1239 }
1240 
IN_Shutdown(void)1241 void IN_Shutdown(void)
1242 {
1243   IN_DeactivateMouse();
1244 
1245   mouse_avail = qfalse;
1246 
1247   if (stick)
1248   {
1249     SDL_JoystickClose(stick);
1250     stick = NULL;
1251   }
1252 
1253   SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
1254 }
1255 
IN_Frame(void)1256 void IN_Frame (void) {
1257 
1258   // bk001130 - from cvs 1.17 (mkv)
1259   IN_JoyMove(); // FIXME: disable if on desktop?
1260 
1261   if ( cls.keyCatchers & KEYCATCH_CONSOLE )
1262   {
1263     // temporarily deactivate if not in the game and
1264     // running on the desktop
1265     // voodoo always counts as full screen
1266     if (Cvar_VariableValue ("r_fullscreen") == 0
1267         && strcmp( Cvar_VariableString("r_glDriver"), _3DFX_DRIVER_NAME ) )
1268     {
1269       IN_DeactivateMouse ();
1270       return;
1271     }
1272   }
1273 
1274   IN_ActivateMouse();
1275 }
1276 
IN_Activate(void)1277 void IN_Activate(void)
1278 {
1279 }
1280 
1281 // bk001130 - cvs1.17 joystick code (mkv) was here, no linux_joystick.c
1282 
Sys_SendKeyEvents(void)1283 void Sys_SendKeyEvents (void) {
1284   // XEvent event; // bk001204 - unused
1285 
1286   if (!screen)
1287     return;
1288   HandleEvents();
1289 }
1290 
1291 
1292 // (moved this back in here from linux_joystick.c, so it's all in one place...
1293 //   --ryan.
1294 
1295 /* We translate axes movement into keypresses. */
1296 static int joy_keys[16] = {
1297      K_LEFTARROW, K_RIGHTARROW,
1298      K_UPARROW, K_DOWNARROW,
1299      K_JOY16, K_JOY17,
1300      K_JOY18, K_JOY19,
1301      K_JOY20, K_JOY21,
1302      K_JOY22, K_JOY23,
1303 
1304      K_JOY24, K_JOY25,
1305      K_JOY26, K_JOY27
1306 };
1307 
1308 
1309 // bk001130 - from linux_glimp.c
1310 extern cvar_t *  in_joystick;
1311 extern cvar_t *  in_joystickDebug;
1312 extern cvar_t *  joy_threshold;
1313 
1314 #define ARRAYLEN(x) (sizeof (x) / sizeof (x[0]))
1315 struct
1316 {
1317     qboolean buttons[16];  // !!! FIXME: these might be too many.
1318     unsigned int oldaxes;
1319 } stick_state;
1320 
1321 
1322 /**********************************************/
1323 /* Joystick routines.                         */
1324 /**********************************************/
1325 // bk001130 - from cvs1.17 (mkv), removed from linux_glimp.c
IN_StartupJoystick(void)1326 void IN_StartupJoystick( void )
1327 {
1328   int i = 0;
1329   int total = 0;
1330 
1331   if (stick != NULL)
1332     SDL_JoystickClose(stick);
1333 
1334   stick = NULL;
1335   memset(&stick_state, '\0', sizeof (stick_state));
1336 
1337   if( !in_joystick->integer ) {
1338     Com_Printf( "Joystick is not active.\n" );
1339     return;
1340   }
1341 
1342   if (!SDL_WasInit(SDL_INIT_JOYSTICK))
1343   {
1344       Com_Printf("Calling SDL_Init(SDL_INIT_JOYSTICK)...\n");
1345       if (SDL_Init(SDL_INIT_JOYSTICK) == -1)
1346       {
1347           Com_Printf("SDL_Init(SDL_INIT_JOYSTICK) failed: %s\n", SDL_GetError());
1348           return;
1349       }
1350       Com_Printf("SDL_Init(SDL_INIT_JOYSTICK) passed.\n");
1351   }
1352 
1353   total = SDL_NumJoysticks();
1354   Com_Printf("I see %d possible joysticks\n", total);
1355   for (i = 0; i < total; i++)
1356     Com_Printf("[%d] %s\n", i, SDL_JoystickName(i));
1357 
1358   // !!! FIXME: someone should add a way to select a specific stick.
1359   for( i = 0; i < total; i++ ) {
1360     stick = SDL_JoystickOpen(i);
1361     if (stick == NULL)
1362         continue;
1363 
1364     Com_Printf( "Joystick %d opened\n", i );
1365     Com_Printf( "Name:    %s\n", SDL_JoystickName(i) );
1366     Com_Printf( "Axes:    %d\n", SDL_JoystickNumAxes(stick) );
1367     Com_Printf( "Hats:    %d\n", SDL_JoystickNumHats(stick) );
1368     Com_Printf( "Buttons: %d\n", SDL_JoystickNumButtons(stick) );
1369     Com_Printf( "Balls: %d\n", SDL_JoystickNumBalls(stick) );
1370 
1371     SDL_JoystickEventState(SDL_QUERY);
1372 
1373     /* Our work here is done. */
1374     return;
1375   }
1376 
1377   /* No soup for you. */
1378   if( stick == NULL ) {
1379     Com_Printf( "No joystick opened.\n" );
1380     return;
1381   }
1382 }
1383 
IN_JoyMove(void)1384 void IN_JoyMove( void )
1385 {
1386     qboolean joy_pressed[ARRAYLEN(joy_keys)];
1387     unsigned int axes = 0;
1388     int total = 0;
1389     int i = 0;
1390 
1391     if (!stick)
1392         return;
1393 
1394     SDL_JoystickUpdate();
1395 
1396     memset(joy_pressed, '\0', sizeof (joy_pressed));
1397 
1398     // update the ball state.
1399     total = SDL_JoystickNumBalls(stick);
1400     if (total > 0)
1401     {
1402         int balldx = 0;
1403         int balldy = 0;
1404         for (i = 0; i < total; i++)
1405         {
1406             int dx = 0;
1407             int dy = 0;
1408             SDL_JoystickGetBall(stick, i, &dx, &dy);
1409             balldx += dx;
1410             balldy += dy;
1411         }
1412         if (balldx || balldy)
1413         {
1414             // !!! FIXME: is this good for stick balls, or just mice?
1415             // Scale like the mouse input...
1416             if (abs(balldx) > 1)
1417                 balldx *= 2;
1418             if (abs(balldy) > 1)
1419                 balldy *= 2;
1420             Sys_QueEvent( 0, SE_MOUSE, balldx, balldy, 0, NULL );
1421         }
1422     }
1423 
1424     // now query the stick buttons...
1425     total = SDL_JoystickNumButtons(stick);
1426     if (total > 0)
1427     {
1428         if (total > ARRAYLEN(stick_state.buttons))
1429             total = ARRAYLEN(stick_state.buttons);
1430         for (i = 0; i < total; i++)
1431         {
1432             qboolean pressed = (SDL_JoystickGetButton(stick, i) != 0);
1433             if (pressed != stick_state.buttons[i])
1434             {
1435                 Sys_QueEvent( 0, SE_KEY, K_JOY1 + i, pressed, 0, NULL );
1436                 stick_state.buttons[i] = pressed;
1437             }
1438         }
1439     }
1440 
1441     // !!! FIXME: look at the hats...
1442 
1443     // finally, look at the axes...
1444     total = SDL_JoystickNumAxes(stick);
1445     if (total > 0)
1446     {
1447         if (total > 16) total = 16;
1448         for (i = 0; i < total; i++)
1449         {
1450             Sint16 axis = SDL_JoystickGetAxis(stick, i);
1451             float f = ( (float) axis ) / 32767.0f;
1452             if( f < -joy_threshold->value ) {
1453                 axes |= ( 1 << ( i * 2 ) );
1454             } else if( f > joy_threshold->value ) {
1455                 axes |= ( 1 << ( ( i * 2 ) + 1 ) );
1456             }
1457         }
1458     }
1459 
1460     /* Time to update axes state based on old vs. new. */
1461     if (axes != stick_state.oldaxes)
1462     {
1463         for( i = 0; i < 16; i++ ) {
1464             if( ( axes & ( 1 << i ) ) && !( stick_state.oldaxes & ( 1 << i ) ) ) {
1465                 Sys_QueEvent( 0, SE_KEY, joy_keys[i], qtrue, 0, NULL );
1466             }
1467 
1468             if( !( axes & ( 1 << i ) ) && ( stick_state.oldaxes & ( 1 << i ) ) ) {
1469                 Sys_QueEvent( 0, SE_KEY, joy_keys[i], qfalse, 0, NULL );
1470             }
1471         }
1472     }
1473 
1474     /* Save for future generations. */
1475     stick_state.oldaxes = axes;
1476 }
1477 
1478 #endif  // USE_SDL_VIDEO
1479 
1480 // end sdl_glimp.c ...
1481 
1482