INPUT: Added alternate C Button and mouse_keys
[mupen64plus-pandora.git] / source / mupen64plus-input-sdl / src / plugin.c
CommitLineData
48d52ab5 1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2 * Mupen64plus-input-sdl - plugin.c *
3 * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
4 * Copyright (C) 2008-2011 Richard Goedeken *
5 * Copyright (C) 2008 Tillin9 *
6 * Copyright (C) 2002 Blight *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include <SDL.h>
29
30#define M64P_PLUGIN_PROTOTYPES 1
31#include "m64p_types.h"
32#include "m64p_plugin.h"
33#include "m64p_common.h"
34#include "m64p_config.h"
35
36#include "plugin.h"
37#include "config.h"
38#include "version.h"
39#include "osal_dynamiclib.h"
40
41#ifdef __linux__
42#include <unistd.h>
43#include <dirent.h>
44#include <fcntl.h>
45#include <sys/types.h>
46#include <linux/input.h>
47#endif /* __linux__ */
48
49#include <errno.h>
50
51/* defines for the force feedback rumble support */
52#ifdef __linux__
53#define BITS_PER_LONG (sizeof(long) * 8)
54#define OFF(x) ((x)%BITS_PER_LONG)
55#define BIT(x) (1UL<<OFF(x))
56#define LONG(x) ((x)/BITS_PER_LONG)
57#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
58#endif //__linux__
59
60/* definitions of pointers to Core config functions */
61ptr_ConfigOpenSection ConfigOpenSection = NULL;
62ptr_ConfigDeleteSection ConfigDeleteSection = NULL;
63ptr_ConfigSaveSection ConfigSaveSection = NULL;
64ptr_ConfigListParameters ConfigListParameters = NULL;
65ptr_ConfigSaveFile ConfigSaveFile = NULL;
66ptr_ConfigSetParameter ConfigSetParameter = NULL;
67ptr_ConfigGetParameter ConfigGetParameter = NULL;
68ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL;
69ptr_ConfigSetDefaultInt ConfigSetDefaultInt = NULL;
70ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat = NULL;
71ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL;
72ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL;
73ptr_ConfigGetParamInt ConfigGetParamInt = NULL;
74ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL;
75ptr_ConfigGetParamBool ConfigGetParamBool = NULL;
76ptr_ConfigGetParamString ConfigGetParamString = NULL;
77
78ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath = NULL;
79ptr_ConfigGetUserConfigPath ConfigGetUserConfigPath = NULL;
80ptr_ConfigGetUserDataPath ConfigGetUserDataPath = NULL;
81ptr_ConfigGetUserCachePath ConfigGetUserCachePath = NULL;
82
83/* global data definitions */
84SController controller[4]; // 4 controllers
85
86/* static data definitions */
87static void (*l_DebugCallback)(void *, int, const char *) = NULL;
88static void *l_DebugCallContext = NULL;
89static int l_PluginInit = 0;
90
91static unsigned short button_bits[] = {
92 0x0001, // R_DPAD
93 0x0002, // L_DPAD
94 0x0004, // D_DPAD
95 0x0008, // U_DPAD
96 0x0010, // START_BUTTON
97 0x0020, // Z_TRIG
98 0x0040, // B_BUTTON
99 0x0080, // A_BUTTON
100 0x0100, // R_CBUTTON
101 0x0200, // L_CBUTTON
102 0x0400, // D_CBUTTON
103 0x0800, // U_CBUTTON
104 0x1000, // R_TRIG
105 0x2000, // L_TRIG
106 0x4000, // Mempak switch
6ff3e5eb 107 0x8000, // Rumblepak switch
108 0x0100, // R_CBUTTON alternate
109 0x0200, // L_CBUTTON alternate
110 0x0400, // D_CBUTTON alternate
111 0x0800 // U_CBUTTON alternate
48d52ab5 112};
113
114static int romopen = 0; // is a rom opened
115
116static unsigned char myKeyState[SDL_NUM_SCANCODES];
117
118#ifdef __linux__
119static struct ff_effect ffeffect[4];
120static struct ff_effect ffstrong[4];
121static struct ff_effect ffweak[4];
122#endif //__linux__
123
124/* Global functions */
125void DebugMessage(int level, const char *message, ...)
126{
127 char msgbuf[1024];
128 va_list args;
129
130 if (l_DebugCallback == NULL)
131 return;
132
133 va_start(args, message);
134 vsprintf(msgbuf, message, args);
135
136 (*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
137
138 va_end(args);
139}
140
141static CONTROL temp_core_controlinfo[4];
142
143/* Mupen64Plus plugin functions */
144EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
145 void (*DebugCallback)(void *, int, const char *))
146{
147 ptr_CoreGetAPIVersions CoreAPIVersionFunc;
148
149 int i, ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion;
150
151 if (l_PluginInit)
152 return M64ERR_ALREADY_INIT;
153
154 /* first thing is to set the callback function for debug info */
155 l_DebugCallback = DebugCallback;
156 l_DebugCallContext = Context;
157
158 /* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */
159 CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
160 if (CoreAPIVersionFunc == NULL)
161 {
162 DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found.");
163 return M64ERR_INCOMPATIBLE;
164 }
165
166 (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
167 if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000) || ConfigAPIVersion < CONFIG_API_VERSION)
168 {
169 DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)",
170 VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION));
171 return M64ERR_INCOMPATIBLE;
172 }
173
174 /* Get the core config function pointers from the library handle */
175 ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection");
176 ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection");
177 ConfigSaveFile = (ptr_ConfigSaveFile) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveFile");
178 ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection");
179 ConfigListParameters = (ptr_ConfigListParameters) osal_dynlib_getproc(CoreLibHandle, "ConfigListParameters");
180 ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter");
181 ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter");
182 ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt");
183 ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat");
184 ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool");
185 ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString");
186 ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt");
187 ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat");
188 ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool");
189 ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString");
190
191 ConfigGetSharedDataFilepath = (ptr_ConfigGetSharedDataFilepath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetSharedDataFilepath");
192 ConfigGetUserConfigPath = (ptr_ConfigGetUserConfigPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserConfigPath");
193 ConfigGetUserDataPath = (ptr_ConfigGetUserDataPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserDataPath");
194 ConfigGetUserCachePath = (ptr_ConfigGetUserCachePath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserCachePath");
195
196 if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSaveFile || !ConfigSaveSection || !ConfigSetParameter || !ConfigGetParameter ||
197 !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString ||
198 !ConfigGetParamInt || !ConfigGetParamFloat || !ConfigGetParamBool || !ConfigGetParamString ||
199 !ConfigGetSharedDataFilepath || !ConfigGetUserConfigPath || !ConfigGetUserDataPath || !ConfigGetUserCachePath)
200 {
201 DebugMessage(M64MSG_ERROR, "Couldn't connect to Core configuration functions");
202 return M64ERR_INCOMPATIBLE;
203 }
204
205 /* reset controllers */
206 memset(controller, 0, sizeof(SController) * 4);
207 for (i = 0; i < SDL_NUM_SCANCODES; i++)
208 {
209 myKeyState[i] = 0;
210 }
211 /* set CONTROL struct pointers to the temporary static array */
212 /* this small struct is used to tell the core whether each controller is plugged in, and what type of pak is connected */
213 /* we only need it so that we can call load_configuration below, to auto-config for a GUI front-end */
214 for (i = 0; i < 4; i++)
215 controller[i].control = temp_core_controlinfo + i;
216
217 /* read plugin config from core config database, auto-config if necessary and update core database */
218 load_configuration(1);
219
220 l_PluginInit = 1;
221 return M64ERR_SUCCESS;
222}
223
224EXPORT m64p_error CALL PluginShutdown(void)
225{
226 if (!l_PluginInit)
227 return M64ERR_NOT_INIT;
228
229 /* reset some local variables */
230 l_DebugCallback = NULL;
231 l_DebugCallContext = NULL;
232
233 l_PluginInit = 0;
234 return M64ERR_SUCCESS;
235}
236
237EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
238{
239 /* set version info */
240 if (PluginType != NULL)
241 *PluginType = M64PLUGIN_INPUT;
242
243 if (PluginVersion != NULL)
244 *PluginVersion = PLUGIN_VERSION;
245
246 if (APIVersion != NULL)
247 *APIVersion = INPUT_PLUGIN_API_VERSION;
248
249 if (PluginNamePtr != NULL)
250 *PluginNamePtr = PLUGIN_NAME;
251
252 if (Capabilities != NULL)
253 {
254 *Capabilities = 0;
255 }
256
257 return M64ERR_SUCCESS;
258}
259
260/* Helper function to handle the SDL keys */
261static void
262doSdlKeys(unsigned char* keystate)
263{
264 int c, b, axis_val, axis_max_val;
265 static int grabmouse = 1, grabtoggled = 0;
266
267 axis_max_val = 80;
6ff3e5eb 268/* if (keystate[SDL_SCANCODE_RCTRL])
48d52ab5 269 axis_max_val -= 40;
270 if (keystate[SDL_SCANCODE_RSHIFT])
6ff3e5eb 271 axis_max_val -= 20;*/
48d52ab5 272
273 for( c = 0; c < 4; c++ )
274 {
275 for( b = 0; b < 16; b++ )
276 {
277 if( controller[c].button[b].key == SDL_SCANCODE_UNKNOWN || ((int) controller[c].button[b].key) < 0)
278 continue;
279 if( keystate[controller[c].button[b].key] )
280 controller[c].buttons.Value |= button_bits[b];
281 }
282 for( b = 0; b < 2; b++ )
283 {
284 // from the N64 func ref: The 3D Stick data is of type signed char and in
285 // the range between 80 and -80. (32768 / 409 = ~80.1)
286 if( b == 0 )
287 axis_val = controller[c].buttons.X_AXIS;
288 else
289 axis_val = -controller[c].buttons.Y_AXIS;
290
291 if( controller[c].axis[b].key_a != SDL_SCANCODE_UNKNOWN && ((int) controller[c].axis[b].key_a) > 0)
292 if( keystate[controller[c].axis[b].key_a] )
293 axis_val = -axis_max_val;
294 if( controller[c].axis[b].key_b != SDL_SCANCODE_UNKNOWN && ((int) controller[c].axis[b].key_b) > 0)
295 if( keystate[controller[c].axis[b].key_b] )
296 axis_val = axis_max_val;
297
298 if( b == 0 )
299 controller[c].buttons.X_AXIS = axis_val;
300 else
301 controller[c].buttons.Y_AXIS = -axis_val;
302 }
303 if (controller[c].mouse)
304 {
6ff3e5eb 305 /* if (keystate[SDL_SCANCODE_LCTRL] && keystate[SDL_SCANCODE_LALT])
48d52ab5 306 {
307 if (!grabtoggled)
308 {
309 grabtoggled = 1;
310 grabmouse = !grabmouse;
311 // grab/ungrab mouse
312#if SDL_VERSION_ATLEAST(2,0,0)
313#warning SDL mouse grabbing not yet supported with SDL 2.0
314#else
315 SDL_WM_GrabInput( grabmouse ? SDL_GRAB_ON : SDL_GRAB_OFF );
316#endif
317 SDL_ShowCursor( grabmouse ? 0 : 1 );
318 }
319 }
320 else grabtoggled = 0;
6ff3e5eb 321 */
48d52ab5 322 }
323 }
324}
325
326static unsigned char DataCRC( unsigned char *Data, int iLenght )
327{
328 unsigned char Remainder = Data[0];
329
330 int iByte = 1;
331 unsigned char bBit = 0;
332
333 while( iByte <= iLenght )
334 {
335 int HighBit = ((Remainder & 0x80) != 0);
336 Remainder = Remainder << 1;
337
338 Remainder += ( iByte < iLenght && Data[iByte] & (0x80 >> bBit )) ? 1 : 0;
339
340 Remainder ^= (HighBit) ? 0x85 : 0;
341
342 bBit++;
343 iByte += bBit/8;
344 bBit %= 8;
345 }
346
347 return Remainder;
348}
349
350/******************************************************************
351 Function: ControllerCommand
352 Purpose: To process the raw data that has just been sent to a
353 specific controller.
354 input: - Controller Number (0 to 3) and -1 signalling end of
355 processing the pif ram.
356 - Pointer of data to be processed.
357 output: none
358
359 note: This function is only needed if the DLL is allowing raw
360 data, or the plugin is set to raw
361
362 the data that is being processed looks like this:
363 initilize controller: 01 03 00 FF FF FF
364 read controller: 01 04 01 FF FF FF FF
365*******************************************************************/
366EXPORT void CALL ControllerCommand(int Control, unsigned char *Command)
367{
368 unsigned char *Data = &Command[5];
369
370 if (Control == -1)
371 return;
372
373 switch (Command[2])
374 {
375 case RD_GETSTATUS:
376#ifdef _DEBUG
377 DebugMessage(M64MSG_INFO, "Get status");
378#endif
379 break;
380 case RD_READKEYS:
381#ifdef _DEBUG
382 DebugMessage(M64MSG_INFO, "Read keys");
383#endif
384 break;
385 case RD_READPAK:
386#ifdef _DEBUG
387 DebugMessage(M64MSG_INFO, "Read pak");
388#endif
389 if (controller[Control].control->Plugin == PLUGIN_RAW)
390 {
391 unsigned int dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
392
393 if(( dwAddress >= 0x8000 ) && ( dwAddress < 0x9000 ) )
394 memset( Data, 0x80, 32 );
395 else
396 memset( Data, 0x00, 32 );
397
398 Data[32] = DataCRC( Data, 32 );
399 }
400 break;
401 case RD_WRITEPAK:
402#ifdef _DEBUG
403 DebugMessage(M64MSG_INFO, "Write pak");
404#endif
405 if (controller[Control].control->Plugin == PLUGIN_RAW)
406 {
407 unsigned int dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
408 if (dwAddress == PAK_IO_RUMBLE && *Data)
409 DebugMessage(M64MSG_VERBOSE, "Triggering rumble pack.");
410#ifdef __linux__
411 struct input_event play;
412 if( dwAddress == PAK_IO_RUMBLE && controller[Control].event_joystick != 0)
413 {
414 if( *Data )
415 {
416 play.type = EV_FF;
417 play.code = ffeffect[Control].id;
418 play.value = 1;
419
420 if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
421 perror("Error starting rumble effect");
422
423 }
424 else
425 {
426 play.type = EV_FF;
427 play.code = ffeffect[Control].id;
428 play.value = 0;
429
430 if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
431 perror("Error stopping rumble effect");
432 }
433 }
434#endif //__linux__
435 Data[32] = DataCRC( Data, 32 );
436 }
437 break;
438 case RD_RESETCONTROLLER:
439#ifdef _DEBUG
440 DebugMessage(M64MSG_INFO, "Reset controller");
441#endif
442 break;
443 case RD_READEEPROM:
444#ifdef _DEBUG
445 DebugMessage(M64MSG_INFO, "Read eeprom");
446#endif
447 break;
448 case RD_WRITEEPROM:
449#ifdef _DEBUG
450 DebugMessage(M64MSG_INFO, "Write eeprom");
451#endif
452 break;
453 }
454}
455
456/******************************************************************
457 Function: GetKeys
458 Purpose: To get the current state of the controllers buttons.
459 input: - Controller Number (0 to 3)
460 - A pointer to a BUTTONS structure to be filled with
461 the controller state.
462 output: none
463*******************************************************************/
464EXPORT void CALL GetKeys( int Control, BUTTONS *Keys )
465{
466 static int mousex_residual = 0;
467 static int mousey_residual = 0;
468 int b, axis_val;
469 SDL_Event event;
470 unsigned char mstate;
471
472 // Handle keyboard input first
473 doSdlKeys(SDL_GetKeyboardState(NULL));
474 doSdlKeys(myKeyState);
475
476 // read joystick state
477 SDL_JoystickUpdate();
478
479 if( controller[Control].device >= 0 )
480 {
6ff3e5eb 481 for( b = 0; b < NUM_BUTTONS; b++ )
48d52ab5 482 {
483 if( controller[Control].button[b].button >= 0 )
484 if( SDL_JoystickGetButton( controller[Control].joystick, controller[Control].button[b].button ) )
485 controller[Control].buttons.Value |= button_bits[b];
486
487 if( controller[Control].button[b].axis >= 0 )
488 {
489 int deadzone = controller[Control].button[b].axis_deadzone;
490 axis_val = SDL_JoystickGetAxis( controller[Control].joystick, controller[Control].button[b].axis );
491 if (deadzone < 0)
492 deadzone = 6000; /* default */
493 if( (controller[Control].button[b].axis_dir < 0) && (axis_val <= -deadzone) )
494 controller[Control].buttons.Value |= button_bits[b];
495 else if( (controller[Control].button[b].axis_dir > 0) && (axis_val >= deadzone) )
496 controller[Control].buttons.Value |= button_bits[b];
497 }
498
499 if( controller[Control].button[b].hat >= 0 )
500 {
501 if( controller[Control].button[b].hat_pos > 0 )
502 if( SDL_JoystickGetHat( controller[Control].joystick, controller[Control].button[b].hat ) & controller[Control].button[b].hat_pos )
503 controller[Control].buttons.Value |= button_bits[b];
504 }
505 }
506 for( b = 0; b < 2; b++ )
507 {
508 /* from the N64 func ref: The 3D Stick data is of type signed char and in the range between -80 and +80 */
509 int deadzone = controller[Control].axis_deadzone[b];
510 int range = controller[Control].axis_peak[b] - controller[Control].axis_deadzone[b];
511 /* skip this axis if the deadzone/peak values are invalid */
512 if (deadzone < 0 || range < 1)
513 continue;
514
515 if( b == 0 )
516 axis_val = controller[Control].buttons.X_AXIS;
517 else
518 axis_val = -controller[Control].buttons.Y_AXIS;
519
520 if( controller[Control].axis[b].axis_a >= 0 ) /* up and left for N64 */
521 {
522 int joy_val = SDL_JoystickGetAxis(controller[Control].joystick, controller[Control].axis[b].axis_a);
523 int axis_dir = controller[Control].axis[b].axis_dir_a;
524 if (joy_val * axis_dir > deadzone)
525 axis_val = -((abs(joy_val) - deadzone) * 80 / range);
526 if (axis_val < -80)
527 axis_val = -80;
528 }
529 if( controller[Control].axis[b].axis_b >= 0 ) /* down and right for N64 */
530 {
531 int joy_val = SDL_JoystickGetAxis(controller[Control].joystick, controller[Control].axis[b].axis_b);
532 int axis_dir = controller[Control].axis[b].axis_dir_b;
533 if (joy_val * axis_dir > deadzone)
534 axis_val = ((abs(joy_val) - deadzone) * 80 / range);
535 if (axis_val > 80)
536 axis_val = 80;
537 }
538 if( controller[Control].axis[b].hat >= 0 )
539 {
540 if( controller[Control].axis[b].hat_pos_a >= 0 )
541 if( SDL_JoystickGetHat( controller[Control].joystick, controller[Control].axis[b].hat ) & controller[Control].axis[b].hat_pos_a )
542 axis_val = -80;
543 if( controller[Control].axis[b].hat_pos_b >= 0 )
544 if( SDL_JoystickGetHat( controller[Control].joystick, controller[Control].axis[b].hat ) & controller[Control].axis[b].hat_pos_b )
545 axis_val = 80;
546 }
547
548 if( controller[Control].axis[b].button_a >= 0 )
549 if( SDL_JoystickGetButton( controller[Control].joystick, controller[Control].axis[b].button_a ) )
550 axis_val = -80;
551 if( controller[Control].axis[b].button_b >= 0 )
552 if( SDL_JoystickGetButton( controller[Control].joystick, controller[Control].axis[b].button_b ) )
553 axis_val = 80;
554
555 if( b == 0 )
556 controller[Control].buttons.X_AXIS = axis_val;
557 else
558 controller[Control].buttons.Y_AXIS = -axis_val;
559 }
560 }
561
562 // process mouse events
563 mstate = SDL_GetMouseState( NULL, NULL );
6ff3e5eb 564 for( b = 0; b < NUM_BUTTONS; b++ )
48d52ab5 565 {
566 if( controller[Control].button[b].mouse < 1 )
567 continue;
568 if( mstate & SDL_BUTTON(controller[Control].button[b].mouse) )
569 controller[Control].buttons.Value |= button_bits[b];
570 }
571
6ff3e5eb 572 if ((controller[Control].mouse)
573 || ((controller[Control].mouse_up) || (controller[Control].mouse_down) || (controller[Control].mouse_left) || (controller[Control].mouse_right) ))
48d52ab5 574 {
575#if SDL_VERSION_ATLEAST(2,0,0)
576#warning SDL mouse grabbing not yet supported with SDL 2.0
577#else
578 if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON)
579 {
580 SDL_PumpEvents();
581#if SDL_VERSION_ATLEAST(1,3,0)
582 while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) == 1)
583#else
584 while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION)) == 1)
585#endif
586 {
587 if (event.motion.xrel)
588 {
589 mousex_residual += (int) (event.motion.xrel * controller[Control].mouse_sens[0]);
590 }
591 if (event.motion.yrel)
592 {
593 mousey_residual += (int) (event.motion.yrel * controller[Control].mouse_sens[1]);
594 }
595 }
596 }
597 else
598#endif
599 {
600 mousex_residual = 0;
601 mousey_residual = 0;
602 }
603 axis_val = mousex_residual;
604 if (axis_val < -80)
605 axis_val = -80;
606 else if (axis_val > 80)
607 axis_val = 80;
6ff3e5eb 608 if (controller[Control].mouse)
609 controller[Control].buttons.X_AXIS = axis_val;
610 else {
611 for( b = 0; b < NUM_BUTTONS; b++ ) {
612 if( controller[Control].button[b].mouse_left > 0 )
613 if (controller[Control].mouse_left) controller[Control].buttons.Value |= (axis_val<-40)?button_bits[b]:0;
614 if( controller[Control].button[b].mouse_right > 0 )
615 if (controller[Control].mouse_right) controller[Control].buttons.Value |= (axis_val>40)?button_bits[b]:0;
616 }
617 }
48d52ab5 618 axis_val = mousey_residual;
619 if (axis_val < -80)
620 axis_val = -80;
621 else if (axis_val > 80)
622 axis_val = 80;
6ff3e5eb 623 if (controller[Control].mouse)
624 controller[Control].buttons.Y_AXIS = -axis_val;
625 else {
626 for( b = 0; b < NUM_BUTTONS; b++ ) {
627 if( controller[Control].button[b].mouse_up > 0 )
628 if (controller[Control].mouse_up) controller[Control].buttons.Value |= (axis_val<-40)?button_bits[b]:0;
629 if( controller[Control].button[b].mouse_down > 0 )
630 if (controller[Control].mouse_down) controller[Control].buttons.Value |= (axis_val>40)?button_bits[b]:0;
631 }
632 }
48d52ab5 633 /* the mouse x/y values decay exponentially */
634 mousex_residual = (mousex_residual * 224) / 256;
635 mousey_residual = (mousey_residual * 224) / 256;
636 }
637
638#ifdef _DEBUG
639 DebugMessage(M64MSG_VERBOSE, "Controller #%d value: 0x%8.8X", Control, *(int *)&controller[Control].buttons );
640#endif
641 *Keys = controller[Control].buttons;
642
643 /* handle mempack / rumblepak switching (only if rumble is active on joystick) */
644#ifdef __linux__
645 if (controller[Control].event_joystick != 0)
646 {
647 struct input_event play;
648 static unsigned int SwitchPackTime[4] = {0, 0, 0, 0}, SwitchPackType[4] = {0, 0, 0, 0};
649 // when the user switches packs, we should mimick the act of removing 1 pack, and then inserting another 1 second later
650 if (controller[Control].buttons.Value & button_bits[14])
651 {
652 SwitchPackTime[Control] = SDL_GetTicks(); // time at which the 'switch pack' command was given
653 SwitchPackType[Control] = PLUGIN_MEMPAK; // type of new pack to insert
654 controller[Control].control->Plugin = PLUGIN_NONE;// remove old pack
655 play.type = EV_FF;
656 play.code = ffweak[Control].id;
657 play.value = 1;
658 if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
659 perror("Error starting rumble effect");
660 }
661 if (controller[Control].buttons.Value & button_bits[15])
662 {
663 SwitchPackTime[Control] = SDL_GetTicks(); // time at which the 'switch pack' command was given
664 SwitchPackType[Control] = PLUGIN_RAW; // type of new pack to insert
665 controller[Control].control->Plugin = PLUGIN_NONE;// remove old pack
666 play.type = EV_FF;
667 play.code = ffstrong[Control].id;
668 play.value = 1;
669 if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
670 perror("Error starting rumble effect");
671 }
672 // handle inserting new pack if the time has arrived
673 if (SwitchPackTime[Control] != 0 && (SDL_GetTicks() - SwitchPackTime[Control]) >= 1000)
674 {
675 controller[Control].control->Plugin = SwitchPackType[Control];
676 SwitchPackTime[Control] = 0;
677 }
678 }
679#endif /* __linux__ */
680
681 controller[Control].buttons.Value = 0;
682}
683
684static void InitiateRumble(int cntrl)
685{
686#ifdef __linux__
687 DIR* dp;
688 struct dirent* ep;
689 unsigned long features[4];
690 char temp[128];
691 char temp2[128];
692 int iFound = 0;
693
694 controller[cntrl].event_joystick = 0;
695
696 sprintf(temp,"/sys/class/input/js%d/device", controller[cntrl].device);
697 dp = opendir(temp);
698
699 if(dp==NULL)
700 return;
701
702 while ((ep=readdir(dp)))
703 {
704 if (strncmp(ep->d_name, "event",5)==0)
705 {
706 sprintf(temp, "/dev/input/%s", ep->d_name);
707 iFound = 1;
708 break;
709 }
710 else if(strncmp(ep->d_name,"input:event", 11)==0)
711 {
712 sscanf(ep->d_name, "input:%s", temp2);
713 sprintf(temp, "/dev/input/%s", temp2);
714 iFound = 1;
715 break;
716 }
717 else if(strncmp(ep->d_name,"input:input", 11)==0)
718 {
719 strcat(temp, "/");
720 strcat(temp, ep->d_name);
721 closedir (dp);
722 dp = opendir(temp);
723 if(dp==NULL)
724 return;
725 }
726 }
727
728 closedir(dp);
729
730 if (!iFound)
731 {
732 DebugMessage(M64MSG_WARNING, "Couldn't find input event for rumble support.");
733 return;
734 }
735
736 controller[cntrl].event_joystick = open(temp, O_RDWR);
737 if(controller[cntrl].event_joystick==-1)
738 {
739 DebugMessage(M64MSG_WARNING, "Couldn't open device file '%s' for rumble support.", temp);
740 controller[cntrl].event_joystick = 0;
741 return;
742 }
743
744 if(ioctl(controller[cntrl].event_joystick, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)==-1)
745 {
746 DebugMessage(M64MSG_WARNING, "Linux kernel communication failed for force feedback (rumble).\n");
747 controller[cntrl].event_joystick = 0;
748 return;
749 }
750
751 if(!test_bit(FF_RUMBLE, features))
752 {
753 DebugMessage(M64MSG_WARNING, "No rumble supported on N64 joystick #%i", cntrl + 1);
754 controller[cntrl].event_joystick = 0;
755 return;
756 }
757
758 ffeffect[cntrl].type = FF_RUMBLE;
759 ffeffect[cntrl].id = -1;
760 ffeffect[cntrl].u.rumble.strong_magnitude = 0xFFFF;
761 ffeffect[cntrl].u.rumble.weak_magnitude = 0xFFFF;
762 ffeffect[cntrl].replay.length = 0x7fff; // hack: xboxdrv is buggy and doesn't support infinite replay.
763 // when xboxdrv is fixed (https://github.com/Grumbel/xboxdrv/issues/47),
764 // please remove this
765
766 ioctl(controller[cntrl].event_joystick, EVIOCSFF, &ffeffect[cntrl]);
767
768 ffstrong[cntrl].type = FF_RUMBLE;
769 ffstrong[cntrl].id = -1;
770 ffstrong[cntrl].u.rumble.strong_magnitude = 0xFFFF;
771 ffstrong[cntrl].u.rumble.weak_magnitude = 0x0000;
772 ffstrong[cntrl].replay.length = 500;
773 ffstrong[cntrl].replay.delay = 0;
774
775 ioctl(controller[cntrl].event_joystick, EVIOCSFF, &ffstrong[cntrl]);
776
777 ffweak[cntrl].type = FF_RUMBLE;
778 ffweak[cntrl].id = -1;
779 ffweak[cntrl].u.rumble.strong_magnitude = 0x0000;
780 ffweak[cntrl].u.rumble.weak_magnitude = 0xFFFF;
781 ffweak[cntrl].replay.length = 500;
782 ffweak[cntrl].replay.delay = 0;
783
784 ioctl(controller[cntrl].event_joystick, EVIOCSFF, &ffweak[cntrl]);
785
786 DebugMessage(M64MSG_INFO, "Rumble activated on N64 joystick #%i", cntrl + 1);
787#endif /* __linux__ */
788}
789
790/******************************************************************
791 Function: InitiateControllers
792 Purpose: This function initialises how each of the controllers
793 should be handled.
794 input: - The handle to the main window.
795 - A controller structure that needs to be filled for
796 the emulator to know how to handle each controller.
797 output: none
798*******************************************************************/
799EXPORT void CALL InitiateControllers(CONTROL_INFO ControlInfo)
800{
801 int i;
802
803 // reset controllers
804 memset( controller, 0, sizeof( SController ) * 4 );
805 for ( i = 0; i < SDL_NUM_SCANCODES; i++)
806 {
807 myKeyState[i] = 0;
808 }
809 // set our CONTROL struct pointers to the array that was passed in to this function from the core
810 // this small struct tells the core whether each controller is plugged in, and what type of pak is connected
811 for (i = 0; i < 4; i++)
812 controller[i].control = ControlInfo.Controls + i;
813
814 // read configuration
815 load_configuration(0);
816
817 for( i = 0; i < 4; i++ )
818 {
819 // test for rumble support for this joystick
820 InitiateRumble(i);
821 // if rumble not supported, switch to mempack
822 if (controller[i].control->Plugin == PLUGIN_RAW && controller[i].event_joystick == 0)
823 controller[i].control->Plugin = PLUGIN_MEMPAK;
824 }
825
826 DebugMessage(M64MSG_INFO, "%s version %i.%i.%i initialized.", PLUGIN_NAME, VERSION_PRINTF_SPLIT(PLUGIN_VERSION));
827}
828
829/******************************************************************
830 Function: ReadController
831 Purpose: To process the raw data in the pif ram that is about to
832 be read.
833 input: - Controller Number (0 to 3) and -1 signalling end of
834 processing the pif ram.
835 - Pointer of data to be processed.
836 output: none
837 note: This function is only needed if the DLL is allowing raw
838 data.
839*******************************************************************/
840EXPORT void CALL ReadController(int Control, unsigned char *Command)
841{
842#ifdef _DEBUG
843 if (Command != NULL)
844 DebugMessage(M64MSG_INFO, "Raw Read (cont=%d): %02X %02X %02X %02X %02X %02X", Control,
845 Command[0], Command[1], Command[2], Command[3], Command[4], Command[5]);
846#endif
847}
848
849/******************************************************************
850 Function: RomClosed
851 Purpose: This function is called when a rom is closed.
852 input: none
853 output: none
854*******************************************************************/
855EXPORT void CALL RomClosed(void)
856{
857 int i;
858
859 // close joysticks
860 for( i = 0; i < 4; i++ )
861 if( controller[i].joystick )
862 {
863 SDL_JoystickClose( controller[i].joystick );
864 controller[i].joystick = NULL;
865 }
866
867 // quit SDL joystick subsystem
868 SDL_QuitSubSystem( SDL_INIT_JOYSTICK );
869
870 // release/ungrab mouse
871#if SDL_VERSION_ATLEAST(2,0,0)
872#warning SDL mouse grabbing not yet supported with SDL 2.0
873#else
874 SDL_WM_GrabInput( SDL_GRAB_OFF );
875#endif
876 SDL_ShowCursor( 1 );
877
878 romopen = 0;
879}
880
881/******************************************************************
882 Function: RomOpen
883 Purpose: This function is called when a rom is open. (from the
884 emulation thread)
885 input: none
886 output: none
887*******************************************************************/
888EXPORT int CALL RomOpen(void)
889{
890 int i;
891
892 // init SDL joystick subsystem
893 if( !SDL_WasInit( SDL_INIT_JOYSTICK ) )
894 if( SDL_InitSubSystem( SDL_INIT_JOYSTICK ) == -1 )
895 {
896 DebugMessage(M64MSG_ERROR, "Couldn't init SDL joystick subsystem: %s", SDL_GetError() );
897 return 0;
898 }
899
900 // open joysticks
901 for( i = 0; i < 4; i++ )
902 if( controller[i].device >= 0 )
903 {
904 controller[i].joystick = SDL_JoystickOpen( controller[i].device );
905 if( controller[i].joystick == NULL )
906 DebugMessage(M64MSG_WARNING, "Couldn't open joystick for controller #%d: %s", i + 1, SDL_GetError() );
907 }
908 else
909 controller[i].joystick = NULL;
910
911 // grab mouse
6ff3e5eb 912 if ((controller[0].mouse || controller[1].mouse || controller[2].mouse || controller[3].mouse)
913 ||(controller[0].mouse_up || controller[0].mouse_down || controller[0].mouse_left || controller[0].mouse_right))
48d52ab5 914 {
915#if SDL_VERSION_ATLEAST(2,0,0)
916#warning SDL mouse grabbing not yet supported with SDL 2.0
917#else
918 SDL_ShowCursor( 0 );
919 if (SDL_WM_GrabInput( SDL_GRAB_ON ) != SDL_GRAB_ON)
920 {
921 DebugMessage(M64MSG_WARNING, "Couldn't grab input! Mouse support won't work!");
922 }
923#endif
924 }
925
926 romopen = 1;
927 return 1;
928}
929
930/******************************************************************
931 Function: SDL_KeyDown
932 Purpose: To pass the SDL_KeyDown message from the emulator to the
933 plugin.
934 input: keymod and keysym of the SDL_KEYDOWN message.
935 output: none
936*******************************************************************/
937EXPORT void CALL SDL_KeyDown(int keymod, int keysym)
938{
939 myKeyState[keysym] = 1;
940}
941
942/******************************************************************
943 Function: SDL_KeyUp
944 Purpose: To pass the SDL_KeyUp message from the emulator to the
945 plugin.
946 input: keymod and keysym of the SDL_KEYUP message.
947 output: none
948*******************************************************************/
949EXPORT void CALL SDL_KeyUp(int keymod, int keysym)
950{
951 myKeyState[keysym] = 0;
952}
953