/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2002 Xodnizel * * 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 "main.h" #include "dface.h" #include "input.h" #include "config.h" #include "sdl.h" #include "sdl-video.h" #include "sdl-joystick.h" #include "../common/cheat.h" #include "../../movie.h" #include "../../fceu.h" #include "../../driver.h" #include "../../state.h" #include "../../utils/xstring.h" #ifdef _S9XLUA_H #include "../../fceulua.h" #endif #ifdef _GTK #include "gui.h" #ifdef SDL_VIDEO_DRIVER_X11 #include #endif #endif #include #include /** GLOBALS **/ int NoWaiting = 0; extern Config *g_config; extern bool bindSavestate, frameAdvanceLagSkip, lagCounterDisplay; /* UsrInputType[] is user-specified. CurInputType[] is current (game loading can override user settings) */ static int UsrInputType[NUM_INPUT_DEVICES] = { SI_GAMEPAD, SI_GAMEPAD, SI_NONE }; static int CurInputType[NUM_INPUT_DEVICES] = { SI_GAMEPAD, SI_GAMEPAD, SI_NONE }; static int cspec = 0; static int buttonConfigInProgress = 0; extern int gametype; /** * Necessary for proper GUI functioning (configuring when a game isn't loaded). */ void InputUserActiveFix () { int x; for (x = 0; x < 3; x++) { CurInputType[x] = UsrInputType[x]; } } /** * Parse game information and configure the input devices accordingly. */ void ParseGIInput (FCEUGI * gi) { gametype = gi->type; CurInputType[0] = UsrInputType[0]; CurInputType[1] = UsrInputType[1]; CurInputType[2] = UsrInputType[2]; if (gi->input[0] >= 0) { CurInputType[0] = gi->input[0]; } if (gi->input[1] >= 0) { CurInputType[1] = gi->input[1]; } if (gi->inputfc >= 0) { CurInputType[2] = gi->inputfc; } cspec = gi->cspecial; } static uint8 QuizKingData = 0; static uint8 HyperShotData = 0; static uint32 MahjongData = 0; static uint32 FTrainerData = 0; static uint8 TopRiderData = 0; static uint8 BWorldData[1 + 13 + 1]; static void UpdateFKB (void); static void UpdateGamepad (void); static void UpdateQuizKing (void); static void UpdateHyperShot (void); static void UpdateMahjong (void); static void UpdateFTrainer (void); static void UpdateTopRider (void); static uint32 JSreturn = 0; /** * Configure cheat devices (game genie, etc.). Restarts the keyboard * and video subsystems. */ static void DoCheatSeq () { SilenceSound (1); KillVideo (); DoConsoleCheatConfig (); InitVideo (GameInfo); SilenceSound (0); } #include "keyscan.h" static uint8 g_keyState[SDL_NUM_SCANCODES]; static int DIPS = 0; static uint8 keyonce[SDL_NUM_SCANCODES]; #define KEY(__a) g_keyState[MKK(__a)] int getKeyState( int k ) { if ( (k >= 0) && (k < SDL_NUM_SCANCODES) ) { return g_keyState[k]; } return 0; } static int _keyonly (int a) { int sc; if ( a < 0 ) { return 0; } sc = SDL_GetScancodeFromKey(a); // check for valid key if (sc >= SDL_NUM_SCANCODES || sc < 0) { return 0; } if (g_keyState[sc]) { if (!keyonce[sc]) { keyonce[sc] = 1; return 1; } } else { keyonce[sc] = 0; } return 0; } #define keyonly(__a) _keyonly(MKK(__a)) static int g_fkbEnabled = 0; // this function loads the sdl hotkeys from the config file into the // global scope. this elimates the need for accessing the config file int Hotkeys[HK_MAX] = { 0 }; // on every cycle of keyboardinput() void setHotKeys (void) { std::string prefix = "SDL.Hotkeys."; for (int i = 0; i < HK_MAX; i++) { g_config->getOption (prefix + getHotkeyString(i), &Hotkeys[i]); } return; } /*** * This function is a wrapper for FCEUI_ToggleEmulationPause that handles * releasing/capturing mouse pointer during pause toggles * */ void TogglePause () { FCEUI_ToggleEmulationPause (); int no_cursor; g_config->getOption("SDL.NoFullscreenCursor", &no_cursor); int fullscreen; g_config->getOption ("SDL.Fullscreen", &fullscreen); return; } /*** * This function opens a file chooser dialog and returns the filename the * user selected. * */ std::string GetFilename (const char *title, bool save, const char *filter) { if (FCEUI_EmulationPaused () == 0) FCEUI_ToggleEmulationPause (); std::string fname = ""; #ifdef WIN32 OPENFILENAME ofn; // common dialog box structure char szFile[260]; // buffer for file name HWND hwnd; // owner window HANDLE hf; // file handle // Initialize OPENFILENAME memset (&ofn, 0, sizeof (ofn)); ofn.lStructSize = sizeof (ofn); ofn.hwndOwner = hwnd; ofn.lpstrFile = szFile; // Set lpstrFile[0] to '\0' so that GetOpenFileName does not // use the contents of szFile to initialize itself. ofn.lpstrFile[0] = '\0'; ofn.nMaxFile = sizeof (szFile); ofn.lpstrFilter = "All\0*.*\0"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; // Display the Open dialog box. fname = GetOpenFileName (&ofn); #endif #ifdef _GTK int fullscreen = 0; g_config->getOption ("SDL.Fullscreen", &fullscreen); if (fullscreen) ToggleFS (); GtkWidget *fileChooser; GtkFileFilter *filterX; GtkFileFilter *filterAll; filterX = gtk_file_filter_new (); gtk_file_filter_add_pattern (filterX, filter); gtk_file_filter_set_name (filterX, filter); filterAll = gtk_file_filter_new (); gtk_file_filter_add_pattern (filterAll, "*"); gtk_file_filter_set_name (filterAll, "All Files"); if (save) fileChooser = gtk_file_chooser_dialog_new ("Save as", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, "_Cancel", GTK_RESPONSE_CANCEL, "_Save", GTK_RESPONSE_ACCEPT, NULL); else fileChooser = gtk_file_chooser_dialog_new ("Open", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, "_Cancel", GTK_RESPONSE_CANCEL, "_Open", GTK_RESPONSE_ACCEPT, NULL); // TODO: make file filters case insensitive //gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fileChooser), filterX); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fileChooser), filterAll); int response = gtk_dialog_run (GTK_DIALOG (fileChooser)); // flush gtk events while (gtk_events_pending ()) gtk_main_iteration_do (TRUE); if (response == GTK_RESPONSE_ACCEPT) fname = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fileChooser)); gtk_widget_destroy (fileChooser); while (gtk_events_pending ()) gtk_main_iteration_do (TRUE); #endif FCEUI_ToggleEmulationPause (); return fname; } /** * This function opens a text entry dialog and returns the user's input */ std::string GetUserText (const char *title) { #ifdef _GTK /* prg318 - 10/13/11 - this is broken in recent build and causes * segfaults/very weird behavior i'd rather remove it for now than it cause * accidental segfaults * TODO fix it */ #if 0 GtkWidget* d; GtkWidget* entry; d = gtk_dialog_new_with_buttons(title, NULL, GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_OK); entry = gtk_entry_new(); GtkWidget* vbox = gtk_dialog_get_content_area(GTK_DIALOG(d)); gtk_container_add(GTK_CONTAINER(vbox), entry); gtk_widget_show_all(d); gtk_dialog_run(GTK_DIALOG(d)); // flush gtk events while(gtk_events_pending()) gtk_main_iteration_do(TRUE); std::string input = gtk_entry_get_text(GTK_ENTRY(entry)); if (FCEUI_EmulationPaused() == 0) FCEUI_ToggleEmulationPause(); // pause emulation int fullscreen = 0; g_config->getOption("SDL.Fullscreen", &fullscreen); if(fullscreen) ToggleFS(); // disable fullscreen emulation FILE *fpipe; std::string command = "zenity --entry --title=\""; command.append(title); command.append("\" --text=\""); command.append(title); command.append(":\""); if (!(fpipe = (FILE*)popen(command.c_str(),"r"))) // If fpipe is NULL FCEUD_PrintError("Pipe error on opening zenity"); int c; std::string input; while((c = fgetc(fpipe))) { if (c == EOF || c == '\n') break; input += c; } pclose(fpipe); gtk_widget_destroy(d); while(gtk_events_pending()) gtk_main_iteration_do(TRUE); FCEUI_ToggleEmulationPause(); // unpause emulation return input; #endif // #if 0 #endif return ""; } /** * Lets the user start a new .fm2 movie file **/ void FCEUD_MovieRecordTo () { std::string fname = GetFilename ("Save FM2 movie for recording", true, "FM2 movies|*.fm2"); if (!fname.size ()) return; // no filename selected, quit the whole thing std::wstring author = mbstowcs (GetUserText ("Author name")); // the author can be empty, so no need to check here FCEUI_SaveMovie (fname.c_str (), MOVIE_FLAG_FROM_POWERON, author); } /** * Lets the user save a savestate to a specific file **/ void FCEUD_SaveStateAs () { std::string fname = GetFilename ("Save savestate as...", true, "Savestates|*.fc0"); if (!fname.size ()) return; // no filename selected, quit the whole thing FCEUI_SaveState (fname.c_str ()); } /** * Lets the user load a savestate from a specific file */ void FCEUD_LoadStateFrom () { std::string fname = GetFilename ("Load savestate from...", false, "Savestates|*.fc?"); if (!fname.size ()) return; // no filename selected, quit the whole thing FCEUI_LoadState (fname.c_str ()); } /** * Hook for transformer board */ unsigned int *GetKeyboard(void) { int size = 256; Uint8* keystate = (Uint8*)SDL_GetKeyboardState(&size); return (unsigned int*)(keystate); } /** * Parse keyboard commands and execute accordingly. */ static void KeyboardCommands (void) { int is_shift, is_alt; // get the keyboard input // check if the family keyboard is enabled if (CurInputType[2] == SIFC_FKB) { if ( g_keyState[SDL_SCANCODE_SCROLLLOCK] ) { g_fkbEnabled ^= 1; FCEUI_DispMessage ("Family Keyboard %sabled.", 0, g_fkbEnabled ? "en" : "dis"); } if (g_fkbEnabled) { return; } } if (g_keyState[SDL_SCANCODE_LSHIFT] || g_keyState[SDL_SCANCODE_RSHIFT]) { is_shift = 1; } else { is_shift = 0; } if (g_keyState[SDL_SCANCODE_LALT] || g_keyState[SDL_SCANCODE_RALT]) { is_alt = 1; } else { is_alt = 0; } if (_keyonly (Hotkeys[HK_TOGGLE_BG])) { if (is_shift) { FCEUI_SetRenderPlanes (true, false); } else { FCEUI_SetRenderPlanes (true, true); } } // Alt-Enter to toggle full-screen // This is already handled by GTK Accelerator //if (keyonly (ENTER) && is_alt) //{ // ToggleFS (); //} // // Alt-M to toggle Main Menu Visibility if ( is_alt ) { if (keyonly (M)) { toggleMenuVis(); } } // Toggle Movie auto-backup if ( is_shift ) { if (keyonly (M)) { autoMovieBackup ^= 1; FCEUI_DispMessage ("Automatic movie backup %sabled.", 0, autoMovieBackup ? "en" : "dis"); } } if ( is_alt ) { // Start recording an FM2 movie on Alt+R if (keyonly (R)) { FCEUD_MovieRecordTo (); } // Save a state from a file if (keyonly (S)) { FCEUD_SaveStateAs (); } // Load a state from a file if (keyonly (L)) { FCEUD_LoadStateFrom (); } } // Famicom disk-system games if (gametype == GIT_FDS) { if (_keyonly (Hotkeys[HK_FDS_SELECT])) { FCEUI_FDSSelect (); } if (_keyonly (Hotkeys[HK_FDS_EJECT])) { FCEUI_FDSInsert (); } } if (_keyonly (Hotkeys[HK_SCREENSHOT])) { FCEUI_SaveSnapshot (); } // if not NES Sound Format if (gametype != GIT_NSF) { if (_keyonly (Hotkeys[HK_CHEAT_MENU])) { DoCheatSeq (); } // f5 (default) save key, hold shift to save movie if (_keyonly (Hotkeys[HK_SAVE_STATE])) { if (is_shift) { std::string movie_fname = FCEU_MakeFName (FCEUMKF_MOVIE, 0, 0); FCEUI_printf ("Recording movie to %s\n", movie_fname.c_str() ); FCEUI_SaveMovie(movie_fname.c_str() , MOVIE_FLAG_NONE, L""); } else { FCEUI_SaveState (NULL); } } // f7 to load state, Shift-f7 to load movie if (_keyonly (Hotkeys[HK_LOAD_STATE])) { if (is_shift) { FCEUI_StopMovie (); std::string fname; fname = GetFilename ("Open FM2 movie for playback...", false, "FM2 movies|*.fm2"); if (fname != "") { if (fname.find (".fm2") != std::string::npos || fname.find (".fm3") != std::string::npos) { FCEUI_printf ("Playing back movie located at %s\n", fname.c_str ()); FCEUI_LoadMovie (fname.c_str (), false, false); } else { FCEUI_printf ("Only .fm2 and .fm3 movies are supported.\n"); } } } else { FCEUI_LoadState(NULL); } } } if (_keyonly (Hotkeys[HK_DECREASE_SPEED])) { DecreaseEmulationSpeed(); } if (_keyonly(Hotkeys[HK_INCREASE_SPEED])) { IncreaseEmulationSpeed (); } if (_keyonly (Hotkeys[HK_TOGGLE_FRAME_DISPLAY])) { FCEUI_MovieToggleFrameDisplay (); } if (_keyonly (Hotkeys[HK_TOGGLE_INPUT_DISPLAY])) { FCEUI_ToggleInputDisplay (); extern int input_display; g_config->setOption ("SDL.InputDisplay", input_display); } if (_keyonly (Hotkeys[HK_MOVIE_TOGGLE_RW])) { FCEUI_SetMovieToggleReadOnly (!FCEUI_GetMovieToggleReadOnly ()); } #ifdef CREATE_AVI if (_keyonly (Hotkeys[HK_MUTE_CAPTURE])) { extern int mutecapture; mutecapture ^= 1; } #endif if (_keyonly (Hotkeys[HK_PAUSE])) { //FCEUI_ToggleEmulationPause(); // use the wrapper function instead of the fceui function directly // so we can handle cursor grabbage TogglePause (); } // Toggle throttling if ( _keyonly(Hotkeys[HK_TURBO]) ) { NoWaiting ^= 1; //printf("NoWaiting: 0x%04x\n", NoWaiting ); } static bool frameAdvancing = false; if ( _keyonly(Hotkeys[HK_FRAME_ADVANCE])) { if (frameAdvancing == false) { FCEUI_FrameAdvance (); frameAdvancing = true; } } else { if (frameAdvancing) { FCEUI_FrameAdvanceEnd (); frameAdvancing = false; } } if (_keyonly (Hotkeys[HK_RESET])) { FCEUI_ResetNES (); } //if(_keyonly(Hotkeys[HK_POWER])) { // FCEUI_PowerNES(); //} if (_keyonly (Hotkeys[HK_QUIT])) { CloseGame(); FCEUI_Kill(); SDL_Quit(); exit(0); } else #ifdef _S9XLUA_H if (_keyonly (Hotkeys[HK_LOAD_LUA])) { std::string fname; fname = GetFilename ("Open LUA script...", false, "Lua scripts|*.lua"); if (fname != "") FCEU_LoadLuaCode (fname.c_str ()); } #endif for (int i = 0; i < 10; i++) { if (_keyonly (Hotkeys[HK_SELECT_STATE_0 + i])) { #ifdef _GTK setStateMenuItem(i); #endif FCEUI_SelectState (i, 1); } } if (_keyonly (Hotkeys[HK_SELECT_STATE_NEXT])) { FCEUI_SelectStateNext (1); #ifdef _GTK setStateMenuItem( CurrentState ); #endif } if (_keyonly (Hotkeys[HK_SELECT_STATE_PREV])) { FCEUI_SelectStateNext (-1); #ifdef _GTK setStateMenuItem( CurrentState ); #endif } if (_keyonly (Hotkeys[HK_BIND_STATE])) { bindSavestate ^= 1; FCEUI_DispMessage ("Savestate binding to movie %sabled.", 0, bindSavestate ? "en" : "dis"); } if (_keyonly (Hotkeys[HK_FA_LAG_SKIP])) { frameAdvanceLagSkip ^= 1; FCEUI_DispMessage ("Skipping lag in Frame Advance %sabled.", 0, frameAdvanceLagSkip ? "en" : "dis"); } if (_keyonly (Hotkeys[HK_LAG_COUNTER_DISPLAY])) { lagCounterDisplay ^= 1; } if (_keyonly (Hotkeys[HK_TOGGLE_SUBTITLE])) { movieSubtitles = !movieSubtitles; FCEUI_DispMessage ("Movie subtitles o%s.", 0, movieSubtitles ? "n" : "ff"); } if (_keyonly (Hotkeys[HK_VOLUME_DOWN])) { FCEUD_SoundVolumeAdjust(-1); } if (_keyonly (Hotkeys[HK_VOLUME_UP])) { FCEUD_SoundVolumeAdjust(1); } // VS Unisystem games if (gametype == GIT_VSUNI) { // insert coin if (_keyonly (Hotkeys[HK_VS_INSERT_COIN])) FCEUI_VSUniCoin (); // toggle dipswitch display if (_keyonly (Hotkeys[HK_VS_TOGGLE_DIPSWITCH])) { DIPS ^= 1; FCEUI_VSUniToggleDIPView (); } if (!(DIPS & 1)) goto DIPSless; // toggle the various dipswitches for(int i=1; i<=8;i++) { if(keyonly(i)) FCEUI_VSUniToggleDIP(i-1); } } else { static uint8 bbuf[32]; static int bbuft; static int barcoder = 0; if (keyonly (H)) FCEUI_NTSCSELHUE (); if (keyonly (T)) FCEUI_NTSCSELTINT (); if (_keyonly (Hotkeys[HK_DECREASE_SPEED])) FCEUI_NTSCDEC (); if (_keyonly (Hotkeys[HK_INCREASE_SPEED])) FCEUI_NTSCINC (); if ((CurInputType[2] == SIFC_BWORLD) || (cspec == SIS_DATACH)) { if (keyonly (F8)) { barcoder ^= 1; if (!barcoder) { if (CurInputType[2] == SIFC_BWORLD) { strcpy ((char *) &BWorldData[1], (char *) bbuf); BWorldData[0] = 1; } else { FCEUI_DatachSet (bbuf); } FCEUI_DispMessage ("Barcode Entered", 0); } else { bbuft = 0; FCEUI_DispMessage ("Enter Barcode", 0); } } } else { barcoder = 0; } #define SSM(x) \ do { \ if(barcoder) { \ if(bbuft < 13) { \ bbuf[bbuft++] = '0' + x; \ bbuf[bbuft] = 0; \ } \ FCEUI_DispMessage("Barcode: %s",0, bbuf); \ } \ } while(0) DIPSless: for(int i=0; i<10;i++) { if (keyonly (i)) SSM (i); } #undef SSM } } /** * Return the state of the mouse buttons. Input 'd' is an array of 3 * integers that store . */ void // removed static for a call in lua-engine.cpp GetMouseData (uint32 (&d)[3]) { int x, y; uint32 t; // retrieve the state of the mouse from SDL t = SDL_GetMouseState (&x, &y); #ifdef _GTK if (noGui == 0) { // don't ask for gtk mouse info when in fullscreen // we can use sdl directly in fullscreen int fullscreen = 0; g_config->getOption ("SDL.Fullscreen", &fullscreen); if (fullscreen == 0) { x = GtkMouseData[0]; y = GtkMouseData[1]; t = GtkMouseData[2]; } } #endif d[2] = 0; if (t & SDL_BUTTON (1)) { d[2] |= 0x1; } if (t & SDL_BUTTON (3)) { d[2] |= 0x2; } // get the mouse position from the SDL video driver t = PtoV (x, y); d[0] = t & 0xFFFF; d[1] = (t >> 16) & 0xFFFF; // debug print // printf("mouse %d %d %d\n", d[0], d[1], d[2]); } void GetMouseRelative (int32 (&d)[3]) { // converts absolute mouse positions to relative ones for input devices that require this // The windows version additionally in fullscreen will constantly return the mouse to center screen // after reading it, so that the user can endlessly keep moving the mouse. // The same should eventually be implemented here, but this version should minimally provide // the necessary relative input, piggybacking on the already implemented GetMouseData. static int cx = -1; static int cy = -1; uint32 md[3]; GetMouseData (md); if (cx < 0 || cy < 0) { cx = md[0]; cy = md[1]; } int dx = md[0] - cx; int dy = md[1] - cy; d[0] = dx; d[1] = dy; d[2] = md[2]; // buttons } //static void checkKeyBoardState( int scanCode ) //{ // printf("Key State is: %i \n", g_keyState[ scanCode ] ); //} /** * Handles outstanding SDL events. */ static void UpdatePhysicalInput () { SDL_Event event; //SDL_JoystickUpdate(); // loop, handling all pending events while (SDL_PollEvent (&event)) { switch (event.type) { case SDL_QUIT: CloseGame (); puts ("Quit"); break; case SDL_FCEU_HOTKEY_EVENT: switch (event.user.code) { case HK_PAUSE: TogglePause (); break; default: FCEU_printf ("Warning: unknown hotkey event %d\n", event.user.code); } break; case SDL_KEYDOWN: case SDL_KEYUP: //printf("SDL_Event.type: %i Keysym: %i ScanCode: %i\n", // event.type, event.key.keysym.sym, event.key.keysym.scancode ); g_keyState[ event.key.keysym.scancode ] = (event.type == SDL_KEYDOWN) ? 1 : 0; //checkKeyBoardState( event.key.keysym.scancode ); break; case SDL_JOYDEVICEADDED: AddJoystick( event.jdevice.which ); break; case SDL_JOYDEVICEREMOVED: RemoveJoystick( event.jdevice.which ); break; default: break; } } //SDL_PumpEvents(); } /** * Begin configuring the buttons by placing the video and joystick * subsystems into a well-known state. Button configuration really * needs to be cleaned up after the new config system is in place. */ int ButtonConfigBegin () { // initialize the joystick subsystem (if not already inited) InitJoysticks (); buttonConfigInProgress = 1; return 1; } /** * Finish configuring the buttons by reverting the video and joystick * subsystems to their previous state. Button configuration really * needs to be cleaned up after the new config system is in place. */ void ButtonConfigEnd () { buttonConfigInProgress = 0; } /** * Tests to see if a specified button is currently pressed. */ static int DTestButton (ButtConfig * bc) { if (bc->ButtType == BUTTC_KEYBOARD) { if (g_keyState[SDL_GetScancodeFromKey (bc->ButtonNum)]) { bc->state = 1; return 1; } else { bc->state = 0; } } else if (bc->ButtType == BUTTC_JOYSTICK) { if (DTestButtonJoy (bc)) { return 1; } } return 0; } #define MK(x) {BUTTC_KEYBOARD,0,MKK(x),0} //#define MK2(x1,x2) {BUTTC_KEYBOARD,0,MKK(x1)} #define MKZ() {0,0,-1,0} #define GPZ() {MKZ(), MKZ(), MKZ(), MKZ()} //ButtConfig GamePadConfig[ GAMEPAD_NUM_DEVICES ][ GAMEPAD_NUM_BUTTONS ] = //{ ///* Gamepad 1 */ // {MK (KP_3), MK (KP_2), MK (SLASH), MK (ENTER), // MK (w), MK (z), MK (a), MK (s), MKZ (), MKZ ()}, // // /* Gamepad 2 */ // GPZ (), // // /* Gamepad 3 */ // GPZ (), // // /* Gamepad 4 */ // GPZ () //}; /** * Update the status of the gamepad input devices. */ static void UpdateGamepad(void) { // don't update during movie playback if (FCEUMOV_Mode (MOVIEMODE_PLAY)) { return; } static int rapid = 0; uint32 JS = 0; int x; int wg; rapid ^= 1; int opposite_dirs; g_config->getOption("SDL.Input.EnableOppositeDirectionals", &opposite_dirs); // go through each of the four game pads for (wg = 0; wg < 4; wg++) { bool left = false; bool up = false; // a, b, select, start, up, down, left, right for (x = 0; x < 8; x++) { if (DTestButton (&GamePad[wg].bmap[x])) { //printf("GamePad%i Button Hit: %i \n", wg, x ); if(opposite_dirs == 0) { // test for left+right and up+down if(x == 4){ up = true; } if((x == 5) && (up == true)){ continue; } if(x == 6){ left = true; } if((x == 7) && (left == true)){ continue; } } JS |= (1 << x) << (wg << 3); } } int four_button_exit; g_config->getOption("SDL.ABStartSelectExit", &four_button_exit); // if a+b+start+select is pressed, exit if (four_button_exit && JS == 15) { FCEUI_printf("all buttons pressed, exiting\n"); CloseGame(); FCEUI_Kill(); exit(0); } // rapid-fire a, rapid-fire b if (rapid) { for (x = 0; x < 2; x++) { if (DTestButton (&GamePad[wg].bmap[8 + x])) { JS |= (1 << x) << (wg << 3); } } } } // for(x=0;x<32;x+=8) /* Now, test to see if anything weird(up+down at same time) // is happening, and correct */ // { // if((JS & (0xC0<ButtonNum == -1) { return name; } switch (bc->ButtType) { case BUTTC_KEYBOARD: return SDL_GetKeyName (bc->ButtonNum); break; case BUTTC_JOYSTICK: { int joyNum, inputNum; const char *inputType, *inputDirection; joyNum = bc->DeviceNum; if (bc->ButtonNum & 0x8000) { inputType = "Axis"; inputNum = bc->ButtonNum & 0x3FFF; inputDirection = bc->ButtonNum & 0x4000 ? "-" : "+"; } else if (bc->ButtonNum & 0x2000) { int inputValue; char direction[128] = ""; inputType = "Hat"; inputNum = (bc->ButtonNum >> 8) & 0x1F; inputValue = bc->ButtonNum & 0xF; if (inputValue & SDL_HAT_UP) strncat (direction, "Up ", sizeof (direction)-1); if (inputValue & SDL_HAT_DOWN) strncat (direction, "Down ", sizeof (direction)-1); if (inputValue & SDL_HAT_LEFT) strncat (direction, "Left ", sizeof (direction)-1); if (inputValue & SDL_HAT_RIGHT) strncat (direction, "Right ", sizeof (direction)-1); if (direction[0]) inputDirection = direction; else inputDirection = "Center"; } else { inputType = "Button"; inputNum = bc->ButtonNum; inputDirection = ""; } sprintf( name, "js%i:%s%i%s", joyNum, inputType, inputNum, inputDirection ); } break; } return name; } /** * Waits for a button input and returns the information as to which * button was pressed. Used in button configuration. */ int DWaitButton (const uint8_t * text, ButtConfig * bc, int *buttonConfigStatus ) { SDL_Event event; static int32 LastAx[64][64]; int x, y; int timeout_ms = 10000; if (text) { std::string title = "Press a key for "; title += (const char *) text; // TODO - SDL2 //SDL_WM_SetCaption (title.c_str (), 0); puts ((const char *) text); } for (x = 0; x < 64; x++) { for (y = 0; y < 64; y++) { LastAx[x][y] = 0x100000; } } // Purge all pending events, so that this next button press // will be the one we want. while (SDL_PollEvent (&event)) { } while (1) { int done = 0; usleep(10000); timeout_ms -= 10; if ( timeout_ms <= 0 ) { break; } #ifdef _GTK while (gtk_events_pending ()) gtk_main_iteration_do (FALSE); #endif while (SDL_PollEvent (&event)) { printf("Event Type: %i \n", event.type ); done++; switch (event.type) { case SDL_KEYDOWN: //printf("SDL KeyDown:%i \n", event.key.keysym.sym ); bc->ButtType = BUTTC_KEYBOARD; bc->DeviceNum = 0; bc->ButtonNum = event.key.keysym.sym; return (1); case SDL_JOYBUTTONDOWN: bc->ButtType = BUTTC_JOYSTICK; bc->DeviceNum = event.jbutton.which; bc->ButtonNum = event.jbutton.button; return (1); case SDL_JOYHATMOTION: if (event.jhat.value == SDL_HAT_CENTERED) done--; else { bc->ButtType = BUTTC_JOYSTICK; bc->DeviceNum = event.jhat.which; bc->ButtonNum = (0x2000 | ((event.jhat.hat & 0x1F) << 8) | event. jhat.value); return (1); } break; case SDL_JOYAXISMOTION: if (LastAx[event.jaxis.which][event.jaxis.axis] == 0x100000) { if (abs (event.jaxis.value) < 1000) { LastAx[event.jaxis.which][event.jaxis.axis] = event.jaxis.value; } done--; } else { if (abs (LastAx[event.jaxis.which][event.jaxis.axis] - event.jaxis.value) >= 8192) { bc->ButtType = BUTTC_JOYSTICK; bc->DeviceNum = event.jaxis.which; bc->ButtonNum = (0x8000 | event.jaxis.axis | ((event.jaxis.value < 0) ? 0x4000 : 0)); return (1); } else done--; } break; default: done--; } } if (done) break; // If the button config window is Closed, // get out of loop. if ( buttonConfigStatus != NULL ) { if ( *buttonConfigStatus == 0 ) { break; } } } return (0); } /** * This function takes in button inputs until either it sees two of * the same button presses in a row or gets four inputs and then saves * the total number of button presses. Each of the keys pressed is * used as input for the specified button, thus allowing up to four * possible settings for each input button. */ // void //ConfigButton (char *text, ButtConfig * bc) //{ // uint8 buf[256]; // int wc; // // for (wc = 0; wc < MAXBUTTCONFIG; wc++) // { // sprintf ((char *) buf, "%s (%d)", text, wc + 1); // DWaitButton (buf, bc, wc, NULL); // // if (wc && // bc->ButtType[wc] == bc->ButtType[wc - 1] && // bc->DeviceNum[wc] == bc->DeviceNum[wc - 1] && // bc->ButtonNum[wc] == bc->ButtonNum[wc - 1]) // { // break; // } // } //} /** * Update the button configuration for a specified device. */ extern Config *g_config; //void ConfigDevice (int which, int arg) //{ // char buf[256]; // int x; // std::string prefix; // const char *str[10] = // { "A", "B", "SELECT", "START", "UP", "DOWN", "LEFT", "RIGHT", "Rapid A", // "Rapid B" // }; // // // XXX soules - set the configuration options so that later calls // // don't override these. This is a temp hack until I // // can clean up this file. // // ButtonConfigBegin (); // switch (which) // { // case FCFGD_QUIZKING: // prefix = "SDL.Input.QuizKing."; // for (x = 0; x < 6; x++) // { // sprintf (buf, "Quiz King Buzzer #%d", x + 1); // ConfigButton (buf, &QuizKingButtons[x]); // // g_config->setOption (prefix + QuizKingNames[x], // QuizKingButtons[x].ButtonNum); // } // // if (QuizKingButtons[0].ButtType == BUTTC_KEYBOARD) // { // g_config->setOption (prefix + "DeviceType", "Keyboard"); // } // else if (QuizKingButtons[0].ButtType == BUTTC_JOYSTICK) // { // g_config->setOption (prefix + "DeviceType", "Joystick"); // } // else // { // g_config->setOption (prefix + "DeviceType", "Unknown"); // } // g_config->setOption (prefix + "DeviceNum", // QuizKingButtons[0].DeviceNum); // break; // case FCFGD_HYPERSHOT: // prefix = "SDL.Input.HyperShot."; // for (x = 0; x < 4; x++) // { // sprintf (buf, "Hyper Shot %d: %s", // ((x & 2) >> 1) + 1, (x & 1) ? "JUMP" : "RUN"); // ConfigButton (buf, &HyperShotButtons[x]); // // g_config->setOption (prefix + HyperShotNames[x], // HyperShotButtons[x].ButtonNum); // } // // if (HyperShotButtons[0].ButtType == BUTTC_KEYBOARD) // { // g_config->setOption (prefix + "DeviceType", "Keyboard"); // } // else if (HyperShotButtons[0].ButtType == BUTTC_JOYSTICK) // { // g_config->setOption (prefix + "DeviceType", "Joystick"); // } // else // { // g_config->setOption (prefix + "DeviceType", "Unknown"); // } // g_config->setOption (prefix + "DeviceNum", // HyperShotButtons[0].DeviceNum); // break; // case FCFGD_POWERPAD: // snprintf (buf, 256, "SDL.Input.PowerPad.%d", (arg & 1)); // prefix = buf; // for (x = 0; x < 12; x++) // { // sprintf (buf, "PowerPad %d: %d", (arg & 1) + 1, x + 11); // ConfigButton (buf, &powerpadsc[arg & 1][x]); // // g_config->setOption (prefix + PowerPadNames[x], // powerpadsc[arg & 1][x].ButtonNum); // } // // if (powerpadsc[arg & 1][0].ButtType == BUTTC_KEYBOARD) // { // g_config->setOption (prefix + "DeviceType", "Keyboard"); // } // else if (powerpadsc[arg & 1][0].ButtType == BUTTC_JOYSTICK) // { // g_config->setOption (prefix + "DeviceType", "Joystick"); // } // else // { // g_config->setOption (prefix + "DeviceType", "Unknown"); // } // g_config->setOption (prefix + "DeviceNum", // powerpadsc[arg & 1][0].DeviceNum); // break; // // case FCFGD_GAMEPAD: // snprintf (buf, 256, "SDL.Input.GamePad.%d", arg); // prefix = buf; // for (x = 0; x < 10; x++) // { // sprintf (buf, "GamePad #%d: %s", arg + 1, str[x]); // ConfigButton (buf, &GamePadConfig[arg][x]); // // g_config->setOption (prefix + GamePadNames[x], // GamePadConfig[arg][x].ButtonNum); // } // // if (GamePadConfig[arg][0].ButtType == BUTTC_KEYBOARD) // { // g_config->setOption (prefix + "DeviceType", "Keyboard"); // } // else if (GamePadConfig[arg][0].ButtType == BUTTC_JOYSTICK) // { // g_config->setOption (prefix + "DeviceType", "Joystick"); // } // else // { // g_config->setOption (prefix + "DeviceType", "Unknown"); // } // g_config->setOption (prefix + "DeviceNum", // GamePadConfig[arg][0].DeviceNum); // break; // } // // ButtonConfigEnd (); //} /** * Update the button configuration for a device, specified by a text string. */ //void InputCfg (const std::string & text) //{ // // if (noGui) // { // if (text.find ("gamepad") != std::string::npos) // { // int device = (text[strlen ("gamepad")] - '1'); // if (device < 0 || device > 3) // { // FCEUD_PrintError // ("Invalid gamepad device specified; must be one of gamepad1 through gamepad4"); // exit (-1); // } // ConfigDevice (FCFGD_GAMEPAD, device); // } // else if (text.find ("powerpad") != std::string::npos) // { // int device = (text[strlen ("powerpad")] - '1'); // if (device < 0 || device > 1) // { // FCEUD_PrintError // ("Invalid powerpad device specified; must be powerpad1 or powerpad2"); // exit (-1); // } // ConfigDevice (FCFGD_POWERPAD, device); // } // else if (text.find ("hypershot") != std::string::npos) // { // ConfigDevice (FCFGD_HYPERSHOT, 0); // } // else if (text.find ("quizking") != std::string::npos) // { // ConfigDevice (FCFGD_QUIZKING, 0); // } // } // else // printf ("Please run \"fceux --nogui\" before using --inputcfg\n"); // //} /** * Hack to map the new configuration onto the existing button * configuration management. Will probably want to change this in the * future - soules. */ void UpdateInput (Config * config) { char buf[64]; std::string device, prefix, guid, mapping; InitJoysticks(); for (unsigned int i = 0; i < 3; i++) { snprintf (buf, 64, "SDL.Input.%u", i); config->getOption (buf, &device); if (device == "None") { UsrInputType[i] = SI_NONE; } else if (device.find ("GamePad") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_GAMEPAD : (int) SIFC_NONE; } else if (device.find ("PowerPad.0") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_POWERPADA : (int) SIFC_NONE; } else if (device.find ("PowerPad.1") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_POWERPADB : (int) SIFC_NONE; } else if (device.find ("QuizKing") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_QUIZKING; } else if (device.find ("HyperShot") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_HYPERSHOT; } else if (device.find ("Mahjong") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_MAHJONG; } else if (device.find ("TopRider") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_TOPRIDER; } else if (device.find ("FTrainer") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_FTRAINERA; } else if (device.find ("FamilyKeyBoard") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_FKB; } else if (device.find ("OekaKids") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_OEKAKIDS; } else if (device.find ("Arkanoid") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_ARKANOID : (int) SIFC_ARKANOID; } else if (device.find ("Shadow") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_SHADOW; } else if (device.find ("Zapper") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_ZAPPER : (int) SIFC_NONE; } else if (device.find ("BWorld") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_BWORLD; } else if (device.find ("4Player") != std::string::npos) { UsrInputType[i] = (i < 2) ? (int) SI_NONE : (int) SIFC_4PLAYER; } else { // Unknown device UsrInputType[i] = SI_NONE; } } // update each of the devices' configuration structure // XXX soules - this is temporary until this file is cleaned up to // simplify the interface between configuration and // structure data. This will likely include the // removal of multiple input buttons for a single // input device key. int type, devnum, button; // gamepad 0 - 3 for (unsigned int i = 0; i < GAMEPAD_NUM_DEVICES; i++) { char buf[64]; snprintf (buf, sizeof(buf)-1, "SDL.Input.GamePad.%u.", i); prefix = buf; config->getOption (prefix + "DeviceType", &device ); config->getOption (prefix + "DeviceGUID", &guid ); config->getOption (prefix + "Profile" , &mapping); GamePad[i].init( i, guid.c_str(), mapping.c_str() ); } // PowerPad 0 - 1 for (unsigned int i = 0; i < POWERPAD_NUM_DEVICES; i++) { char buf[64]; snprintf (buf, sizeof(buf)-1, "SDL.Input.PowerPad.%u.", i); prefix = buf; config->getOption (prefix + "DeviceType", &device); if (device.find ("Keyboard") != std::string::npos) { type = BUTTC_KEYBOARD; } else if (device.find ("Joystick") != std::string::npos) { type = BUTTC_JOYSTICK; } else { type = 0; } config->getOption (prefix + "DeviceNum", &devnum); for (unsigned int j = 0; j < POWERPAD_NUM_BUTTONS; j++) { config->getOption (prefix + PowerPadNames[j], &button); powerpadsc[i][j].ButtType = type; powerpadsc[i][j].DeviceNum = devnum; powerpadsc[i][j].ButtonNum = button; } } // QuizKing prefix = "SDL.Input.QuizKing."; config->getOption (prefix + "DeviceType", &device); if (device.find ("Keyboard") != std::string::npos) { type = BUTTC_KEYBOARD; } else if (device.find ("Joystick") != std::string::npos) { type = BUTTC_JOYSTICK; } else { type = 0; } config->getOption (prefix + "DeviceNum", &devnum); for (unsigned int j = 0; j < QUIZKING_NUM_BUTTONS; j++) { config->getOption (prefix + QuizKingNames[j], &button); QuizKingButtons[j].ButtType = type; QuizKingButtons[j].DeviceNum = devnum; QuizKingButtons[j].ButtonNum = button; } // HyperShot prefix = "SDL.Input.HyperShot."; config->getOption (prefix + "DeviceType", &device); if (device.find ("Keyboard") != std::string::npos) { type = BUTTC_KEYBOARD; } else if (device.find ("Joystick") != std::string::npos) { type = BUTTC_JOYSTICK; } else { type = 0; } config->getOption (prefix + "DeviceNum", &devnum); for (unsigned int j = 0; j < HYPERSHOT_NUM_BUTTONS; j++) { config->getOption (prefix + HyperShotNames[j], &button); HyperShotButtons[j].ButtType = type; HyperShotButtons[j].DeviceNum = devnum; HyperShotButtons[j].ButtonNum = button; } // Mahjong prefix = "SDL.Input.Mahjong."; config->getOption (prefix + "DeviceType", &device); if (device.find ("Keyboard") != std::string::npos) { type = BUTTC_KEYBOARD; } else if (device.find ("Joystick") != std::string::npos) { type = BUTTC_JOYSTICK; } else { type = 0; } config->getOption (prefix + "DeviceNum", &devnum); for (unsigned int j = 0; j < MAHJONG_NUM_BUTTONS; j++) { config->getOption (prefix + MahjongNames[j], &button); MahjongButtons[j].ButtType = type; MahjongButtons[j].DeviceNum = devnum; MahjongButtons[j].ButtonNum = button; } // TopRider prefix = "SDL.Input.TopRider."; config->getOption (prefix + "DeviceType", &device); if (device.find ("Keyboard") != std::string::npos) { type = BUTTC_KEYBOARD; } else if (device.find ("Joystick") != std::string::npos) { type = BUTTC_JOYSTICK; } else { type = 0; } config->getOption (prefix + "DeviceNum", &devnum); for (unsigned int j = 0; j < TOPRIDER_NUM_BUTTONS; j++) { config->getOption (prefix + TopRiderNames[j], &button); TopRiderButtons[j].ButtType = type; TopRiderButtons[j].DeviceNum = devnum; TopRiderButtons[j].ButtonNum = button; } // FTrainer prefix = "SDL.Input.FTrainer."; config->getOption (prefix + "DeviceType", &device); if (device.find ("Keyboard") != std::string::npos) { type = BUTTC_KEYBOARD; } else if (device.find ("Joystick") != std::string::npos) { type = BUTTC_JOYSTICK; } else { type = 0; } config->getOption (prefix + "DeviceNum", &devnum); for (unsigned int j = 0; j < FTRAINER_NUM_BUTTONS; j++) { config->getOption (prefix + FTrainerNames[j], &button); FTrainerButtons[j].ButtType = type; FTrainerButtons[j].DeviceNum = devnum; FTrainerButtons[j].ButtonNum = button; } // FamilyKeyBoard prefix = "SDL.Input.FamilyKeyBoard."; config->getOption (prefix + "DeviceType", &device); if (device.find ("Keyboard") != std::string::npos) { type = BUTTC_KEYBOARD; } else if (device.find ("Joystick") != std::string::npos) { type = BUTTC_JOYSTICK; } else { type = 0; } config->getOption (prefix + "DeviceNum", &devnum); for (unsigned int j = 0; j < FAMILYKEYBOARD_NUM_BUTTONS; j++) { config->getOption (prefix + FamilyKeyBoardNames[j], &button); fkbmap[j].ButtType = type; fkbmap[j].DeviceNum = devnum; fkbmap[j].ButtonNum = button; } } // Definitions from main.h: // GamePad defaults const char *GamePadNames[GAMEPAD_NUM_BUTTONS] = { "A", "B", "Select", "Start", "Up", "Down", "Left", "Right", "TurboA", "TurboB" }; const char *DefaultGamePadDevice[GAMEPAD_NUM_DEVICES] = { "Keyboard", "None", "None", "None" }; const int DefaultGamePad[GAMEPAD_NUM_DEVICES][GAMEPAD_NUM_BUTTONS] = { {SDLK_f, SDLK_d, SDLK_s, SDLK_RETURN, SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1} }; // PowerPad defaults const char *PowerPadNames[POWERPAD_NUM_BUTTONS] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B" }; const char *DefaultPowerPadDevice[POWERPAD_NUM_DEVICES] = { "Keyboard", "None" }; const int DefaultPowerPad[POWERPAD_NUM_DEVICES][POWERPAD_NUM_BUTTONS] = { {SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; // QuizKing defaults const char *QuizKingNames[QUIZKING_NUM_BUTTONS] = { "0", "1", "2", "3", "4", "5" }; const char *DefaultQuizKingDevice = "Keyboard"; const int DefaultQuizKing[QUIZKING_NUM_BUTTONS] = { SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y }; // HyperShot defaults const char *HyperShotNames[HYPERSHOT_NUM_BUTTONS] = { "0", "1", "2", "3" }; const char *DefaultHyperShotDevice = "Keyboard"; const int DefaultHyperShot[HYPERSHOT_NUM_BUTTONS] = { SDLK_q, SDLK_w, SDLK_e, SDLK_r }; // Mahjong defaults const char *MahjongNames[MAHJONG_NUM_BUTTONS] = { "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20" }; const char *DefaultMahjongDevice = "Keyboard"; const int DefaultMahjong[MAHJONG_NUM_BUTTONS] = { SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_b, SDLK_n, SDLK_m }; // TopRider defaults const char *TopRiderNames[TOPRIDER_NUM_BUTTONS] = { "0", "1", "2", "3", "4", "5", "6", "7" }; const char *DefaultTopRiderDevice = "Keyboard"; const int DefaultTopRider[TOPRIDER_NUM_BUTTONS] = { SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i }; // FTrainer defaults const char *FTrainerNames[FTRAINER_NUM_BUTTONS] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B" }; const char *DefaultFTrainerDevice = "Keyboard"; const int DefaultFTrainer[FTRAINER_NUM_BUTTONS] = { SDLK_o, SDLK_p, SDLK_LEFTBRACKET, SDLK_RIGHTBRACKET, SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH }; // FamilyKeyBoard defaults const char *FamilyKeyBoardNames[FAMILYKEYBOARD_NUM_BUTTONS] = { "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "MINUS", "EQUAL", "BACKSLASH", "BACKSPACE", "ESCAPE", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "GRAVE", "BRACKET_LEFT", "ENTER", "LEFTCONTROL", "A", "S", "D", "F", "G", "H", "J", "K", "L", "SEMICOLON", "APOSTROPHE", "BRACKET_RIGHT", "INSERT", "LEFTSHIFT", "Z", "X", "C", "V", "B", "N", "M", "COMMA", "PERIOD", "SLASH", "RIGHTALT", "RIGHTSHIFT", "LEFTALT", "SPACE", "DELETE", "END", "PAGEDOWN", "CURSORUP", "CURSORLEFT", "CURSORRIGHT", "CURSORDOWN" }; const char *DefaultFamilyKeyBoardDevice = "Keyboard"; const int DefaultFamilyKeyBoard[FAMILYKEYBOARD_NUM_BUTTONS] = { SDLK_F1, SDLK_F2, SDLK_F3, SDLK_F4, SDLK_F5, SDLK_F6, SDLK_F7, SDLK_F8, SDLK_1, SDLK_2, SDLK_3, SDLK_4, SDLK_5, SDLK_6, SDLK_7, SDLK_8, SDLK_9, SDLK_0, SDLK_MINUS, SDLK_EQUALS, SDLK_BACKSLASH, SDLK_BACKSPACE, SDLK_ESCAPE, SDLK_q, SDLK_w, SDLK_e, SDLK_r, SDLK_t, SDLK_y, SDLK_u, SDLK_i, SDLK_o, SDLK_p, SDLK_BACKQUOTE, SDLK_LEFTBRACKET, SDLK_RETURN, SDLK_LCTRL, SDLK_a, SDLK_s, SDLK_d, SDLK_f, SDLK_g, SDLK_h, SDLK_j, SDLK_k, SDLK_l, SDLK_SEMICOLON, SDLK_QUOTE, SDLK_RIGHTBRACKET, SDLK_INSERT, SDLK_LSHIFT, SDLK_z, SDLK_x, SDLK_c, SDLK_v, SDLK_b, SDLK_n, SDLK_m, SDLK_COMMA, SDLK_PERIOD, SDLK_SLASH, SDLK_RALT, SDLK_RSHIFT, SDLK_LALT, SDLK_SPACE, SDLK_DELETE, SDLK_END, SDLK_PAGEDOWN, SDLK_UP, SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN };