Input SDL plugin. Compile and run on the OpenPandora. Include config for Pandora...
[mupen64plus-pandora.git] / source / mupen64plus-input-sdl / src / plugin.c
diff --git a/source/mupen64plus-input-sdl/src/plugin.c b/source/mupen64plus-input-sdl/src/plugin.c
new file mode 100644 (file)
index 0000000..26b1814
--- /dev/null
@@ -0,0 +1,928 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *   Mupen64plus-input-sdl - plugin.c                                      *
+ *   Mupen64Plus homepage: http://code.google.com/p/mupen64plus/           *
+ *   Copyright (C) 2008-2011 Richard Goedeken                              *
+ *   Copyright (C) 2008 Tillin9                                            *
+ *   Copyright (C) 2002 Blight                                             *
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This program is distributed in the hope that it will be useful,       *
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
+ *   GNU General Public License for more details.                          *
+ *                                                                         *
+ *   You should have received a copy of the GNU General Public License     *
+ *   along with this program; if not, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.          *
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <SDL.h>
+
+#define M64P_PLUGIN_PROTOTYPES 1
+#include "m64p_types.h"
+#include "m64p_plugin.h"
+#include "m64p_common.h"
+#include "m64p_config.h"
+
+#include "plugin.h"
+#include "config.h"
+#include "version.h"
+#include "osal_dynamiclib.h"
+
+#ifdef __linux__
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <linux/input.h>
+#endif /* __linux__ */
+
+#include <errno.h>
+
+/* defines for the force feedback rumble support */
+#ifdef __linux__
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define OFF(x)  ((x)%BITS_PER_LONG)
+#define BIT(x)  (1UL<<OFF(x))
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array)    ((array[LONG(bit)] >> OFF(bit)) & 1)
+#endif //__linux__
+
+/* definitions of pointers to Core config functions */
+ptr_ConfigOpenSection      ConfigOpenSection = NULL;
+ptr_ConfigDeleteSection    ConfigDeleteSection = NULL;
+ptr_ConfigSaveSection      ConfigSaveSection = NULL;
+ptr_ConfigListParameters   ConfigListParameters = NULL;
+ptr_ConfigSaveFile         ConfigSaveFile = NULL;
+ptr_ConfigSetParameter     ConfigSetParameter = NULL;
+ptr_ConfigGetParameter     ConfigGetParameter = NULL;
+ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL;
+ptr_ConfigSetDefaultInt    ConfigSetDefaultInt = NULL;
+ptr_ConfigSetDefaultFloat  ConfigSetDefaultFloat = NULL;
+ptr_ConfigSetDefaultBool   ConfigSetDefaultBool = NULL;
+ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL;
+ptr_ConfigGetParamInt      ConfigGetParamInt = NULL;
+ptr_ConfigGetParamFloat    ConfigGetParamFloat = NULL;
+ptr_ConfigGetParamBool     ConfigGetParamBool = NULL;
+ptr_ConfigGetParamString   ConfigGetParamString = NULL;
+
+ptr_ConfigGetSharedDataFilepath ConfigGetSharedDataFilepath = NULL;
+ptr_ConfigGetUserConfigPath     ConfigGetUserConfigPath = NULL;
+ptr_ConfigGetUserDataPath       ConfigGetUserDataPath = NULL;
+ptr_ConfigGetUserCachePath      ConfigGetUserCachePath = NULL;
+
+/* global data definitions */
+SController controller[4];   // 4 controllers
+
+/* static data definitions */
+static void (*l_DebugCallback)(void *, int, const char *) = NULL;
+static void *l_DebugCallContext = NULL;
+static int l_PluginInit = 0;
+
+static unsigned short button_bits[] = {
+    0x0001,  // R_DPAD
+    0x0002,  // L_DPAD
+    0x0004,  // D_DPAD
+    0x0008,  // U_DPAD
+    0x0010,  // START_BUTTON
+    0x0020,  // Z_TRIG
+    0x0040,  // B_BUTTON
+    0x0080,  // A_BUTTON
+    0x0100,  // R_CBUTTON
+    0x0200,  // L_CBUTTON
+    0x0400,  // D_CBUTTON
+    0x0800,  // U_CBUTTON
+    0x1000,  // R_TRIG
+    0x2000,  // L_TRIG
+    0x4000,  // Mempak switch
+    0x8000   // Rumblepak switch
+};
+
+static int romopen = 0;         // is a rom opened
+
+static unsigned char myKeyState[SDL_NUM_SCANCODES];
+
+#ifdef __linux__
+static struct ff_effect ffeffect[4];
+static struct ff_effect ffstrong[4];
+static struct ff_effect ffweak[4];
+#endif //__linux__
+
+/* Global functions */
+void DebugMessage(int level, const char *message, ...)
+{
+  char msgbuf[1024];
+  va_list args;
+
+  if (l_DebugCallback == NULL)
+      return;
+
+  va_start(args, message);
+  vsprintf(msgbuf, message, args);
+
+  (*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
+
+  va_end(args);
+}
+
+static CONTROL temp_core_controlinfo[4];
+
+/* Mupen64Plus plugin functions */
+EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
+                                   void (*DebugCallback)(void *, int, const char *))
+{
+    ptr_CoreGetAPIVersions CoreAPIVersionFunc;
+    
+    int i, ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion;
+
+    if (l_PluginInit)
+        return M64ERR_ALREADY_INIT;
+
+    /* first thing is to set the callback function for debug info */
+    l_DebugCallback = DebugCallback;
+    l_DebugCallContext = Context;
+
+    /* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */
+    CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
+    if (CoreAPIVersionFunc == NULL)
+    {
+        DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found.");
+        return M64ERR_INCOMPATIBLE;
+    }
+    
+    (*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
+    if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000) || ConfigAPIVersion < CONFIG_API_VERSION)
+    {
+        DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)",
+                VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION));
+        return M64ERR_INCOMPATIBLE;
+    }
+
+    /* Get the core config function pointers from the library handle */
+    ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection");
+    ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection");
+    ConfigSaveFile = (ptr_ConfigSaveFile) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveFile");
+    ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection");
+    ConfigListParameters = (ptr_ConfigListParameters) osal_dynlib_getproc(CoreLibHandle, "ConfigListParameters");
+    ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter");
+    ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter");
+    ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt");
+    ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat");
+    ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool");
+    ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString");
+    ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt");
+    ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat");
+    ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool");
+    ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString");
+
+    ConfigGetSharedDataFilepath = (ptr_ConfigGetSharedDataFilepath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetSharedDataFilepath");
+    ConfigGetUserConfigPath = (ptr_ConfigGetUserConfigPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserConfigPath");
+    ConfigGetUserDataPath = (ptr_ConfigGetUserDataPath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserDataPath");
+    ConfigGetUserCachePath = (ptr_ConfigGetUserCachePath) osal_dynlib_getproc(CoreLibHandle, "ConfigGetUserCachePath");
+
+    if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSaveFile || !ConfigSaveSection || !ConfigSetParameter || !ConfigGetParameter ||
+        !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString ||
+        !ConfigGetParamInt   || !ConfigGetParamFloat   || !ConfigGetParamBool   || !ConfigGetParamString ||
+        !ConfigGetSharedDataFilepath || !ConfigGetUserConfigPath || !ConfigGetUserDataPath || !ConfigGetUserCachePath)
+    {
+        DebugMessage(M64MSG_ERROR, "Couldn't connect to Core configuration functions");
+        return M64ERR_INCOMPATIBLE;
+    }
+
+    /* reset controllers */
+    memset(controller, 0, sizeof(SController) * 4);
+    for (i = 0; i < SDL_NUM_SCANCODES; i++)
+    {
+        myKeyState[i] = 0;
+    }
+    /* set CONTROL struct pointers to the temporary static array */
+    /* this small struct is used to tell the core whether each controller is plugged in, and what type of pak is connected */
+    /* we only need it so that we can call load_configuration below, to auto-config for a GUI front-end */
+    for (i = 0; i < 4; i++)
+        controller[i].control = temp_core_controlinfo + i;
+
+    /* read plugin config from core config database, auto-config if necessary and update core database */
+    load_configuration(1);
+
+    l_PluginInit = 1;
+    return M64ERR_SUCCESS;
+}
+
+EXPORT m64p_error CALL PluginShutdown(void)
+{
+    if (!l_PluginInit)
+        return M64ERR_NOT_INIT;
+
+    /* reset some local variables */
+    l_DebugCallback = NULL;
+    l_DebugCallContext = NULL;
+
+    l_PluginInit = 0;
+    return M64ERR_SUCCESS;
+}
+
+EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
+{
+    /* set version info */
+    if (PluginType != NULL)
+        *PluginType = M64PLUGIN_INPUT;
+
+    if (PluginVersion != NULL)
+        *PluginVersion = PLUGIN_VERSION;
+
+    if (APIVersion != NULL)
+        *APIVersion = INPUT_PLUGIN_API_VERSION;
+    
+    if (PluginNamePtr != NULL)
+        *PluginNamePtr = PLUGIN_NAME;
+
+    if (Capabilities != NULL)
+    {
+        *Capabilities = 0;
+    }
+                    
+    return M64ERR_SUCCESS;
+}
+
+/* Helper function to handle the SDL keys */
+static void
+doSdlKeys(unsigned char* keystate)
+{
+    int c, b, axis_val, axis_max_val;
+    static int grabmouse = 1, grabtoggled = 0;
+
+    axis_max_val = 80;
+    if (keystate[SDL_SCANCODE_RCTRL])
+        axis_max_val -= 40;
+    if (keystate[SDL_SCANCODE_RSHIFT])
+        axis_max_val -= 20;
+
+    for( c = 0; c < 4; c++ )
+    {
+        for( b = 0; b < 16; b++ )
+        {
+            if( controller[c].button[b].key == SDL_SCANCODE_UNKNOWN || ((int) controller[c].button[b].key) < 0)
+                continue;
+            if( keystate[controller[c].button[b].key] )
+                controller[c].buttons.Value |= button_bits[b];
+        }
+        for( b = 0; b < 2; b++ )
+        {
+            // from the N64 func ref: The 3D Stick data is of type signed char and in
+            // the range between 80 and -80. (32768 / 409 = ~80.1)
+            if( b == 0 )
+                axis_val = controller[c].buttons.X_AXIS;
+            else
+                axis_val = -controller[c].buttons.Y_AXIS;
+
+            if( controller[c].axis[b].key_a != SDL_SCANCODE_UNKNOWN && ((int) controller[c].axis[b].key_a) > 0)
+                if( keystate[controller[c].axis[b].key_a] )
+                    axis_val = -axis_max_val;
+            if( controller[c].axis[b].key_b != SDL_SCANCODE_UNKNOWN && ((int) controller[c].axis[b].key_b) > 0)
+                if( keystate[controller[c].axis[b].key_b] )
+                    axis_val = axis_max_val;
+
+            if( b == 0 )
+                controller[c].buttons.X_AXIS = axis_val;
+            else
+                controller[c].buttons.Y_AXIS = -axis_val;
+        }
+        if (controller[c].mouse)
+        {
+            if (keystate[SDL_SCANCODE_LCTRL] && keystate[SDL_SCANCODE_LALT])
+            {
+                if (!grabtoggled)
+                {
+                    grabtoggled = 1;
+                    grabmouse = !grabmouse;
+                    // grab/ungrab mouse
+#if SDL_VERSION_ATLEAST(2,0,0)
+#warning SDL mouse grabbing not yet supported with SDL 2.0
+#else
+                    SDL_WM_GrabInput( grabmouse ? SDL_GRAB_ON : SDL_GRAB_OFF );
+#endif
+                    SDL_ShowCursor( grabmouse ? 0 : 1 );
+                }
+            }
+            else grabtoggled = 0;
+        }
+    }
+}
+
+static unsigned char DataCRC( unsigned char *Data, int iLenght )
+{
+    unsigned char Remainder = Data[0];
+
+    int iByte = 1;
+    unsigned char bBit = 0;
+
+    while( iByte <= iLenght )
+    {
+        int HighBit = ((Remainder & 0x80) != 0);
+        Remainder = Remainder << 1;
+
+        Remainder += ( iByte < iLenght && Data[iByte] & (0x80 >> bBit )) ? 1 : 0;
+
+        Remainder ^= (HighBit) ? 0x85 : 0;
+
+        bBit++;
+        iByte += bBit/8;
+        bBit %= 8;
+    }
+
+    return Remainder;
+}
+
+/******************************************************************
+  Function: ControllerCommand
+  Purpose:  To process the raw data that has just been sent to a
+            specific controller.
+  input:    - Controller Number (0 to 3) and -1 signalling end of
+              processing the pif ram.
+            - Pointer of data to be processed.
+  output:   none
+
+  note:     This function is only needed if the DLL is allowing raw
+            data, or the plugin is set to raw
+
+            the data that is being processed looks like this:
+            initilize controller: 01 03 00 FF FF FF
+            read controller:      01 04 01 FF FF FF FF
+*******************************************************************/
+EXPORT void CALL ControllerCommand(int Control, unsigned char *Command)
+{
+    unsigned char *Data = &Command[5];
+
+    if (Control == -1)
+        return;
+
+    switch (Command[2])
+    {
+        case RD_GETSTATUS:
+#ifdef _DEBUG
+            DebugMessage(M64MSG_INFO, "Get status");
+#endif
+            break;
+        case RD_READKEYS:
+#ifdef _DEBUG
+            DebugMessage(M64MSG_INFO, "Read keys");
+#endif
+            break;
+        case RD_READPAK:
+#ifdef _DEBUG
+            DebugMessage(M64MSG_INFO, "Read pak");
+#endif
+            if (controller[Control].control->Plugin == PLUGIN_RAW)
+            {
+                unsigned int dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
+
+                if(( dwAddress >= 0x8000 ) && ( dwAddress < 0x9000 ) )
+                    memset( Data, 0x80, 32 );
+                else
+                    memset( Data, 0x00, 32 );
+
+                Data[32] = DataCRC( Data, 32 );
+            }
+            break;
+        case RD_WRITEPAK:
+#ifdef _DEBUG
+            DebugMessage(M64MSG_INFO, "Write pak");
+#endif
+            if (controller[Control].control->Plugin == PLUGIN_RAW)
+            {
+                unsigned int dwAddress = (Command[3] << 8) + (Command[4] & 0xE0);
+              if (dwAddress == PAK_IO_RUMBLE && *Data)
+                    DebugMessage(M64MSG_VERBOSE, "Triggering rumble pack.");
+#ifdef __linux__
+                struct input_event play;
+                if( dwAddress == PAK_IO_RUMBLE && controller[Control].event_joystick != 0)
+                {
+                    if( *Data )
+                    {
+                        play.type = EV_FF;
+                        play.code = ffeffect[Control].id;
+                        play.value = 1;
+
+                        if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
+                            perror("Error starting rumble effect");
+
+                    }
+                    else
+                    {
+                        play.type = EV_FF;
+                        play.code = ffeffect[Control].id;
+                        play.value = 0;
+
+                        if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
+                            perror("Error stopping rumble effect");
+                    }
+                }
+#endif //__linux__
+                Data[32] = DataCRC( Data, 32 );
+            }
+            break;
+        case RD_RESETCONTROLLER:
+#ifdef _DEBUG
+            DebugMessage(M64MSG_INFO, "Reset controller");
+#endif
+            break;
+        case RD_READEEPROM:
+#ifdef _DEBUG
+            DebugMessage(M64MSG_INFO, "Read eeprom");
+#endif
+            break;
+        case RD_WRITEEPROM:
+#ifdef _DEBUG
+            DebugMessage(M64MSG_INFO, "Write eeprom");
+#endif
+            break;
+        }
+}
+
+/******************************************************************
+  Function: GetKeys
+  Purpose:  To get the current state of the controllers buttons.
+  input:    - Controller Number (0 to 3)
+            - A pointer to a BUTTONS structure to be filled with
+            the controller state.
+  output:   none
+*******************************************************************/
+EXPORT void CALL GetKeys( int Control, BUTTONS *Keys )
+{
+    static int mousex_residual = 0;
+    static int mousey_residual = 0;
+    int b, axis_val;
+    SDL_Event event;
+    unsigned char mstate;
+
+    // Handle keyboard input first
+    doSdlKeys(SDL_GetKeyboardState(NULL));
+    doSdlKeys(myKeyState);
+
+    // read joystick state
+    SDL_JoystickUpdate();
+
+    if( controller[Control].device >= 0 )
+    {
+        for( b = 0; b < 16; b++ )
+        {
+            if( controller[Control].button[b].button >= 0 )
+                if( SDL_JoystickGetButton( controller[Control].joystick, controller[Control].button[b].button ) )
+                    controller[Control].buttons.Value |= button_bits[b];
+
+            if( controller[Control].button[b].axis >= 0 )
+            {
+                int deadzone = controller[Control].button[b].axis_deadzone;
+                axis_val = SDL_JoystickGetAxis( controller[Control].joystick, controller[Control].button[b].axis );
+                if (deadzone < 0)
+                    deadzone = 6000; /* default */
+                if( (controller[Control].button[b].axis_dir < 0) && (axis_val <= -deadzone) )
+                    controller[Control].buttons.Value |= button_bits[b];
+                else if( (controller[Control].button[b].axis_dir > 0) && (axis_val >= deadzone) )
+                    controller[Control].buttons.Value |= button_bits[b];
+            }
+
+            if( controller[Control].button[b].hat >= 0 )
+            {
+                if( controller[Control].button[b].hat_pos > 0 )
+                    if( SDL_JoystickGetHat( controller[Control].joystick, controller[Control].button[b].hat ) & controller[Control].button[b].hat_pos )
+                        controller[Control].buttons.Value |= button_bits[b];
+            }
+        }
+        for( b = 0; b < 2; b++ )
+        {
+            /* from the N64 func ref: The 3D Stick data is of type signed char and in the range between -80 and +80 */
+            int deadzone = controller[Control].axis_deadzone[b];
+            int range = controller[Control].axis_peak[b] - controller[Control].axis_deadzone[b];
+            /* skip this axis if the deadzone/peak values are invalid */
+            if (deadzone < 0 || range < 1)
+                continue;
+
+            if( b == 0 )
+                axis_val = controller[Control].buttons.X_AXIS;
+            else
+                axis_val = -controller[Control].buttons.Y_AXIS;
+
+            if( controller[Control].axis[b].axis_a >= 0 )  /* up and left for N64 */
+            {
+                int joy_val = SDL_JoystickGetAxis(controller[Control].joystick, controller[Control].axis[b].axis_a);
+                int axis_dir = controller[Control].axis[b].axis_dir_a;
+                if (joy_val * axis_dir > deadzone)
+                    axis_val = -((abs(joy_val) - deadzone) * 80 / range);
+                if (axis_val < -80)
+                    axis_val = -80;
+            }
+            if( controller[Control].axis[b].axis_b >= 0 ) /* down and right for N64 */
+            {
+                int joy_val = SDL_JoystickGetAxis(controller[Control].joystick, controller[Control].axis[b].axis_b);
+                int axis_dir = controller[Control].axis[b].axis_dir_b;
+                if (joy_val * axis_dir > deadzone)
+                    axis_val = ((abs(joy_val) - deadzone) * 80 / range);
+                if (axis_val > 80)
+                    axis_val = 80;
+            }
+            if( controller[Control].axis[b].hat >= 0 )
+            {
+                if( controller[Control].axis[b].hat_pos_a >= 0 )
+                    if( SDL_JoystickGetHat( controller[Control].joystick, controller[Control].axis[b].hat ) & controller[Control].axis[b].hat_pos_a )
+                        axis_val = -80;
+                if( controller[Control].axis[b].hat_pos_b >= 0 )
+                    if( SDL_JoystickGetHat( controller[Control].joystick, controller[Control].axis[b].hat ) & controller[Control].axis[b].hat_pos_b )
+                        axis_val = 80;
+            }
+
+            if( controller[Control].axis[b].button_a >= 0 )
+                if( SDL_JoystickGetButton( controller[Control].joystick, controller[Control].axis[b].button_a ) )
+                    axis_val = -80;
+            if( controller[Control].axis[b].button_b >= 0 )
+                if( SDL_JoystickGetButton( controller[Control].joystick, controller[Control].axis[b].button_b ) )
+                    axis_val = 80;
+
+            if( b == 0 )
+                controller[Control].buttons.X_AXIS = axis_val;
+            else
+                controller[Control].buttons.Y_AXIS = -axis_val;
+        }
+    }
+
+    // process mouse events
+    mstate = SDL_GetMouseState( NULL, NULL );
+    for( b = 0; b < 16; b++ )
+    {
+        if( controller[Control].button[b].mouse < 1 )
+            continue;
+        if( mstate & SDL_BUTTON(controller[Control].button[b].mouse) )
+            controller[Control].buttons.Value |= button_bits[b];
+    }
+
+    if (controller[Control].mouse)
+    {
+#if SDL_VERSION_ATLEAST(2,0,0)
+#warning SDL mouse grabbing not yet supported with SDL 2.0
+#else
+        if (SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON)
+        {
+            SDL_PumpEvents();
+#if SDL_VERSION_ATLEAST(1,3,0)
+            while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) == 1)
+#else
+            while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION)) == 1)
+#endif
+            {
+                if (event.motion.xrel)
+                {
+                    mousex_residual += (int) (event.motion.xrel * controller[Control].mouse_sens[0]);
+                }
+                if (event.motion.yrel)
+                {
+                    mousey_residual += (int) (event.motion.yrel * controller[Control].mouse_sens[1]);
+                }
+            }
+        }
+        else
+#endif
+        {
+            mousex_residual = 0;
+            mousey_residual = 0;
+        }
+        axis_val = mousex_residual;
+        if (axis_val < -80)
+            axis_val = -80;
+        else if (axis_val > 80)
+            axis_val = 80;
+        controller[Control].buttons.X_AXIS = axis_val;
+        axis_val = mousey_residual;
+        if (axis_val < -80)
+            axis_val = -80;
+        else if (axis_val > 80)
+            axis_val = 80;
+        controller[Control].buttons.Y_AXIS = -axis_val;
+        /* the mouse x/y values decay exponentially */
+        mousex_residual = (mousex_residual * 224) / 256;
+        mousey_residual = (mousey_residual * 224) / 256;
+    }
+
+#ifdef _DEBUG
+    DebugMessage(M64MSG_VERBOSE, "Controller #%d value: 0x%8.8X", Control, *(int *)&controller[Control].buttons );
+#endif
+    *Keys = controller[Control].buttons;
+
+    /* handle mempack / rumblepak switching (only if rumble is active on joystick) */
+#ifdef __linux__
+    if (controller[Control].event_joystick != 0)
+    {
+        struct input_event play;
+        static unsigned int SwitchPackTime[4] = {0, 0, 0, 0}, SwitchPackType[4] = {0, 0, 0, 0};
+        // when the user switches packs, we should mimick the act of removing 1 pack, and then inserting another 1 second later
+        if (controller[Control].buttons.Value & button_bits[14])
+        {
+            SwitchPackTime[Control] = SDL_GetTicks();         // time at which the 'switch pack' command was given
+            SwitchPackType[Control] = PLUGIN_MEMPAK;          // type of new pack to insert
+            controller[Control].control->Plugin = PLUGIN_NONE;// remove old pack
+            play.type = EV_FF;
+            play.code = ffweak[Control].id;
+            play.value = 1;
+            if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
+                perror("Error starting rumble effect");
+        }
+        if (controller[Control].buttons.Value & button_bits[15])
+        {
+            SwitchPackTime[Control] = SDL_GetTicks();         // time at which the 'switch pack' command was given
+            SwitchPackType[Control] = PLUGIN_RAW;             // type of new pack to insert
+            controller[Control].control->Plugin = PLUGIN_NONE;// remove old pack
+            play.type = EV_FF;
+            play.code = ffstrong[Control].id;
+            play.value = 1;
+            if (write(controller[Control].event_joystick, (const void*) &play, sizeof(play)) == -1)
+                perror("Error starting rumble effect");
+        }
+        // handle inserting new pack if the time has arrived
+        if (SwitchPackTime[Control] != 0 && (SDL_GetTicks() - SwitchPackTime[Control]) >= 1000)
+        {
+            controller[Control].control->Plugin = SwitchPackType[Control];
+            SwitchPackTime[Control] = 0;
+        }
+    }
+#endif /* __linux__ */
+
+    controller[Control].buttons.Value = 0;
+}
+
+static void InitiateRumble(int cntrl)
+{
+#ifdef __linux__
+    DIR* dp;
+    struct dirent* ep;
+    unsigned long features[4];
+    char temp[128];
+    char temp2[128];
+    int iFound = 0;
+
+    controller[cntrl].event_joystick = 0;
+
+    sprintf(temp,"/sys/class/input/js%d/device", controller[cntrl].device);
+    dp = opendir(temp);
+
+    if(dp==NULL)
+        return;
+
+    while ((ep=readdir(dp)))
+        {
+        if (strncmp(ep->d_name, "event",5)==0)
+            {
+            sprintf(temp, "/dev/input/%s", ep->d_name);
+            iFound = 1;
+            break;
+            }
+        else if(strncmp(ep->d_name,"input:event", 11)==0)
+            {
+            sscanf(ep->d_name, "input:%s", temp2);
+            sprintf(temp, "/dev/input/%s", temp2);
+            iFound = 1;
+            break;
+            }
+        else if(strncmp(ep->d_name,"input:input", 11)==0)
+            {
+            strcat(temp, "/");
+            strcat(temp, ep->d_name);
+            closedir (dp);
+            dp = opendir(temp);
+            if(dp==NULL)
+                return;
+            }
+       }
+
+    closedir(dp);
+
+    if (!iFound)
+    {
+        DebugMessage(M64MSG_WARNING, "Couldn't find input event for rumble support.");
+        return;
+    }
+
+    controller[cntrl].event_joystick = open(temp, O_RDWR);
+    if(controller[cntrl].event_joystick==-1)
+        {
+        DebugMessage(M64MSG_WARNING, "Couldn't open device file '%s' for rumble support.", temp);
+        controller[cntrl].event_joystick = 0;
+        return;
+        }
+
+    if(ioctl(controller[cntrl].event_joystick, EVIOCGBIT(EV_FF, sizeof(unsigned long) * 4), features)==-1)
+        {
+        DebugMessage(M64MSG_WARNING, "Linux kernel communication failed for force feedback (rumble).\n");
+        controller[cntrl].event_joystick = 0;
+        return;
+        }
+
+    if(!test_bit(FF_RUMBLE, features))
+        {
+        DebugMessage(M64MSG_WARNING, "No rumble supported on N64 joystick #%i", cntrl + 1);
+        controller[cntrl].event_joystick = 0;
+        return;
+        }
+
+    ffeffect[cntrl].type = FF_RUMBLE;
+    ffeffect[cntrl].id = -1;
+    ffeffect[cntrl].u.rumble.strong_magnitude = 0xFFFF;
+    ffeffect[cntrl].u.rumble.weak_magnitude = 0xFFFF;
+    ffeffect[cntrl].replay.length = 0x7fff;             // hack: xboxdrv is buggy and doesn't support infinite replay.
+                                                        // when xboxdrv is fixed (https://github.com/Grumbel/xboxdrv/issues/47),
+                                                        // please remove this
+
+    ioctl(controller[cntrl].event_joystick, EVIOCSFF, &ffeffect[cntrl]);
+
+    ffstrong[cntrl].type = FF_RUMBLE;
+    ffstrong[cntrl].id = -1;
+    ffstrong[cntrl].u.rumble.strong_magnitude = 0xFFFF;
+    ffstrong[cntrl].u.rumble.weak_magnitude = 0x0000;
+    ffstrong[cntrl].replay.length = 500;
+    ffstrong[cntrl].replay.delay = 0;
+
+    ioctl(controller[cntrl].event_joystick, EVIOCSFF, &ffstrong[cntrl]);
+
+    ffweak[cntrl].type = FF_RUMBLE;
+    ffweak[cntrl].id = -1;
+    ffweak[cntrl].u.rumble.strong_magnitude = 0x0000;
+    ffweak[cntrl].u.rumble.weak_magnitude = 0xFFFF;
+    ffweak[cntrl].replay.length = 500;
+    ffweak[cntrl].replay.delay = 0;
+
+    ioctl(controller[cntrl].event_joystick, EVIOCSFF, &ffweak[cntrl]);
+
+    DebugMessage(M64MSG_INFO, "Rumble activated on N64 joystick #%i", cntrl + 1);
+#endif /* __linux__ */
+}
+
+/******************************************************************
+  Function: InitiateControllers
+  Purpose:  This function initialises how each of the controllers
+            should be handled.
+  input:    - The handle to the main window.
+            - A controller structure that needs to be filled for
+              the emulator to know how to handle each controller.
+  output:   none
+*******************************************************************/
+EXPORT void CALL InitiateControllers(CONTROL_INFO ControlInfo)
+{
+    int i;
+
+    // reset controllers
+    memset( controller, 0, sizeof( SController ) * 4 );
+    for ( i = 0; i < SDL_NUM_SCANCODES; i++)
+    {
+        myKeyState[i] = 0;
+    }
+    // set our CONTROL struct pointers to the array that was passed in to this function from the core
+    // this small struct tells the core whether each controller is plugged in, and what type of pak is connected
+    for (i = 0; i < 4; i++)
+        controller[i].control = ControlInfo.Controls + i;
+
+    // read configuration
+    load_configuration(0);
+
+    for( i = 0; i < 4; i++ )
+    {
+        // test for rumble support for this joystick
+        InitiateRumble(i);
+        // if rumble not supported, switch to mempack
+        if (controller[i].control->Plugin == PLUGIN_RAW && controller[i].event_joystick == 0)
+            controller[i].control->Plugin = PLUGIN_MEMPAK;
+    }
+
+    DebugMessage(M64MSG_INFO, "%s version %i.%i.%i initialized.", PLUGIN_NAME, VERSION_PRINTF_SPLIT(PLUGIN_VERSION));
+}
+
+/******************************************************************
+  Function: ReadController
+  Purpose:  To process the raw data in the pif ram that is about to
+            be read.
+  input:    - Controller Number (0 to 3) and -1 signalling end of
+              processing the pif ram.
+            - Pointer of data to be processed.
+  output:   none
+  note:     This function is only needed if the DLL is allowing raw
+            data.
+*******************************************************************/
+EXPORT void CALL ReadController(int Control, unsigned char *Command)
+{
+#ifdef _DEBUG
+    if (Command != NULL)
+        DebugMessage(M64MSG_INFO, "Raw Read (cont=%d):  %02X %02X %02X %02X %02X %02X", Control,
+                     Command[0], Command[1], Command[2], Command[3], Command[4], Command[5]);
+#endif
+}
+
+/******************************************************************
+  Function: RomClosed
+  Purpose:  This function is called when a rom is closed.
+  input:    none
+  output:   none
+*******************************************************************/
+EXPORT void CALL RomClosed(void)
+{
+    int i;
+
+    // close joysticks
+    for( i = 0; i < 4; i++ )
+        if( controller[i].joystick )
+        {
+            SDL_JoystickClose( controller[i].joystick );
+            controller[i].joystick = NULL;
+        }
+
+    // quit SDL joystick subsystem
+    SDL_QuitSubSystem( SDL_INIT_JOYSTICK );
+
+    // release/ungrab mouse
+#if SDL_VERSION_ATLEAST(2,0,0)
+#warning SDL mouse grabbing not yet supported with SDL 2.0
+#else
+    SDL_WM_GrabInput( SDL_GRAB_OFF );
+#endif
+    SDL_ShowCursor( 1 );
+
+    romopen = 0;
+}
+
+/******************************************************************
+  Function: RomOpen
+  Purpose:  This function is called when a rom is open. (from the
+            emulation thread)
+  input:    none
+  output:   none
+*******************************************************************/
+EXPORT int CALL RomOpen(void)
+{
+    int i;
+
+    // init SDL joystick subsystem
+    if( !SDL_WasInit( SDL_INIT_JOYSTICK ) )
+        if( SDL_InitSubSystem( SDL_INIT_JOYSTICK ) == -1 )
+        {
+            DebugMessage(M64MSG_ERROR, "Couldn't init SDL joystick subsystem: %s", SDL_GetError() );
+            return 0;
+        }
+
+    // open joysticks
+    for( i = 0; i < 4; i++ )
+        if( controller[i].device >= 0 )
+        {
+            controller[i].joystick = SDL_JoystickOpen( controller[i].device );
+            if( controller[i].joystick == NULL )
+                DebugMessage(M64MSG_WARNING, "Couldn't open joystick for controller #%d: %s", i + 1, SDL_GetError() );
+        }
+        else
+            controller[i].joystick = NULL;
+
+    // grab mouse
+    if (controller[0].mouse || controller[1].mouse || controller[2].mouse || controller[3].mouse)
+    {
+#if SDL_VERSION_ATLEAST(2,0,0)
+#warning SDL mouse grabbing not yet supported with SDL 2.0
+#else
+        SDL_ShowCursor( 0 );
+        if (SDL_WM_GrabInput( SDL_GRAB_ON ) != SDL_GRAB_ON)
+        {
+            DebugMessage(M64MSG_WARNING, "Couldn't grab input! Mouse support won't work!");
+        }
+#endif
+    }
+
+    romopen = 1;
+    return 1;
+}
+
+/******************************************************************
+  Function: SDL_KeyDown
+  Purpose:  To pass the SDL_KeyDown message from the emulator to the
+            plugin.
+  input:    keymod and keysym of the SDL_KEYDOWN message.
+  output:   none
+*******************************************************************/
+EXPORT void CALL SDL_KeyDown(int keymod, int keysym)
+{
+    myKeyState[keysym] = 1;
+}
+
+/******************************************************************
+  Function: SDL_KeyUp
+  Purpose:  To pass the SDL_KeyUp message from the emulator to the
+            plugin.
+  input:    keymod and keysym of the SDL_KEYUP message.
+  output:   none
+*******************************************************************/
+EXPORT void CALL SDL_KeyUp(int keymod, int keysym)
+{
+    myKeyState[keysym] = 0;
+}
+