fceux/drivers/win/input.cpp

1835 lines
51 KiB
C++

/* 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 <dinput.h>
#include <windows.h>
#include <commctrl.h>
#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;x<bc->NumC;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<<x)<<(wg<<3);
// if(rapidAlternator)
for(x=0;x<2;x++)
if(DTestButton(&GamePadConfig[wg][8+x]))
JS|=((1<<x)<<(wg<<3))*(rapidAlternator^(x*DesynchAutoFire));
}
if(autoHoldOn)
{
if(autoHoldRefire)
{
autoHoldRefire--;
if(!autoHoldRefire)
JSAutoHeldAffected = 0;
}
for(wg=0;wg<4;wg++)
for(x=0;x<8;x++)
if(DTestButton(&GamePadConfig[wg][x]))
{
if(!autoHoldRefire || !(JSAutoHeldAffected&(1<<x)<<(wg<<3)))
{
JSAutoHeld^=(1<<x)<<(wg<<3);
JSAutoHeldAffected|=(1<<x)<<(wg<<3);
autoHoldRefire = 192;
}
}
char inputstr [32];
{
uint32 c = JSAutoHeld;
sprintf(inputstr, "%c%c%c%c%c%c%c%c %c%c%c%c%c%c%c%c",
(c&0x40)?'<':' ', (c&0x10)?'^':' ', (c&0x80)?'>':' ', (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<<x;
return r;
}
static uint8 fkbkeys[0x48];
static uint8 suborkbkeys[0x60];
void KeyboardUpdateState(void); //mbg merge 7/17/06 yech had to add this
void GetMouseData(uint32 *md); //mbg merge 7/17/06 yech had to add this
void FCEUD_UpdateInput()
{
int x;
int t=0;
KeyboardUpdateState();
UpdateJoysticks();
//UpdatePhysicalInput();
//KeyboardCommands();
FCEUI_HandleEmuCommands(FCEUD_TestCommandState);
{
for(x=0;x<2;x++)
switch(InputType[x])
{
case SI_GAMEPAD:t|=1;break;
case SI_ARKANOID:t|=2;break;
case SI_ZAPPER:t|=2;break;
case SI_POWERPADA:
case SI_POWERPADB:powerpadbuf[x]=UpdatePPadData(x);break;
}
switch(InputType[2])
{
case SIFC_ARKANOID:t|=2;break;
case SIFC_SHADOW:t|=2;break;
case SIFC_FKB:if(cidisabled) UpdateFKB();break;
case SIFC_SUBORKB:if(cidisabled) UpdateSuborKB();break;
case SIFC_HYPERSHOT: UpdateHyperShot();break;
case SIFC_MAHJONG: UpdateMahjong();break;
case SIFC_QUIZKING: UpdateQuizKing();break;
case SIFC_FTRAINERB:
case SIFC_FTRAINERA: UpdateFTrainer();break;
case SIFC_TOPRIDER: UpdateTopRider();break;
case SIFC_OEKAKIDS:t|=2;break;
}
if(t&1)
UpdateGamepad();
if(t&2)
GetMouseData(MouseData);
}
}
void InitOtherInput(void)
{
void *InputDPtr;
int t;
int x;
int attrib;
for(t=0,x=0;x<2;x++)
{
attrib=0;
InputDPtr=0;
switch(InputType[x])
{
case SI_POWERPADA:
case SI_POWERPADB:InputDPtr=&powerpadbuf[x];break;
case SI_GAMEPAD:InputDPtr=&JSreturn;break;
case SI_ARKANOID:InputDPtr=MouseData;t|=1;break;
case SI_ZAPPER:InputDPtr=MouseData;
t|=1;
attrib=screenmode;
break;
}
FCEUI_SetInput(x,InputType[x],InputDPtr,attrib);
}
attrib=0;
InputDPtr=0;
switch(InputType[2])
{
case SIFC_SHADOW:InputDPtr=MouseData;t|=1;attrib=screenmode;break;
case SIFC_OEKAKIDS:InputDPtr=MouseData;t|=1;attrib=screenmode;break;
case SIFC_ARKANOID:InputDPtr=MouseData;t|=1;break;
case SIFC_FKB:InputDPtr=fkbkeys;break;
case SIFC_SUBORKB:InputDPtr=suborkbkeys;break;
case SIFC_HYPERSHOT:InputDPtr=&HyperShotData;break;
case SIFC_MAHJONG:InputDPtr=&MahjongData;break;
case SIFC_QUIZKING:InputDPtr=&QuizKingData;break;
case SIFC_TOPRIDER:InputDPtr=&TopRiderData;break;
case SIFC_BWORLD:InputDPtr=BWorldData;break;
case SIFC_FTRAINERA:
case SIFC_FTRAINERB:InputDPtr=&FTrainerData;break;
}
FCEUI_SetInputFC(InputType[2],InputDPtr,attrib);
FCEUI_DisableFourScore(eoptions&EO_NOFOURSCORE);
}
ButtConfig fkbmap[0x48]=
{
MK(F1),MK(F2),MK(F3),MK(F4),MK(F5),MK(F6),MK(F7),MK(F8),
MK(1),MK(2),MK(3),MK(4),MK(5),MK(6),MK(7),MK(8),MK(9),MK(0),
MK(MINUS),MK(EQUAL),MK(BACKSLASH),MK(BACKSPACE),
MK(ESCAPE),MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I),MK(O),
MK(P),MK(GRAVE),MK(BRACKET_LEFT),MK(ENTER),
MK(LEFTCONTROL),MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K),
MK(L),MK(SEMICOLON),MK(APOSTROPHE),MK(BRACKET_RIGHT),MK(INSERT),
MK(LEFTSHIFT),MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M),MK(COMMA),
MK(PERIOD),MK(SLASH),MK(RIGHTALT),MK(RIGHTSHIFT),MK(LEFTALT),MK(SPACE),
MK(BL_DELETE),
MK(BL_END),
MK(BL_PAGEDOWN),
MK(BL_CURSORUP),MK(BL_CURSORLEFT),MK(BL_CURSORRIGHT),MK(BL_CURSORDOWN)
};
ButtConfig suborkbmap[0x60]=
{
MC(0x01),MC(0x3b),MC(0x3c),MC(0x3d),MC(0x3e),MC(0x3f),MC(0x40),MC(0x41),MC(0x42),MC(0x43),
MC(0x44),MC(0x57),MC(0x58),MC(0x45),MC(0x29),MC(0x02),MC(0x03),MC(0x04),MC(0x05),MC(0x06),
MC(0x07),MC(0x08),MC(0x09),MC(0x0a),MC(0x0b),MC(0x0c),MC(0x0d),MC(0x0e),MC(0xd2),MC(0xc7),
MC(0xc9),MC(0xc5),MC(0xb5),MC(0x37),MC(0x4a),MC(0x0f),MC(0x10),MC(0x11),MC(0x12),MC(0x13),
MC(0x14),MC(0x15),MC(0x16),MC(0x17),MC(0x18),MC(0x19),MC(0x1a),MC(0x1b),MC(0x1c),MC(0xd3),
MC(0xca),MC(0xd1),MC(0x47),MC(0x48),MC(0x49),MC(0x4e),MC(0x3a),MC(0x1e),MC(0x1f),MC(0x20),
MC(0x21),MC(0x22),MC(0x23),MC(0x24),MC(0x25),MC(0x26),MC(0x27),MC(0x28),MC(0x4b),MC(0x4c),
MC(0x4d),MC(0x2a),MC(0x2c),MC(0x2d),MC(0x2e),MC(0x2f),MC(0x30),MC(0x31),MC(0x32),MC(0x33),
MC(0x34),MC(0x35),MC(0x2b),MC(0xc8),MC(0x4f),MC(0x50),MC(0x51),MC(0x1d),MC(0x38),MC(0x39),
MC(0xcb),MC(0xd0),MC(0xcd),MC(0x52),MC(0x53)
};
static void UpdateFKB(void)
{
int x;
for(x=0;x<0x48;x++)
{
fkbkeys[x]=0;
if(DTestButton(&fkbmap[x]))
fkbkeys[x]=1;
}
}
static void UpdateSuborKB(void)
{
int x;
for(x=0;x<0x60;x++)
{
suborkbkeys[x]=0;
if(DTestButton(&suborkbmap[x]))
suborkbkeys[x]=1;
}
}
static ButtConfig HyperShotButtons[4]=
{
MK(Q),MK(W),MK(E),MK(R)
};
static void UpdateHyperShot(void)
{
int x;
HyperShotData=0;
for(x=0;x<0x4;x++)
{
if(DTestButton(&HyperShotButtons[x]))
HyperShotData|=1<<x;
}
}
static ButtConfig MahjongButtons[21]=
{
MK(Q),MK(W),MK(E),MK(R),MK(T),
MK(A),MK(S),MK(D),MK(F),MK(G),MK(H),MK(J),MK(K),MK(L),
MK(Z),MK(X),MK(C),MK(V),MK(B),MK(N),MK(M)
};
static void UpdateMahjong(void)
{
int x;
MahjongData=0;
for(x=0;x<21;x++)
{
if(DTestButton(&MahjongButtons[x]))
MahjongData|=1<<x;
}
}
ButtConfig QuizKingButtons[6]=
{
MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y)
};
static void UpdateQuizKing(void)
{
int x;
QuizKingData=0;
for(x=0;x<6;x++)
{
if(DTestButton(&QuizKingButtons[x]))
QuizKingData|=1<<x;
}
}
ButtConfig TopRiderButtons[8]=
{
MK(Q),MK(W),MK(E),MK(R),MK(T),MK(Y),MK(U),MK(I)
};
static void UpdateTopRider(void)
{
int x;
TopRiderData=0;
for(x=0;x<8;x++)
if(DTestButton(&TopRiderButtons[x]))
TopRiderData|=1<<x;
}
ButtConfig FTrainerButtons[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)
};
static void UpdateFTrainer(void)
{
int x;
FTrainerData=0;
for(x=0;x<12;x++)
{
if(DTestButton(&FTrainerButtons[x]))
FTrainerData|=1<<x;
}
}
int DWaitButton(HWND hParent, const uint8 *text, ButtConfig *bc);
int DWaitSimpleButton(HWND hParent, const uint8 *text);
CFGSTRUCT InputConfig[]={
ACA(UsrInputType),
AC(powerpadsc),
AC(QuizKingButtons),
AC(FTrainerButtons),
AC(HyperShotButtons),
AC(MahjongButtons),
AC(GamePadConfig),
AC(fkbmap),
AC(suborkbmap),
ENDCFGSTRUCT
};
void InitInputStuff(void)
{
int x,y;
KeyboardInitialize();
InitJoysticks(hAppWnd);
for(x=0; x<4; x++)
for(y=0; y<10; y++)
JoyClearBC(&GamePadConfig[x][y]);
for(x=0; x<2; x++)
for(y=0; y<12; y++)
JoyClearBC(&powerpadsc[x][y]);
for(x=0; x<0x48; x++)
JoyClearBC(&fkbmap[x]);
for(x=0; x<0x60; x++)
JoyClearBC(&suborkbmap[x]);
for(x=0; x<6; x++)
JoyClearBC(&QuizKingButtons[x]);
for(x=0; x<12; x++)
JoyClearBC(&FTrainerButtons[x]);
for(x=0; x<21; x++)
JoyClearBC(&MahjongButtons[x]);
for(x=0; x<4; x++)
JoyClearBC(&HyperShotButtons[x]);
}
static void FCExp(char *text)
{
static char *fccortab[12]={"none","arkanoid","shadow","4player","fkb","suborkb",
"hypershot","mahjong","quizking","ftrainera","ftrainerb","oekakids"};
static int fccortabi[12]={SIFC_NONE,SIFC_ARKANOID,SIFC_SHADOW,
SIFC_4PLAYER,SIFC_FKB,SIFC_SUBORKB,SIFC_HYPERSHOT,SIFC_MAHJONG,SIFC_QUIZKING,
SIFC_FTRAINERA,SIFC_FTRAINERB,SIFC_OEKAKIDS};
int y;
for(y=0;y<12;y++)
if(!strcmp(fccortab[y],text))
UsrInputType[2]=fccortabi[y];
}
static char *cortab[6]={"none","gamepad","zapper","powerpada","powerpadb","arkanoid"};
static int cortabi[6]={SI_NONE,SI_GAMEPAD,
SI_ZAPPER,SI_POWERPADA,SI_POWERPADB,SI_ARKANOID};
static void Input1(char *text)
{
int y;
for(y=0;y<6;y++)
if(!strcmp(cortab[y],text))
UsrInputType[0]=cortabi[y];
}
static void Input2(char *text)
{
int y;
for(y=0;y<6;y++)
if(!strcmp(cortab[y],text))
UsrInputType[1]=cortabi[y];
}
ARGPSTRUCT InputArgs[]={
{"-fcexp",0,(void *)FCExp,0x2000},
{"-input1",0,(void *)Input1,0x2000},
{"-input2",0,(void *)Input2,0x2000},
{0,0,0,0}
};
static char *MakeButtString(ButtConfig *bc)
{
uint32 x; //mbg merge 7/17/06 changed to uint
char tmpstr[512];
char *astr;
tmpstr[0] = 0;
for(x=0;x<bc->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]={"<none>","Gamepad","Zapper","Power Pad A","Power Pad B","Arkanoid Paddle"};
static const char *strf[14]=
{"<none>","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; i<NUM_DEFAULT_MAPPINGS; ++i)
FCEUD_CommandMapping[DefaultCommandMapping[i].cmd] = DefaultCommandMapping[i].key;
}
int FCEUD_TestCommandState(int c)
{
int cmd=FCEUD_CommandMapping[c];
int cmdmask=cmd&CMD_KEY_MASK;
// allow certain commands be affected by key repeat
if(c == EMUCMD_FRAME_ADVANCE/*
|| c == EMUCMD_SOUND_VOLUME_UP
|| c == EMUCMD_SOUND_VOLUME_DOWN
|| c == EMUCMD_SPEED_SLOWER
|| c == EMUCMD_SPEED_FASTER*/)
{
keys=GetKeyboard();
/* if((cmdmask & CMD_KEY_LALT) == CMD_KEY_LALT
|| (cmdmask & CMD_KEY_RALT) == CMD_KEY_RALT
|| (cmdmask & CMD_KEY_LALT) == CMD_KEY_LALT
|| (cmdmask & CMD_KEY_LCTRL) == CMD_KEY_LCTRL
|| (cmdmask & CMD_KEY_RCTRL) == CMD_KEY_RCTRL
|| (cmdmask & CMD_KEY_LSHIFT) == CMD_KEY_LSHIFT
|| (cmdmask & CMD_KEY_RSHIFT) == CMD_KEY_RSHIFT)*/
keys_nr=GetKeyboard();
// else
// keys_nr=GetKeyboard_nr();
}
else if(c != EMUCMD_SPEED_TURBO) // TODO: this should be made more general by detecting if the command has an "off" function, but right now Turbo is the only command that has it
{
keys=GetKeyboard_jd();
keys_nr=GetKeyboard_nr();
}
else
{
keys=GetKeyboard_nr();
keys_nr=GetKeyboard_nr();
}
/* test CTRL, SHIFT, ALT */
if (cmd & CMD_KEY_ALT)
{
int ctlstate = (cmd & CMD_KEY_LALT) ? keys_nr[SCAN_LEFTALT] : 0;
ctlstate |= (cmd & CMD_KEY_RALT) ? keys_nr[SCAN_RIGHTALT] : 0;
if (!ctlstate)
return 0;
}
else if((cmdmask != SCAN_LEFTALT && keys_nr[SCAN_LEFTALT]) || (cmdmask != SCAN_RIGHTALT && keys_nr[SCAN_RIGHTALT]))
return 0;
if (cmd & CMD_KEY_CTRL)
{
int ctlstate = (cmd & CMD_KEY_LCTRL) ? keys_nr[SCAN_LEFTCONTROL] : 0;
ctlstate |= (cmd & CMD_KEY_RCTRL) ? keys_nr[SCAN_RIGHTCONTROL] : 0;
if (!ctlstate)
return 0;
}
else if((cmdmask != SCAN_LEFTCONTROL && keys_nr[SCAN_LEFTCONTROL]) || (cmdmask != SCAN_RIGHTCONTROL && keys_nr[SCAN_RIGHTCONTROL]))
return 0;
if (cmd & CMD_KEY_SHIFT)
{
int ctlstate = (cmd & CMD_KEY_LSHIFT) ? keys_nr[SCAN_LEFTSHIFT] : 0;
ctlstate |= (cmd & CMD_KEY_RSHIFT) ? keys_nr[SCAN_RIGHTSHIFT] : 0;
if (!ctlstate)
return 0;
}
else if((cmdmask != SCAN_LEFTSHIFT && keys_nr[SCAN_LEFTSHIFT]) || (cmdmask != SCAN_RIGHTSHIFT && keys_nr[SCAN_RIGHTSHIFT]))
return 0;
return keys[cmdmask] ? 1 : 0;
}
static const char* ScanNames[256]=
{
/* 0x00-0x0f */ 0, "Escape", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "Minus", "Equals", "Backspace", "Tab",
/* 0x10-0x1f */ "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "[", "]", "Enter", "Left Ctrl", "A", "S",
/* 0x20-0x2f */ "D", "F", "G", "H", "J", "K", "L", "Semicolon", "Apostrophe", "Grave", "Left Shift", "Backslash", "Z", "X", "C", "V",
/* 0x30-0x3f */ "B", "N", "M", "Comma", "Period", "Slash", "Right Shift", "Numpad *", "Left Alt", "Space", "Caps Lock", "F1", "F2", "F3", "F4", "F5",
/* 0x40-0x4f */ "F6", "F7", "F8", "F9", "F10", "NumLock", "ScrollLock", "Numpad 7", "Numpad 8", "Numpad 9", "Numpad Minus", "Numpad 4", "Numpad 5", "Numpad 6", "Numpad Plus", "Numpad 1",
/* 0x50-0x5f */ "Numpad 2", "Numpad 3", "Numpad 0", "Numpad Period", 0, 0, "Backslash", "F11", "F12", 0, 0, 0, 0, 0, 0, 0,
/* 0x60-0x6f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x70-0x7f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x80-0x8f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0x90-0x9f */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Numpad Enter", "Right Ctrl", 0, 0,
/* 0xa0-0xaf */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xb0-0xbf */ 0, 0, 0, 0, 0, "Numpad Divide", 0, "PrintScrn", "Right Alt", 0, 0, 0, 0, 0, 0, 0,
/* 0xc0-0xcf */ 0, 0, 0, 0, 0, "Pause", 0, "Home", "Up Arrow", "PgUp", 0, "Left Arrow", 0, "Right Arrow", 0, "End",
/* 0xd0-0xdf */ "Down Arrow", "PgDn", "Ins", "Del", 0, 0, 0, 0, 0, 0, 0, "Left Win", "Right Win", "AppMenu", 0, 0,
/* 0xe0-0xef */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 0xf0-0xff */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
static const char* GetKeyName(int code)
{
static char name[16];
code &= 0xff;
if(ScanNames[code])
return ScanNames[code];
sprintf(name, "Key 0x%.2x", code);
return name;
}
static char* GetKeyComboName(int c)
{
static char text[80];
text[0]='\0';
if(!c)
return text;
if ((c & CMD_KEY_CTRL) == CMD_KEY_CTRL) strcat(text, "Ctrl + ");
else if ((c & CMD_KEY_CTRL) == CMD_KEY_LCTRL) strcat(text, "Left Ctrl + ");
else if ((c & CMD_KEY_CTRL) == CMD_KEY_RCTRL) strcat(text, "Right Ctrl + ");
if ((c & CMD_KEY_ALT) == CMD_KEY_ALT) strcat(text, "Alt + ");
else if ((c & CMD_KEY_ALT) == CMD_KEY_LALT) strcat(text, "Left Alt + ");
else if ((c & CMD_KEY_ALT) == CMD_KEY_RALT) strcat(text, "Right Alt + ");
if ((c & CMD_KEY_SHIFT) == CMD_KEY_SHIFT) strcat(text, "Shift + ");
else if ((c & CMD_KEY_SHIFT) == CMD_KEY_LSHIFT) strcat(text, "Left Shift + ");
else if ((c & CMD_KEY_SHIFT) == CMD_KEY_RSHIFT) strcat(text, "Right Shift + ");
strcat(text, GetKeyName(c & CMD_KEY_MASK));
return text;
}
struct INPUTDLGTHREADARGS
{
HANDLE hThreadExit;
HWND hwndDlg;
};
static DWORD WINAPI NewInputDialogThread(LPVOID lpvArg)
{
struct INPUTDLGTHREADARGS* args = (struct INPUTDLGTHREADARGS*)lpvArg;
while (args->hThreadExit)
{
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; i<EMUCMD_MAX-1; ++i)
{
int j;
for(j=i+1; j<EMUCMD_MAX; ++j)
{
if(FCEUD_CommandMapping[i] &&
FCEUD_CommandMapping[i] == FCEUD_CommandMapping[j])
{
ConflictTable[i]=1;
ConflictTable[j]=1;
}
}
}
}
// Populate display.
for(i=0, idx=0; i<EMUCMD_MAX; ++i)
{
if(ShouldDisplayMapping(i, filter))
{
memset(&lvi, 0, sizeof(lvi));
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.iItem = idx;
lvi.iSubItem = 0;
lvi.pszText = (char*)FCEUI_CommandTypeNames[FCEUI_CommandTable[i].type];
lvi.lParam = (LPARAM)i;
SendMessage(hwndListView, LVM_INSERTITEM, (WPARAM)0, (LPARAM)&lvi);
memset(&lvi, 0, sizeof(lvi));
lvi.mask = LVIF_TEXT;
lvi.iItem = idx;
lvi.iSubItem = 1;
lvi.pszText = FCEUI_CommandTable[i].name;
SendMessage(hwndListView, LVM_SETITEM, (WPARAM)0, (LPARAM)&lvi);
memset(&lvi, 0, sizeof(lvi));
lvi.mask = LVIF_TEXT;
lvi.iItem = idx;
lvi.iSubItem = 2;
lvi.pszText = GetKeyComboName(FCEUD_CommandMapping[i]);
SendMessage(hwndListView, LVM_SETITEM, (WPARAM)0, (LPARAM)&lvi);
idx++;
}
}
if(ConflictTable)
free(ConflictTable);
}
static BOOL CALLBACK MapInputDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
HWND hwndListView = GetDlgItem(hwndDlg, 1003);
LVCOLUMN lv;
//LVITEM lvi; //mbg merge 7/17/06 removed
int i;
// Set full row select.
SendMessage(hwndListView, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
// Init ListView columns.
memset(&lv, 0, sizeof(lv));
lv.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lv.fmt = LVCFMT_LEFT;
lv.pszText = "Type";
lv.cx = 40;
SendMessage(hwndListView, LVM_INSERTCOLUMN, (WPARAM)0, (LPARAM)&lv);
memset(&lv, 0, sizeof(lv));
lv.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
lv.fmt = LVCFMT_LEFT;
lv.pszText = "Command";
lv.cx = 180;
SendMessage(hwndListView, LVM_INSERTCOLUMN, (WPARAM)1, (LPARAM)&lv);
memset(&lv, 0, sizeof(lv));
lv.mask = LVCF_FMT | LVCF_TEXT;
lv.fmt = LVCFMT_LEFT;
lv.pszText = "Input";
SendMessage(hwndListView, LVM_INSERTCOLUMN, (WPARAM)2, (LPARAM)&lv);
// Populate the filter combobox.
SendDlgItemMessage(hwndDlg, 300, CB_INSERTSTRING, 0, (LPARAM)"None");
for(i=0; i<EMUCMDTYPE_MAX; ++i)
SendDlgItemMessage(hwndDlg, 300, CB_INSERTSTRING, i+1, (LPARAM)FCEUI_CommandTypeNames[i]);
SendDlgItemMessage(hwndDlg, 300, CB_INSERTSTRING, ++i, (LPARAM)"Assigned");
SendDlgItemMessage(hwndDlg, 300, CB_INSERTSTRING, ++i, (LPARAM)"Unassigned");
SendDlgItemMessage(hwndDlg, 300, CB_INSERTSTRING, ++i, (LPARAM)"Conflicts");
SendDlgItemMessage(hwndDlg, 300, CB_SETCURSEL, 0, 0); // Default filter is "none".
// Now populate the mapping display.
PopulateMappingDisplay(hwndDlg);
// Autosize last column.
SendMessage(hwndListView, LVM_SETCOLUMNWIDTH, (WPARAM)2, MAKELPARAM(LVSCW_AUTOSIZE_USEHEADER, 0));
}
return FALSE;
case WM_COMMAND:
if(HIWORD(wParam) == CBN_SELCHANGE)
{
PopulateMappingDisplay(hwndDlg);
break;
}
switch(LOWORD(wParam))
{
case IDOK:
EndDialog(hwndDlg, 1);
return TRUE;
case IDCANCEL:
EndDialog(hwndDlg, 0);
return TRUE;
case 200:
ApplyDefaultCommandMapping();
PopulateMappingDisplay(hwndDlg);
return TRUE;
default:
break;
}
break;
case WM_NOTIFY:
switch(LOWORD(wParam))
{
case 1003:
if (lParam)
{
HWND hwndListView = GetDlgItem(hwndDlg, 1003);
NMHDR* pnm = (NMHDR*)lParam;
if (pnm->code == 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;
}