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