/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // For commctrl.h below #define _WIN32_IE 0x0300 #include "common.h" #include #include #include #include "input.h" #include "keyboard.h" #include "joystick.h" #include "../../fceu.h" //mbg merge 7/17/06 added #include "keyscan.h" LPDIRECTINPUT7 lpDI=0; /* UsrInputType[] is user-specified. InputType[] is current (game loading can override user settings) */ int UsrInputType[3]={SI_GAMEPAD,SI_GAMEPAD,SIFC_NONE}; int InputType[3]={0,0,0}; int cspec=0; int gametype=0; int InitDInput(void) { HRESULT ddrval; //mbg merge 7/17/06 changed: //ddrval=DirectInputCreateEx(fceu_hInstance,DIRECTINPUT_VERSION,&IID_IDirectInput7,(LPVOID *)&lpDI,0); ddrval=DirectInputCreateEx(fceu_hInstance,DIRECTINPUT_VERSION,IID_IDirectInput7,(LPVOID *)&lpDI,0); if(ddrval!=DI_OK) { FCEUD_PrintError("DirectInput: Error creating DirectInput object."); return 0; } return 1; } static uint32 MouseData[3]; static int screenmode=0; void InputScreenChanged(int fs) { int x; if(GI) { for(x=0;x<2;x++) if(InputType[x]==SI_ZAPPER) FCEUI_SetInput(x,SI_ZAPPER,MouseData,fs); if(InputType[2]==SIFC_SHADOW) FCEUI_SetInputFC(SIFC_SHADOW,MouseData,fs); } screenmode=fs; } /* Necessary for proper GUI functioning(configuring when a game isn't loaded). */ void InputUserActiveFix(void) { int x; for(x=0;x<3;x++) InputType[x]=UsrInputType[x]; } void ParseGIInput(FCEUGI *gi) { InputType[0]=UsrInputType[0]; InputType[1]=UsrInputType[1]; InputType[2]=UsrInputType[2]; if(gi) { if(gi->input[0]>=0) InputType[0]=gi->input[0]; if(gi->input[1]>=0) InputType[1]=gi->input[1]; if(gi->inputfc>=0) InputType[2]=gi->inputfc; cspec = gi->cspecial; gametype=gi->type; InitOtherInput(); } else cspec=gametype=0; } 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 UpdateSuborKB(void); 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; int NoWaiting=0; #include "keyscan.h" static unsigned char *keys=0; static unsigned char *keys_nr=0; static int DIPS=0; static uint8 keyonce[MKK_COUNT]; //#define KEY(__a) keys_nr[MKK(__a)] static int _keyonly(int a) { if(keys_nr[a]) { if(!keyonce[a]) { keyonce[a]=1; return(1); } } else keyonce[a]=0; return(0); } int cidisabled=0; #define MK(x) {{BUTTC_KEYBOARD},{0},{MKK(x)},1} #define MC(x) {{BUTTC_KEYBOARD},{0},{x},1} #define MK2(x1,x2) {{BUTTC_KEYBOARD},{0},{MKK(x1),MKK(x2)},2} #define MKZ() {{0},{0},{0},0} #define GPZ() {MKZ(), MKZ(), MKZ(), MKZ()} ButtConfig GamePadConfig[4][10]={ /* Gamepad 1 */ { MK(LEFTALT), MK(LEFTCONTROL), MK(TAB), MK(ENTER), MK(BL_CURSORUP), MK(BL_CURSORDOWN),MK(BL_CURSORLEFT),MK(BL_CURSORRIGHT) }, /* Gamepad 2 */ GPZ(), /* Gamepad 3 */ GPZ(), /* Gamepad 4 */ GPZ() }; extern int rapidAlternator; // for auto-fire / autofire int DesynchAutoFire=0; // A and B not at same time uint32 JSAutoHeld=0, JSAutoHeldAffected=0; // for auto-hold uint8 autoHoldOn=0, autoHoldReset=0, autoHoldRefire=0; // for auto-hold void SetAutoFireDesynch(int DesynchOn) { if(DesynchOn) { DesynchAutoFire = 1; } else { DesynchAutoFire = 0; } } int GetAutoFireDesynch() { return DesynchAutoFire; } int DTestButton(ButtConfig *bc) { uint32 x;//mbg merge 7/17/06 changed to uint for(x=0;xNumC;x++) { if(bc->ButtType[x]==BUTTC_KEYBOARD) { if(keys_nr[bc->ButtonNum[x]]) { return(1); } } } if(DTestButtonJoy(bc)) return(1); return(0); } void UpdateGamepad() { if(FCEUI_IsMovieActive()<0) return; uint32 JS=0; int x; int wg; if(FCEUI_IsMovieActive()>0) AutoFire(); for(wg=0;wg<4;wg++) { for(x=0;x<8;x++) if(DTestButton(&GamePadConfig[wg][x])) JS|=(1<':' ', (c&0x20)?'v':' ', (c&0x01)?'A':' ', (c&0x02)?'B':' ', (c&0x08)?'S':' ', (c&0x04)?'s':' ', (c&0x4000)?'<':' ', (c&0x1000)?'^':' ', (c&0x8000)?'>':' ', (c&0x2000)?'v':' ', (c&0x0100)?'A':' ', (c&0x0200)?'B':' ', (c&0x0800)?'S':' ', (c&0x0400)?'s':' '); if(!(c&0xff00)) inputstr[8] = '\0'; } FCEU_DispMessage("Held: %s", inputstr); } else { JSAutoHeldAffected = 0; autoHoldRefire = 0; } if(autoHoldReset) { FCEU_DispMessage("Held: "); JSAutoHeld = 0; JSAutoHeldAffected = 0; autoHoldRefire = 0; } // apply auto-hold if(JSAutoHeld) JS ^= JSAutoHeld; JSreturn=JS; } ButtConfig powerpadsc[2][12]={ { MK(O),MK(P),MK(BRACKET_LEFT), MK(BRACKET_RIGHT), MK(K),MK(L),MK(SEMICOLON), MK(APOSTROPHE), MK(M),MK(COMMA),MK(PERIOD),MK(SLASH) }, { MK(O),MK(P),MK(BRACKET_LEFT), MK(BRACKET_RIGHT),MK(K),MK(L),MK(SEMICOLON), MK(APOSTROPHE), MK(M),MK(COMMA),MK(PERIOD),MK(SLASH) } }; static uint32 powerpadbuf[2]; static uint32 UpdatePPadData(int w) { uint32 r=0; ButtConfig *ppadtsc=powerpadsc[w]; int x; for(x=0;x<12;x++) if(DTestButton(&ppadtsc[x])) r|=1<NumC;x++) { if(x) strcat(tmpstr, ", "); if(bc->ButtType[x] == BUTTC_KEYBOARD) { strcat(tmpstr,"KB: "); if(!GetKeyNameText(bc->ButtonNum[x]<<16,tmpstr+strlen(tmpstr),16)) { switch(bc->ButtonNum[x]) { case 200: strcpy(tmpstr+strlen(tmpstr),"Up Arrow"); break; case 203: strcpy(tmpstr+strlen(tmpstr),"Left Arrow"); break; case 205: strcpy(tmpstr+strlen(tmpstr),"Right Arrow"); break; case 208: strcpy(tmpstr+strlen(tmpstr),"Down Arrow"); break; default: sprintf(tmpstr+strlen(tmpstr),"%03d",bc->ButtonNum[x]); break; } } } else if(bc->ButtType[x] == BUTTC_JOYSTICK) { strcat(tmpstr,"JS "); sprintf(tmpstr+strlen(tmpstr), "%d ", bc->DeviceNum[x]); if(bc->ButtonNum[x] & 0x8000) { char *asel[3]={"x","y","z"}; sprintf(tmpstr+strlen(tmpstr), "axis %s%s", asel[bc->ButtonNum[x] & 3],(bc->ButtonNum[x]&0x4000)?"-":"+"); } else if(bc->ButtonNum[x] & 0x2000) { sprintf(tmpstr+strlen(tmpstr), "hat %d:%d", (bc->ButtonNum[x] >> 4)&3, bc->ButtonNum[x]&3); } else { sprintf(tmpstr+strlen(tmpstr), "button %d", bc->ButtonNum[x] & 127); } } } astr=(char*)malloc(strlen(tmpstr) + 1); //mbg merge 7/17/06 added cast strcpy(astr,tmpstr); return(astr); } static int DWBStarted; static ButtConfig *DWBButtons; static const uint8 *DWBText; static HWND die; static BOOL CALLBACK DWBCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_DESTROY: die = NULL; return(0); case WM_TIMER: { uint8 devicenum; uint16 buttonnum; GUID guid; if(DoJoyWaitTest(&guid, &devicenum, &buttonnum)) { ButtConfig *bc = DWBButtons; char *nstr; int wc; if(DWBStarted) { ButtConfig *bc = DWBButtons; bc->NumC = 0; DWBStarted = 0; } wc = bc->NumC; //FCEU_printf("%d: %d\n",devicenum,buttonnum); bc->ButtType[wc]=BUTTC_JOYSTICK; bc->DeviceNum[wc]=devicenum; bc->ButtonNum[wc]=buttonnum; bc->DeviceInstance[wc] = guid; /* Stop config if the user pushes the same button twice in a row. */ if(wc && bc->ButtType[wc]==bc->ButtType[wc-1] && bc->DeviceNum[wc]==bc->DeviceNum[wc-1] && bc->ButtonNum[wc]==bc->ButtonNum[wc-1]) goto gornk; bc->NumC++; /* Stop config if we reached our maximum button limit. */ if(bc->NumC >= MAXBUTTCONFIG) goto gornk; nstr = MakeButtString(bc); SetDlgItemText(hwndDlg, 100, nstr); free(nstr); } } break; case WM_USER + 666: //SetFocus(GetDlgItem(hwndDlg,100)); if(DWBStarted) { char *nstr; ButtConfig *bc = DWBButtons; bc->NumC = 0; DWBStarted = 0; nstr = MakeButtString(bc); SetDlgItemText(hwndDlg, 100, nstr); free(nstr); } { ButtConfig *bc = DWBButtons; int wc = bc->NumC; char *nstr; bc->ButtType[wc]=BUTTC_KEYBOARD; bc->DeviceNum[wc]=0; bc->ButtonNum[wc]=lParam&255; /* Stop config if the user pushes the same button twice in a row. */ if(wc && bc->ButtType[wc]==bc->ButtType[wc-1] && bc->DeviceNum[wc]==bc->DeviceNum[wc-1] && bc->ButtonNum[wc]==bc->ButtonNum[wc-1]) goto gornk; bc->NumC++; /* Stop config if we reached our maximum button limit. */ if(bc->NumC >= MAXBUTTCONFIG) goto gornk; nstr = MakeButtString(bc); SetDlgItemText(hwndDlg, 100, nstr); free(nstr); } break; case WM_INITDIALOG: SetWindowText(hwndDlg, (char*)DWBText); //mbg merge 7/17/06 added cast BeginJoyWait(hwndDlg); SetTimer(hwndDlg,666,25,0); /* Every 25ms.*/ { char *nstr = MakeButtString(DWBButtons); SetDlgItemText(hwndDlg, 100, nstr); free(nstr); } break; case WM_CLOSE: case WM_QUIT: goto gornk; case WM_COMMAND: switch(wParam&0xFFFF) { case 200: { ButtConfig *bc = DWBButtons; char *nstr; bc->NumC = 0; nstr = MakeButtString(bc); SetDlgItemText(hwndDlg, 100, nstr); free(nstr); } break; case 201: gornk: KillTimer(hwndDlg,666); EndJoyWait(hAppWnd); SetForegroundWindow(GetParent(hwndDlg)); DestroyWindow(hwndDlg); break; } } return 0; } int DWaitButton(HWND hParent, const uint8 *text, ButtConfig *bc) { DWBText=text; DWBButtons = bc; DWBStarted = 1; die = CreateDialog(fceu_hInstance, "DWBDIALOG", hParent, DWBCallB); EnableWindow(hParent, 0); ShowWindow(die, 1); while(die) { MSG msg; while(PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) { if(GetMessage(&msg, 0, 0, 0) > 0) { if(msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) { LPARAM tmpo; tmpo=((msg.lParam>>16)&0x7F)|((msg.lParam>>17)&0x80); PostMessage(die,WM_USER+666,0,tmpo); continue; } if(msg.message == WM_SYSCOMMAND) continue; if(!IsDialogMessage(die, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } Sleep(10); } EnableWindow(hParent, 1); return 0; //mbg merge TODO 7/17/06 - had to add this return value--is it right? } int DWaitSimpleButton(HWND hParent, const uint8 *text) { DWBStarted = 1; int ret = 0; die = CreateDialog(fceu_hInstance, "DWBDIALOGSIMPLE", hParent, NULL); SetWindowText(die, (char*)text); //mbg merge 7/17/06 added cast EnableWindow(hParent, 0); ShowWindow(die, 1); while(die) { MSG msg; while(PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) { if(GetMessage(&msg, 0, 0, 0) > 0) { if(msg.message == WM_KEYDOWN || msg.message == WM_SYSKEYDOWN) { LPARAM tmpo; tmpo=((msg.lParam>>16)&0x7F)|((msg.lParam>>17)&0x80); ret = tmpo; goto done; } if(msg.message == WM_SYSCOMMAND) continue; if(!IsDialogMessage(die, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } } Sleep(10); } done: EndDialog(die,0); EnableWindow(hParent, 1); if(ret == 1) // convert Esc to nothing (why is it 1 and not VK_ESCAPE?) ret = 0; return ret; } static ButtConfig *DoTBButtons=0; static const char *DoTBTitle=0; static int DoTBMax=0; static int DoTBType=0,DoTBPort=0; static BOOL CALLBACK DoTBCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_INITDIALOG: if(DoTBType == SI_GAMEPAD) { char buf[32]; sprintf(buf,"Virtual Gamepad %d",DoTBPort+1); SetDlgItemText(hwndDlg, 100,buf); sprintf(buf,"Virtual Gamepad %d",DoTBPort+3); SetDlgItemText(hwndDlg, 101, buf); CheckDlgButton(hwndDlg,400,(eoptions & EO_NOFOURSCORE)?BST_CHECKED:BST_UNCHECKED); } SetWindowText(hwndDlg, DoTBTitle); break; case WM_CLOSE: case WM_QUIT: goto gornk; case WM_COMMAND: { int b; b=wParam&0xFFFF; if(b>= 300 && b < (300 + DoTBMax)) { char btext[128]; btext[0]=0; GetDlgItemText(hwndDlg, b, btext, 128); DWaitButton(hwndDlg, (uint8*)btext,&DoTBButtons[b - 300]); //mbg merge 7/17/06 added cast } else switch(wParam&0xFFFF) { case 1: gornk: if(DoTBType == SI_GAMEPAD) { eoptions &= ~EO_NOFOURSCORE; if(IsDlgButtonChecked(hwndDlg,400)==BST_CHECKED) eoptions|=EO_NOFOURSCORE; } EndDialog(hwndDlg,0); break; } } } return 0; } static void DoTBConfig(HWND hParent, const char *text, char *_template, ButtConfig *buttons, int max) { DoTBTitle=text; DoTBButtons = buttons; DoTBMax = max; DialogBox(fceu_hInstance,_template,hParent,DoTBCallB); } static BOOL CALLBACK InputConCallB(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static const char *strn[6]={"","Gamepad","Zapper","Power Pad A","Power Pad B","Arkanoid Paddle"}; static const char *strf[14]= {"","Arkanoid Paddle","Hyper Shot gun","4-Player Adapter", "Family Keyboard","Subor Keyboard","HyperShot Pads", "Mahjong", "Quiz King Buzzers", "Family Trainer A","Family Trainer B", "Oeka Kids Tablet", "Barcode World", "Top Rider"}; static const int haven[6]={0,1,0,1,1,0}; static const int havef[14]={0,0,0,0, 1,1,0,0, 1,1,1,0, 0,0}; int x; switch(uMsg) { case WM_INITDIALOG: SetDlgItemText(hwndDlg,65488,"Select the device you want to be enabled on input ports 1 and 2, and the Famicom expansion port. You may configure the device listed above each drop-down list by pressing \"Configure\", if applicable. The device currently being emulated on the each port is listed above the drop down list; loading certain games will override your settings, but only temporarily. If you select a device to be on the emulated Famicom Expansion Port, you should probably have emulated gamepads on the emulated NES-style input ports."); for(x=0;x<2;x++) { int y; for(y=0;y<6;y++) SendDlgItemMessage(hwndDlg,104+x,CB_ADDSTRING,0,(LPARAM)(LPSTR)strn[y]); SendDlgItemMessage(hwndDlg,104+x,CB_SETCURSEL,UsrInputType[x],(LPARAM)(LPSTR)0); EnableWindow(GetDlgItem(hwndDlg,106+x),haven[InputType[x]]); SetDlgItemText(hwndDlg,200+x,(LPTSTR)strn[InputType[x]]); } { int y; for(y=0;y<13;y++) SendDlgItemMessage(hwndDlg,110,CB_ADDSTRING,0,(LPARAM)(LPSTR)strf[y]); SendDlgItemMessage(hwndDlg,110,CB_SETCURSEL,UsrInputType[2],(LPARAM)(LPSTR)0); EnableWindow(GetDlgItem(hwndDlg,111),havef[InputType[2]]); SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputType[2]]); } extern int autoHoldKey, autoHoldClearKey; char btext[128]; if(autoHoldKey) { if(!GetKeyNameText(autoHoldKey<<16,btext,128)) sprintf(btext, "KB: %d", autoHoldKey); } else sprintf(btext, "not assigned"); SetDlgItemText(hwndDlg, 115, btext); if(autoHoldClearKey) { if(!GetKeyNameText(autoHoldClearKey<<16,btext,128)) sprintf(btext, "KB: %d", autoHoldClearKey); } else sprintf(btext, "not assigned"); SetDlgItemText(hwndDlg, 116, btext); break; case WM_CLOSE: case WM_QUIT: goto gornk; case WM_COMMAND: if(HIWORD(wParam)==CBN_SELENDOK) { switch(LOWORD(wParam)) { case 104: case 105:UsrInputType[LOWORD(wParam)-104]=InputType[LOWORD(wParam)-104]=SendDlgItemMessage(hwndDlg,LOWORD(wParam),CB_GETCURSEL,0,(LPARAM)(LPSTR)0); EnableWindow( GetDlgItem(hwndDlg,LOWORD(wParam)+2),haven[InputType[LOWORD(wParam)-104]]); SetDlgItemText(hwndDlg,200+LOWORD(wParam)-104,(LPTSTR)strn[InputType[LOWORD(wParam)-104]]); break; case 110:UsrInputType[2]=InputType[2]=SendDlgItemMessage(hwndDlg,110,CB_GETCURSEL,0,(LPARAM)(LPSTR)0); EnableWindow(GetDlgItem(hwndDlg,111),havef[InputType[2]]); SetDlgItemText(hwndDlg,202,(LPTSTR)strf[InputType[2]]); break; } } if(!(wParam>>16)) switch(wParam&0xFFFF) { case 111: { const char *text = strf[InputType[2]]; DoTBType=DoTBPort=0; switch(InputType[2]) { case SIFC_FTRAINERA: case SIFC_FTRAINERB:DoTBConfig(hwndDlg, text, "POWERPADDIALOG", FTrainerButtons, 12); break; case SIFC_FKB:DoTBConfig(hwndDlg, text, "FKBDIALOG",fkbmap,0x48);break; case SIFC_SUBORKB:DoTBConfig(hwndDlg, text, "SUBORKBDIALOG",suborkbmap,0x60);break; case SIFC_QUIZKING:DoTBConfig(hwndDlg, text, "QUIZKINGDIALOG",QuizKingButtons,6);break; } } break; case 107: case 106: { int which=(wParam&0xFFFF)-106; const char *text = strn[InputType[which]]; DoTBType=DoTBPort=0; switch(InputType[which]) { case SI_GAMEPAD: { ButtConfig tmp[10 + 10]; memcpy(tmp, GamePadConfig[which], 10 * sizeof(ButtConfig)); memcpy(&tmp[10], GamePadConfig[which+2], 10 * sizeof(ButtConfig)); DoTBType=SI_GAMEPAD; DoTBPort=which; DoTBConfig(hwndDlg, text, "GAMEPADDIALOG", tmp, 10 + 10); memcpy(GamePadConfig[which], tmp, 10 * sizeof(ButtConfig)); memcpy(GamePadConfig[which+2], &tmp[10], 10 * sizeof(ButtConfig)); } break; case SI_POWERPADA: case SI_POWERPADB: DoTBConfig(hwndDlg, text, "POWERPADDIALOG",powerpadsc[which],12); break; } } break; case 112: // auto-hold button { char btext[128]; btext[0]=0; GetDlgItemText(hwndDlg, 112, btext, 128); int button = DWaitSimpleButton(hwndDlg, (uint8*)btext); //mbg merge 7/17/06 if(button) { if(!GetKeyNameText(button<<16,btext,128)) sprintf(btext, "KB: %d", button); } else sprintf(btext, "not assigned"); extern int autoHoldKey; autoHoldKey = button; SetDlgItemText(hwndDlg, 115, btext); } break; case 114: // auto-hold clear button { char btext[128]; btext[0]=0; GetDlgItemText(hwndDlg, 114, btext, 128); int button = DWaitSimpleButton(hwndDlg, (uint8*)btext); //mbg merge 7/17/06 added cast if(button) { if(!GetKeyNameText(button<<16,btext,128)) sprintf(btext, "KB: %d", button); } else sprintf(btext, "not assigned"); extern int autoHoldClearKey; autoHoldClearKey = button; SetDlgItemText(hwndDlg, 116, btext); } break; case 1: gornk: EndDialog(hwndDlg,0); break; } } return 0; } void ConfigInput(HWND hParent) { DialogBox(fceu_hInstance,"INPUTCONFIG",hParent,InputConCallB); if(GI) InitOtherInput(); } void DestroyInput(void) { if(lpDI) { KillJoysticks(); KeyboardClose(); IDirectInput7_Release(lpDI); } } #define CMD_KEY_MASK 0xff #define CMD_KEY_LSHIFT (1<<16) #define CMD_KEY_RSHIFT (1<<17) #define CMD_KEY_SHIFT (CMD_KEY_LSHIFT|CMD_KEY_RSHIFT) #define CMD_KEY_LCTRL (1<<18) #define CMD_KEY_RCTRL (1<<19) #define CMD_KEY_CTRL (CMD_KEY_LCTRL|CMD_KEY_RCTRL) #define CMD_KEY_LALT (1<<20) #define CMD_KEY_RALT (1<<21) #define CMD_KEY_ALT (CMD_KEY_LALT|CMD_KEY_RALT) int FCEUD_CommandMapping[EMUCMD_MAX]; CFGSTRUCT HotkeyConfig[]={ AC(FCEUD_CommandMapping), ENDCFGSTRUCT }; static struct { int cmd; int key; } DefaultCommandMapping[]= { { EMUCMD_RESET, SCAN_R | CMD_KEY_CTRL, }, { EMUCMD_PAUSE, SCAN_PAUSE, }, { EMUCMD_FRAME_ADVANCE, SCAN_BACKSLASH, }, { EMUCMD_SCREENSHOT, SCAN_F12 }, { EMUCMD_HIDE_MENU_TOGGLE, SCAN_ESCAPE }, { EMUCMD_SPEED_SLOWER, SCAN_MINUS, }, { EMUCMD_SPEED_FASTER, SCAN_EQUAL, }, { EMUCMD_SPEED_TURBO, SCAN_TAB, }, { EMUCMD_SAVE_SLOT_0, SCAN_0, }, { EMUCMD_SAVE_SLOT_1, SCAN_1, }, { EMUCMD_SAVE_SLOT_2, SCAN_2, }, { EMUCMD_SAVE_SLOT_3, SCAN_3, }, { EMUCMD_SAVE_SLOT_4, SCAN_4, }, { EMUCMD_SAVE_SLOT_5, SCAN_5, }, { EMUCMD_SAVE_SLOT_6, SCAN_6, }, { EMUCMD_SAVE_SLOT_7, SCAN_7, }, { EMUCMD_SAVE_SLOT_8, SCAN_8, }, { EMUCMD_SAVE_SLOT_9, SCAN_9, }, { EMUCMD_SAVE_STATE, SCAN_F5 | CMD_KEY_CTRL, }, { EMUCMD_LOAD_STATE, SCAN_F7 | CMD_KEY_CTRL, }, { EMUCMD_SAVE_STATE_SLOT_0, SCAN_F10 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_1, SCAN_F1 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_2, SCAN_F2 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_3, SCAN_F3 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_4, SCAN_F4 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_5, SCAN_F5 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_6, SCAN_F6 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_7, SCAN_F7 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_8, SCAN_F8 | CMD_KEY_SHIFT, }, { EMUCMD_SAVE_STATE_SLOT_9, SCAN_F9 | CMD_KEY_SHIFT, }, { EMUCMD_LOAD_STATE_SLOT_0, SCAN_F10, }, { EMUCMD_LOAD_STATE_SLOT_1, SCAN_F1, }, { EMUCMD_LOAD_STATE_SLOT_2, SCAN_F2, }, { EMUCMD_LOAD_STATE_SLOT_3, SCAN_F3, }, { EMUCMD_LOAD_STATE_SLOT_4, SCAN_F4, }, { EMUCMD_LOAD_STATE_SLOT_5, SCAN_F5, }, { EMUCMD_LOAD_STATE_SLOT_6, SCAN_F6, }, { EMUCMD_LOAD_STATE_SLOT_7, SCAN_F7, }, { EMUCMD_LOAD_STATE_SLOT_8, SCAN_F8, }, { EMUCMD_LOAD_STATE_SLOT_9, SCAN_F9, }, /* { EMUCMD_MOVIE_SLOT_0, SCAN_0 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_1, SCAN_1 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_2, SCAN_2 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_3, SCAN_3 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_4, SCAN_4 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_5, SCAN_5 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_6, SCAN_6 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_7, SCAN_7 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_8, SCAN_8 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_SLOT_9, SCAN_9 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_RECORD, SCAN_F5 | CMD_KEY_ALT, }, { EMUCMD_MOVIE_REPLAY, SCAN_F7 | CMD_KEY_ALT, },*/ { EMUCMD_MOVIE_FRAME_DISPLAY_TOGGLE, SCAN_PERIOD, }, { EMUCMD_FDS_EJECT_INSERT, SCAN_F8 | CMD_KEY_CTRL, }, { EMUCMD_FDS_SIDE_SELECT, SCAN_F6 | CMD_KEY_CTRL, }, { EMUCMD_MOVIE_INPUT_DISPLAY_TOGGLE, SCAN_COMMA, }, { EMUCMD_MOVIE_READONLY_TOGGLE, SCAN_8 | CMD_KEY_SHIFT, }, { EMUCMD_MISC_REWIND, SCAN_R, }, }; #define NUM_DEFAULT_MAPPINGS (sizeof(DefaultCommandMapping)/sizeof(DefaultCommandMapping[0])) void ApplyDefaultCommandMapping(void) { int i; memset(FCEUD_CommandMapping, 0, sizeof(FCEUD_CommandMapping)); for(i=0; ihThreadExit) { if (WaitForSingleObject(args->hThreadExit, 20) == WAIT_OBJECT_0) break; // Poke our owner dialog periodically. PostMessage(args->hwndDlg, WM_USER, 0, 0); } return 0; } static int GetKeyPressed() { int key=0; int i; keys_nr=GetKeyboard_nr(); for(i=0; i<256 && !key; ++i) { if(_keyonly(i)) key=i; } return key; } static int NothingPressed() { int i; keys_nr=GetKeyboard_nr(); for(i=0; i<256; ++i) { if(keys_nr[i]) return 0; } return 1; } static int GetKeyMeta(int key) { int meta = key & (~CMD_KEY_MASK); switch(key & CMD_KEY_MASK) { case SCAN_LEFTCONTROL: case SCAN_RIGHTCONTROL: return CMD_KEY_CTRL | meta; case SCAN_LEFTALT: case SCAN_RIGHTALT: return CMD_KEY_ALT | meta; case SCAN_LEFTSHIFT: case SCAN_RIGHTSHIFT: return CMD_KEY_SHIFT | meta; default: break; } return meta; } static void ClearExtraMeta(int* key) { switch((*key)&0xff) { case SCAN_LEFTCONTROL: case SCAN_RIGHTCONTROL: *key &= ~(CMD_KEY_CTRL); break; case SCAN_LEFTALT: case SCAN_RIGHTALT: *key &= ~(CMD_KEY_ALT); break; case SCAN_LEFTSHIFT: case SCAN_RIGHTSHIFT: *key &= ~(CMD_KEY_SHIFT); break; default: break; } } static BOOL CALLBACK ChangeInputDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static HANDLE hThread = NULL; static DWORD dwThreadId = 0; static struct INPUTDLGTHREADARGS threadargs; static int key=0; switch (uMsg) { case WM_INITDIALOG: { // Start the message thread. threadargs.hThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL); threadargs.hwndDlg = hwndDlg; hThread = CreateThread(NULL, 0, NewInputDialogThread, (LPVOID)&threadargs, 0, &dwThreadId); key=0; memset(keyonce, 0, sizeof(keyonce)); KeyboardSetBackgroundAccess(1); SetFocus(GetDlgItem(hwndDlg, 100)); } return FALSE; case WM_COMMAND: if(LOWORD(wParam) == 202 && HIWORD(wParam) == BN_CLICKED) { key=0; PostMessage(hwndDlg, WM_USER+1, 0, 0); // Send quit message. } else if(LOWORD(wParam) == 200 && HIWORD(wParam) == BN_CLICKED) { key=-1; PostMessage(hwndDlg, WM_USER+1, 0, 0); // Send quit message. } break; case WM_USER: { // Our thread sent us a timer signal. int newkey; int meta=GetKeyMeta(key); KeyboardUpdateState(); if((newkey=GetKeyPressed()) != 0) { key = newkey | meta; ClearExtraMeta(&key); SetDlgItemText(hwndDlg, 100, GetKeyComboName(key)); } else if(NothingPressed() && key) PostMessage(hwndDlg, WM_USER+1, 0, 0); // Send quit message. } break; case WM_USER+1: { // Done with keyboard. KeyboardSetBackgroundAccess(0); // Kill the thread. SetEvent(threadargs.hThreadExit); WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(threadargs.hThreadExit); // End the dialog. EndDialog(hwndDlg, key); return TRUE; } default: break; } return FALSE; } static int* ConflictTable=0; static int ShouldDisplayMapping(int mapn, int filter) { //mbg merge 7/17/06 changed to if..elseif if(filter==0) /* No filter */ return 1; else if(filter <= EMUCMDTYPE_MAX) /* Filter by type */ return (FCEUI_CommandTable[mapn].type == filter-1); else if(filter == EMUCMDTYPE_MAX+1) /* Assigned */ return FCEUD_CommandMapping[mapn]; else if(filter == EMUCMDTYPE_MAX+2) /* Unassigned */ return !(FCEUD_CommandMapping[mapn]); else if(filter == EMUCMDTYPE_MAX+3) /* Conflicts */ return ConflictTable[mapn]; else return 0; } static void PopulateMappingDisplay(HWND hwndDlg) { LVITEM lvi; int i; int idx; HWND hwndListView = GetDlgItem(hwndDlg, 1003); int num=SendMessage(hwndListView, LVM_GETITEMCOUNT, 0, 0); int filter = (int)SendDlgItemMessage(hwndDlg, 300, CB_GETCURSEL, 0, 0); // Delete everything in the current display. for(i=num; i>0; --i) SendMessage(hwndListView, LVM_DELETEITEM, (WPARAM)i-1, 0); // Get the filter type. ConflictTable=0; if (filter == EMUCMDTYPE_MAX+3) { // Set up the conflict table. ConflictTable=(int*)malloc(sizeof(int)*EMUCMD_MAX); memset(ConflictTable, 0, sizeof(int)*EMUCMD_MAX); for(i=0; icode == LVN_ITEMACTIVATE) { int nSel = SendMessage(hwndListView, LVM_GETNEXTITEM, (WPARAM)-1, LVNI_SELECTED); if (nSel != -1) { int nCmd; int nRet; LVITEM lvi; // Get the corresponding input memset(&lvi, 0, sizeof(lvi)); lvi.mask = LVIF_PARAM; lvi.iItem = nSel; lvi.iSubItem = 0; SendMessage(hwndListView, LVM_GETITEM, 0, (LPARAM)&lvi); nCmd = lvi.lParam; nRet = DialogBox(fceu_hInstance,"NEWINPUT",hwndListView,ChangeInputDialogProc); if (nRet) { // nRet will be -1 when the user selects "clear". FCEUD_CommandMapping[nCmd] = (nRet<0) ? 0 : nRet; memset(&lvi, 0, sizeof(lvi)); lvi.mask = LVIF_TEXT; lvi.iItem = nSel; lvi.iSubItem = 2; lvi.pszText = GetKeyComboName(FCEUD_CommandMapping[nCmd]); SendMessage(hwndListView, LVM_SETITEM, (WPARAM)0, (LPARAM)&lvi); } } } return TRUE; } break; default: break; } break; default: break; } return FALSE; } void MapInput(void) { // Make a backup of the current mappings, in case the user changes their mind. int* backupmapping = (int*)malloc(sizeof(FCEUD_CommandMapping)); memcpy(backupmapping, FCEUD_CommandMapping, sizeof(FCEUD_CommandMapping)); if(!DialogBox(fceu_hInstance,"MAPINPUT",hAppWnd,MapInputDialogProc)) memcpy(FCEUD_CommandMapping, backupmapping, sizeof(FCEUD_CommandMapping)); free(backupmapping); } void FCEUD_TurboOn(void) { NoWaiting|=1; } void FCEUD_TurboOff(void) { NoWaiting&=~1; }