convert line ends
[canaan.git] / prj / cam / src / ui / joypoll.cpp
blob955b09c496a8f121d98a447e5d68b2a57f4da7a6
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
6 // $Header: r:/t2repos/thief2/src/ui/joypoll.cpp,v 1.4 2000/02/19 13:28:21 toml Exp $
8 //taken from flight 2 - DL
10 #include <limits.h>
11 #include <stdlib.h>
13 #include <appagg.h>
14 #include <comtools.h>
15 #include <dispapi.h>
17 #include <keydefs.h>
18 #include <event.h>
20 #include <joyapi.h>
21 #include <inpbase.h>
23 #include <tminit.h>
24 #include <timer.h>
26 #include <mprintf.h>
28 #include <joypoll.h>
29 #include <gen_bind.h>
31 #include <config.h>
32 #include <memall.h>
33 #include <dbmem.h> // must be last header!
35 #define MAX_BUTTONS 32
36 #define DOWN(x) ((x)|KB_FLAG_DOWN)
38 static IJoystick *g_pJoystick;
41 static bool bHasNoRudder = FALSE;
42 static bool bHasNoThrottle = FALSE;
44 void DispatchFrameEvent(void);
45 // return cooked keycode given a string like 'alt_shift_g'
46 // bit16 is set if "rep" modifier was present
47 //ulong ParseKeyName( char *pKeyName );
49 long joyRangeX, joyMidX;
50 long joyRangeY, joyMidY;
51 long joyRangeZ, joyMidZ;
52 long joyRangeRudder, joyMidRudder;
54 // array of cooked keycodes for joystick buttons
55 static ulong buttonKeys[kJoyButtonsMax];
57 // if bit 16 of buttonKeys is set, button will repeat
58 #define BUTTON_KEY_REPEAT 0x10000
59 static float buttonRepeatTime[kJoyButtonsMax];
61 #define DEFAULT_BUTTON_REPEAT_INTERVAL 0.1
62 static float buttonRepeatInterval;
63 #define DEFAULT_BUTTON_REPEAT_DELAY 0.25
64 static float buttonRepeatDelay;
66 // an invalid keycode which indicates that this button
67 // doesn't generate a keycode OR a button event
68 #define JOYBUTTON_DISABLED 0xFFFF
70 // These are to determine if there has been a change
71 static Point joyAPos = {0,0}; // joystick
72 static Point joyBPos = {0,0}; // throttle,rudder
73 static Point joyCPos = {0,0}; // hat
74 static char joyButtons[MAX_BUTTONS] =
75 { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
76 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 }; // 0 is up
77 // Hat translation values
78 static Point hatVals[9] = {{0,0},{0,1},{1,0},{0,-1},{-1,0}, {1,1},{1,-1},{-1,-1},{-1,1}};
79 static int lastHat = 0; // for spoofing key events
80 static int hatCodes[5] = {0,KEY_PAD_UP,KEY_PAD_RIGHT,KEY_PAD_DOWN,KEY_PAD_LEFT};
82 cDarkJoyProcControl g_dark_joy_control;
84 cDarkJoyProcControl :: cDarkJoyProcControl() : cIBJoyAxisProcess()
86 SetDeadZoneX(0.1);
87 SetDeadZoneY(0.1);
88 SetDeadZoneZ(0.0);
89 SetDeadZoneR(0.0);
92 // set and get axis ranges
93 void UiJoyAxisConfigure(eJoystickObjs axis, long *pRange, long *pMid, int deadZone)
95 long joyMin, joyMax;
97 // By default, we don't know what the range is, init to reasonable values
98 // in case there is no joystick and player wants to use the mouse, for instance
99 *pRange = 1000;
100 *pMid = 0;
102 if (g_pJoystick)
104 g_pJoystick->SetAxisRange(axis, SHRT_MIN, SHRT_MAX);
105 g_pJoystick->SetAxisDeadZone(axis, deadZone );
107 if (SUCCEEDED(g_pJoystick->GetAxisRange(axis, &joyMin, &joyMax)))
109 // round up range to keep us -1<x<1
110 *pRange = (joyMax-joyMin)/2+1;
111 if (1 > *pRange)
112 *pRange = 1;
113 // on the (bad) off-chance that the set didn't work properly
114 *pMid = (joyMax+joyMin)/2;
120 // setup joystick for use
121 void UiJoyInit ()
123 sInputDeviceIter iter;
124 int i;
126 IInputManager *g_pInputMan = AppGetObj(IInputManager);
127 g_pInputMan->IterStart(&iter, IID_IJoystick);
128 if (g_pInputMan->IterFinished(&iter))
129 Warning(("No joystick devices found\n"));
130 // else
132 g_pJoystick = (IJoystick*)(g_pInputMan->IterGet(&iter));
134 UiJoyAxisConfigure(kJoystickXAxis, &joyRangeX, &joyMidX, 0);
135 UiJoyAxisConfigure(kJoystickYAxis, &joyRangeY, &joyMidY, 0);
136 UiJoyAxisConfigure(kJoystickZAxis, &joyRangeZ, &joyMidZ, 0);
137 UiJoyAxisConfigure(kJoystickRudder, &joyRangeRudder, &joyMidRudder, 0);
141 SafeRelease(g_pInputMan);
143 // get the user joybutton->key mapping
144 for ( i = 0; i < kJoyButtonsMax; i++ ) {
146 sprintf( buttonName, "button%d_key", i + 1 );
147 keyName[0] = 0;
148 config_get_raw( buttonName, keyName, sizeof(keyName) );
150 if ( keyName[0] != 0 ) {
151 if ( strcmpi( keyName, "off" ) == 0 ) {
152 // disable the joystick button entirely
153 buttonKeys[i] = JOYBUTTON_DISABLED;
154 } else {
155 // turn the button into a key
156 buttonKeys[i] = ParseKeyName( keyName );
158 } else {
159 */ // leave the button a button
160 buttonKeys[i] = 0;
161 // }
164 // get the joystick key repeat rate
165 buttonRepeatInterval = DEFAULT_BUTTON_REPEAT_INTERVAL;
166 config_get_float( "joykey_repeat_interval", &buttonRepeatInterval );
167 buttonRepeatDelay = DEFAULT_BUTTON_REPEAT_DELAY;
168 config_get_float( "joykey_repeat_delay", &buttonRepeatDelay );
170 // get the no-rudder & no-throttle overrides
171 bHasNoRudder = config_is_defined( "has_no_rudder" );
172 bHasNoThrottle = config_is_defined( "has_no_throttle" );
174 // set rudder amidships, so if user has a throttle but no rudder, the throttle
175 // triggered joy event will have a reasonable value for the rudder
176 joyBPos.x = joyMidRudder;
178 g_pInputBinder->RegisterJoyProcObj((void *)&g_dark_joy_control);
183 // This is to create/dispatch/handle joystick events
185 // I guess it's mostly so we can record/playback joystick events
188 void DispatchJoystickMoveEvent(char joyNum, int x, int y)
190 uiJoyEvent ev;
192 uiMakeMotionEvent((uiMouseEvent *)&ev);
194 ev.type = UI_EVENT_JOY;
195 ev.action = UI_JOY_MOTION;
196 ev.joynum = joyNum;
197 ev.joypos.x = x;
198 ev.joypos.y = y;
200 uiDispatchEvent( (uiEvent *)&ev );
204 void DispatchJoystickButtonEvent(char buttNum, char state)
206 uiJoyEvent ev;
208 uiMakeMotionEvent((uiMouseEvent *)&ev);
209 ev.type = UI_EVENT_JOY;
211 // this is pretty hackly, but the deal is that joystick buttonup
212 // and down messages always use the 'button1' types, and the button
213 // number goes into the joynum member
214 if (state == 0)
215 ev.action = UI_JOY_BUTTON1UP;
216 else
217 ev.action = UI_JOY_BUTTON1DOWN;
219 ev.joynum = buttNum;
220 ev.joypos.x = 0;
221 ev.joypos.y = 0;
223 uiDispatchEvent( (uiEvent*)&ev );
227 void uiJoystickPoller(void)
229 // for filtering out button presses from flightstick hats
230 static bool bHatHasEverMoved = FALSE;
231 static bool bRudderHasEverMoved = FALSE;
232 static bool bFirstPoll = TRUE;
233 static int oldRudderPos;
234 static int oldPOV = -1;
236 // spike rejection stuff (could be merged into oldPOV, but I'm tired)
237 static int hatHist1 = -1;
238 static int hatHist2 = -1;
240 Point hatval;
241 int l;
242 int hat = 0;
243 int i;
244 sJoyState joyState;
245 uchar buttonVal;
246 HRESULT hRes;
247 float now;
249 // This is for retarded machines that
250 // can't deal with having i/o and the
251 // display locked
252 // HACK HACK HACK
253 IDisplayDevice *pDisp = AppGetObj(IDisplayDevice);
254 l = pDisp->BreakLock();
256 if (g_pJoystick != NULL)
257 hRes = g_pJoystick->GetState(&joyState);
258 else
259 hRes = E_FAIL;
261 pDisp->RestoreLock(l);
262 SafeRelease (pDisp);
264 if (FAILED(hRes))
265 return;
267 if ( (joyState.x != joyAPos.x) || (joyState.y != joyAPos.y) )
269 DispatchJoystickMoveEvent( 0, joyState.x, joyState.y );
270 joyAPos.x = joyState.x;
271 joyAPos.y = joyState.y;
274 // clamp these to fit in 16-bits
275 joyState.rz = ( SHRT_MAX < joyState.rz ) ? SHRT_MAX : joyState.rz;
276 joyState.rz = ( SHRT_MIN > joyState.rz ) ? SHRT_MIN : joyState.rz;
277 joyState.z = ( SHRT_MAX < joyState.z ) ? SHRT_MAX : joyState.z;
278 joyState.z = ( SHRT_MIN > joyState.z ) ? SHRT_MIN : joyState.z;
280 if ( bFirstPoll == TRUE ) {
281 // ignore the first rudder value - this is to handle screwed
282 // up drivers which say there are rudders when there are none
283 bFirstPoll = FALSE;
284 oldRudderPos = joyState.rz;
286 if ( oldRudderPos != joyState.rz ) {
287 bRudderHasEverMoved = TRUE;
289 oldRudderPos = joyState.rz;
290 if ( !bRudderHasEverMoved ) {
291 // if the rudder has never moved, pretend it is centered
292 joyState.rz = joyMidRudder;
295 if ( (joyState.rz != joyBPos.x) || (joyState.z != joyBPos.y) )
297 DispatchJoystickMoveEvent( 1, joyState.rz, joyState.z );
298 joyBPos.x = joyState.rz;
299 joyBPos.y = joyState.z;
303 // Pretend the hat is joystick C
304 switch(joyState.POV[0])
306 case JOY_HAT_CENTER:
307 hat = 0;
308 break;
309 case JOY_HAT_UP:
310 hat = 1;
311 break;
312 case JOY_HAT_RIGHT:
313 hat = 2;
314 break;
315 case JOY_HAT_DOWN:
316 hat = 3;
317 break;
318 case JOY_HAT_LEFT:
319 hat = 4;
320 break;
321 case JOY_HAT_UPRIGHT:
322 hat = 5;
323 break;
324 case JOY_HAT_DOWNRIGHT:
325 hat = 6;
326 break;
327 case JOY_HAT_DOWNLEFT:
328 hat = 7;
329 break;
330 case JOY_HAT_UPLEFT:
331 hat = 8;
332 break;
335 // has the hat ever moved?
336 if (joyState.POV[0] != oldPOV)
338 if (oldPOV == -1)
339 oldPOV = joyState.POV[0];
340 else
341 bHatHasEverMoved = TRUE;
344 hatval = hatVals[hat];
346 // spike reject - if the hat hasn't been held for 2 successive frames, don't
347 // let the message code know about it
348 if ((hat==hatHist1) && (hat != hatHist2))
351 if ( (hatval.x != joyCPos.x) || (hatval.y != joyCPos.y) )
353 DispatchJoystickMoveEvent( 2, hatval.x, hatval.y );
355 joyCPos.x = hatval.x;
356 joyCPos.y = hatval.y;
358 // Now we dispatch key events to spoof
359 if (hat!=lastHat) {
360 uiCookedKeyEvent ev;
362 // dispatch up event
363 if (lastHat!=0) {
364 ev.type = UI_EVENT_KBD_COOKED;
365 ev.code = hatCodes[lastHat];
366 uiDispatchEvent((uiEvent *)&ev);
369 // dispatch down
370 if (hat!=0) {
371 // dispatch down event
372 ev.type = UI_EVENT_KBD_COOKED;
373 ev.code = DOWN(hatCodes[hat]);
374 uiDispatchEvent((uiEvent *)&ev);
377 lastHat = hat;
383 hatHist2 = hatHist1;
384 hatHist1 = hat;
386 now = (double)(tm_get_millisec()/1000.0);
387 for(i=0;i<MAX_BUTTONS;i++)
390 // this ugliness is because a CH hat shows up as a button pattern
391 // and our joystick lib doesn't know if you have a hat, and if you don't,
392 // 'hat' is always HAT_UP. Jeez.
393 if ( (i>3) || ( !hat ) || (!bHatHasEverMoved))
396 buttonVal = (joyState.buttons[i] != 0);
397 if ( buttonVal != joyButtons[i] )
399 joyButtons[i] = buttonVal;
400 DispatchJoystickButtonEvent( i, joyButtons[i] );
401 // set first button repeat time
402 buttonRepeatTime[i] = now + buttonRepeatDelay;
403 } else if ( buttonVal != 0 ) {
404 // button is still depressed - see if it should repeat
405 if ( (buttonKeys[i] & BUTTON_KEY_REPEAT)
406 && (now > buttonRepeatTime[i]) ) {
407 // button has repeated
408 buttonRepeatTime[i] = now + buttonRepeatInterval;
409 DispatchJoystickButtonEvent( i, joyButtons[i] );
418 // set midpoint of x, y and rudders to current joystick position
420 void uiSetControlCenter( void )
422 sJoyState joyState;
423 HRESULT hRes;
425 if ( g_pJoystick != NULL ) {
426 hRes = g_pJoystick->GetState(&joyState);
427 if ( SUCCEEDED( hRes ) ) {
428 // set midpoints to be current joystick/throttle/rudder position
429 joyMidX = joyState.x;
430 joyMidY = joyState.y;
431 joyMidRudder = joyState.rz;
437 // Okay, this is how we set the global
438 // joystick state, which is actually more
439 // useful for most of our stuff than making
440 // it event based.
441 bool uiJoystickHandler(uiEvent* pEv, Region* pReg, void* state)
443 uiJoyEvent* pJ;
445 if (pEv->type != UI_EVENT_JOY)
446 return FALSE;
448 pJ = (uiJoyEvent*)pEv;
450 switch(pJ->action)
452 case UI_JOY_BUTTON1UP:
453 joyButtons[pJ->joynum] = 0;
454 gJoyInfo.buttons[pJ->joynum] = FALSE;
455 break;
456 case UI_JOY_BUTTON1DOWN:
457 joyButtons[pJ->joynum] = 1;
458 gJoyInfo.buttons[pJ->joynum] = TRUE;
459 break;
460 case UI_JOY_MOTION:
461 switch( pJ->joynum)
463 case 0:
464 joyAPos.x = pJ->joypos.x;
465 joyAPos.y = pJ->joypos.y;
466 gJoyInfo.joyAX = (float)(joyAPos.x-joyMidX) / joyRangeX;
467 gJoyInfo.joyAY = (float)(joyAPos.y-joyMidY) / joyRangeY;
469 break;
470 case 1:
471 joyBPos.x = pJ->joypos.x;
472 joyBPos.y = pJ->joypos.y;
473 if ( !bHasNoRudder ) {
474 gJoyInfo.joyBX = (float)(joyBPos.x-joyMidRudder) / joyRangeRudder;
476 if ( !bHasNoThrottle ) {
477 gJoyInfo.joyBY = (float)(joyBPos.y-joyMidZ) / joyRangeZ;
479 break;
480 case 2:
481 joyCPos.x = pJ->joypos.x;
482 joyCPos.y = pJ->joypos.y;
483 gJoyInfo.hatX = (float)joyCPos.x;
484 gJoyInfo.hatY = (float)joyCPos.y;
485 break;
487 break;
488 default:
489 break;
492 return TRUE;