diff --git a/src/drivers/sdl/gui.cpp b/src/drivers/sdl/gui.cpp index b5e64e54..3327a9f8 100644 --- a/src/drivers/sdl/gui.cpp +++ b/src/drivers/sdl/gui.cpp @@ -35,35 +35,32 @@ extern Config *g_config; GtkWidget* MainWindow = NULL; GtkWidget* socket = NULL; -GtkWidget* padNoCombo; +GtkWidget* padNoCombo = NULL; +GtkWidget* configNoCombo = NULL; +GtkWidget* buttonMappings[10]; // This function configures a single button on a gamepad int configGamepadButton(GtkButton* button, gpointer p) { gint x = ((gint)(glong)(p)); //gint x = GPOINTER_TO_INT(p); - int padNo = 0; - char* padStr = gtk_combo_box_get_active_text(GTK_COMBO_BOX(padNoCombo)); - - if(!strcmp(padStr, "1")) - padNo = 0; - if(!strcmp(padStr, "2")) - padNo = 1; - if(!strcmp(padStr, "3")) - padNo = 2; - if(!strcmp(padStr, "4")) - padNo = 3; + int padNo = atoi(gtk_combo_box_get_active_text(GTK_COMBO_BOX(padNoCombo))) - 1; + int configNo = atoi(gtk_combo_box_get_active_text(GTK_COMBO_BOX(configNoCombo))) - 1; char buf[256]; std::string prefix; + // only configure when the "Change" button is pressed in, not when it is unpressed + if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) + return 0; + ButtonConfigBegin(); - snprintf(buf, 256, "SDL.Input.GamePad.%d", padNo); + snprintf(buf, sizeof(buf), "SDL.Input.GamePad.%d", padNo); prefix = buf; - ConfigButton((char*)GamePadNames[x], &GamePadConfig[padNo][x]); + DWaitButton((const uint8*)"Press a button", &GamePadConfig[padNo][x], configNo); - g_config->setOption(prefix + GamePadNames[x], GamePadConfig[padNo][x].ButtonNum[0]); + g_config->setOption(prefix + GamePadNames[x], GamePadConfig[padNo][x].ButtonNum[configNo]); if(GamePadConfig[padNo][x].ButtType[0] == BUTTC_KEYBOARD) { @@ -73,10 +70,16 @@ int configGamepadButton(GtkButton* button, gpointer p) } else { g_config->setOption(prefix + "DeviceType", "Unknown"); } - g_config->setOption(prefix + "DeviceNum", GamePadConfig[padNo][0].DeviceNum[0]); + g_config->setOption(prefix + "DeviceNum", GamePadConfig[padNo][x].DeviceNum[configNo]); + g_config->save(); + + snprintf(buf, sizeof(buf), "%s", ButtonName(&GamePadConfig[padNo][x], configNo)); + gtk_label_set_markup(GTK_LABEL(buttonMappings[x]), buf); ButtonConfigEnd(); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), FALSE); + return 0; } @@ -435,6 +438,38 @@ int setInputDevice(GtkWidget* w, gpointer p) return 1; } +gboolean closeGamepadConfig(GtkWidget* w, GdkEvent* event, gpointer p) +{ + gint paused = ((gint)(glong)(p)); + if(!paused) + FCEUI_SetEmulationPaused(0); + gtk_widget_destroy(w); + return FALSE; +} + +void updateGamepadConfig(GtkWidget* w, gpointer p) +{ + int i; + char strBuf[128]; + int padNo = atoi(gtk_combo_box_get_active_text(GTK_COMBO_BOX(padNoCombo))) - 1; + int configNo = atoi(gtk_combo_box_get_active_text(GTK_COMBO_BOX(configNoCombo))) - 1; + + for(i=0; i<10; i++) + { + GtkWidget* mappedKey = buttonMappings[i]; + if(GamePadConfig[padNo][i].ButtType[configNo] == BUTTC_KEYBOARD) + { + snprintf(strBuf, sizeof(strBuf), "%s", + SDL_GetKeyName((SDLKey)GamePadConfig[padNo][i].ButtonNum[configNo])); + } + else // FIXME: display joystick button/hat/axis names properly + strncpy(strBuf, "Joystick", sizeof(strBuf)); + + gtk_label_set_text(GTK_LABEL(mappedKey), strBuf); + gtk_label_set_use_markup(GTK_LABEL(mappedKey), TRUE); + } +} + // creates and opens the gamepad config window void openGamepadConfig() { @@ -442,17 +477,29 @@ void openGamepadConfig() GtkWidget* vbox; GtkWidget* hboxPadNo; GtkWidget* padNoLabel; + GtkWidget* configNoLabel; GtkWidget* fourScoreChk; + GtkWidget* oppositeDirChk; + GtkWidget* buttonFrame; + GtkWidget* buttonTable; - GtkWidget* buttons[10]; + win = gtk_dialog_new_with_buttons("Controller Configuration", + GTK_WINDOW(MainWindow), + (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_CLOSE, + GTK_RESPONSE_OK, + NULL); + gtk_window_set_title(GTK_WINDOW(win), "Controller Configuration"); + gtk_widget_set_size_request(win, 350, 500); - win = gtk_window_new(GTK_WINDOW_TOPLEVEL); - gtk_window_set_title(GTK_WINDOW(win), "Gamepad Config"); - gtk_widget_set_size_request(win, 250, 500); - vbox = gtk_vbox_new(TRUE, 4); - hboxPadNo = gtk_hbox_new(FALSE, 5); - padNoLabel = gtk_label_new("Gamepad Number:"); - fourScoreChk = gtk_check_button_new_with_label("Enable four score"); + vbox = gtk_dialog_get_content_area(GTK_DIALOG(win)); + gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE); + + hboxPadNo = gtk_hbox_new(FALSE, 0); + padNoLabel = gtk_label_new("Port:"); + configNoLabel = gtk_label_new("Config Number:"); + fourScoreChk = gtk_check_button_new_with_label("Enable Four Score"); + oppositeDirChk = gtk_check_button_new_with_label("Allow Up+Down / Left+Right"); typeCombo = gtk_combo_box_new_text(); gtk_combo_box_append_text(GTK_COMBO_BOX(typeCombo), "gamepad"); @@ -469,8 +516,17 @@ void openGamepadConfig() gtk_combo_box_append_text(GTK_COMBO_BOX(padNoCombo), "2"); gtk_combo_box_append_text(GTK_COMBO_BOX(padNoCombo), "3"); gtk_combo_box_append_text(GTK_COMBO_BOX(padNoCombo), "4"); - gtk_combo_box_set_active(GTK_COMBO_BOX(padNoCombo), 0); + g_signal_connect(GTK_OBJECT(padNoCombo), "changed", G_CALLBACK(updateGamepadConfig), NULL); + + configNoCombo = gtk_combo_box_new_text(); + gtk_combo_box_append_text(GTK_COMBO_BOX(configNoCombo), "1"); + gtk_combo_box_append_text(GTK_COMBO_BOX(configNoCombo), "2"); + gtk_combo_box_append_text(GTK_COMBO_BOX(configNoCombo), "3"); + gtk_combo_box_append_text(GTK_COMBO_BOX(configNoCombo), "4"); + gtk_combo_box_set_active(GTK_COMBO_BOX(configNoCombo), 0); + g_signal_connect(GTK_OBJECT(padNoCombo), "changed", G_CALLBACK(updateGamepadConfig), NULL); + g_signal_connect(GTK_OBJECT(typeCombo), "changed", G_CALLBACK(setInputDevice), gtk_combo_box_get_active_text(GTK_COMBO_BOX(typeCombo))); @@ -486,20 +542,56 @@ void openGamepadConfig() g_signal_connect(GTK_OBJECT(fourScoreChk), "clicked", G_CALLBACK(toggleOption), (gpointer)"SDL.FourScore"); gtk_box_pack_start(GTK_BOX(hboxPadNo), padNoLabel, TRUE, TRUE, 5); - gtk_box_pack_start(GTK_BOX(hboxPadNo), padNoCombo, TRUE, FALSE, 5); - gtk_box_pack_start(GTK_BOX(vbox), hboxPadNo, TRUE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(hboxPadNo), padNoCombo, TRUE, TRUE, 5); + //gtk_box_pack_start(GTK_BOX(hboxPadNo), configNoLabel, TRUE, TRUE, 5); + //gtk_box_pack_start(GTK_BOX(hboxPadNo), configNoCombo, TRUE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(vbox), hboxPadNo, FALSE, TRUE, 5); //gtk_box_pack_start_defaults(GTK_BOX(vbox), typeCombo); - gtk_box_pack_start(GTK_BOX(vbox), fourScoreChk, TRUE, TRUE, 5); + gtk_box_pack_start(GTK_BOX(vbox), fourScoreChk, FALSE, TRUE, 5); + //gtk_box_pack_start(GTK_BOX(vbox), oppositeDirChk, FALSE, TRUE, 5); + + // create gamepad buttons + buttonFrame = gtk_frame_new("Buttons"); + gtk_label_set_use_markup(GTK_LABEL(gtk_frame_get_label_widget(GTK_FRAME(buttonFrame))), TRUE); + buttonTable = gtk_table_new(10, 3, FALSE); + gtk_table_set_col_spacings(GTK_TABLE(buttonTable), 5); + gtk_container_add(GTK_CONTAINER(buttonFrame), buttonTable); for(int i=0; i<10; i++) { - buttons[i] = gtk_button_new_with_label(GamePadNames[i]); - gtk_box_pack_start(GTK_BOX(vbox), buttons[i], TRUE, TRUE, 3); - gtk_signal_connect(GTK_OBJECT(buttons[i]), "clicked", G_CALLBACK(configGamepadButton), GINT_TO_POINTER(i)); + GtkWidget* buttonName = gtk_label_new(GamePadNames[i]); + GtkWidget* mappedKey = gtk_label_new(NULL); + GtkWidget* changeButton = gtk_toggle_button_new(); + char strBuf[128]; + + sprintf(strBuf, "%s:", GamePadNames[i]); + gtk_label_set_text(GTK_LABEL(buttonName), strBuf); + gtk_misc_set_alignment(GTK_MISC(buttonName), 1.0, 0.5); + + gtk_misc_set_alignment(GTK_MISC(mappedKey), 0.0, 0.5); + + gtk_button_set_label(GTK_BUTTON(changeButton), "Change"); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(changeButton), FALSE); + + gtk_table_attach(GTK_TABLE(buttonTable), buttonName, 0, 1, i, i+1, GTK_FILL, GTK_FILL, 0, 0); + gtk_table_attach(GTK_TABLE(buttonTable), mappedKey, 1, 2, i, i+1, + (GtkAttachOptions)(GTK_EXPAND|GTK_FILL), (GtkAttachOptions)(GTK_EXPAND|GTK_FILL), 0, 0); + gtk_table_attach(GTK_TABLE(buttonTable), changeButton, 2, 3, i, i+1, + (GtkAttachOptions)0, (GtkAttachOptions)0, 0, 0); + + gtk_signal_connect(GTK_OBJECT(changeButton), "clicked", G_CALLBACK(configGamepadButton), GINT_TO_POINTER(i)); + buttonMappings[i] = mappedKey; } - gtk_container_add(GTK_CONTAINER(win), vbox); + // display the button mappings for the currently selected configuration + updateGamepadConfig(NULL, NULL); + + gtk_box_pack_start(GTK_BOX(vbox), buttonFrame, TRUE, TRUE, 5); + + g_signal_connect(GTK_OBJECT(win), "delete-event", G_CALLBACK(closeGamepadConfig), GINT_TO_POINTER(FCEUI_EmulationPaused())); + g_signal_connect(GTK_OBJECT(win), "response", G_CALLBACK(closeGamepadConfig), GINT_TO_POINTER(FCEUI_EmulationPaused())); + FCEUI_SetEmulationPaused(1); gtk_widget_show_all(win); @@ -916,6 +1008,7 @@ void openSoundConfig() bufferHscale = gtk_hscale_new_with_range(15, 200, 2); bufferLbl = gtk_label_new("Buffer size (in ms)"); + // sync widget with cfg g_config->getOption("SDL.Sound.BufSize", &cfgBuf); gtk_range_set_value(GTK_RANGE(bufferHscale), cfgBuf); @@ -1597,13 +1690,6 @@ gint convertKeypress(GtkWidget *grab, GdkEventKey *event, gpointer user_data) SDL_Event sdlev; SDLKey sdlkey; int keystate; - - // Only grab keys from the main window. - if (grab != MainWindow) - { - // Don't push this key onto the SDL event stack. - return FALSE; - } switch (event->type) { diff --git a/src/drivers/sdl/input.cpp b/src/drivers/sdl/input.cpp index 8878e0a5..2814cfb7 100644 --- a/src/drivers/sdl/input.cpp +++ b/src/drivers/sdl/input.cpp @@ -40,7 +40,10 @@ #endif #ifdef _GTK_LITE -#include +#include "gui.h" +#ifdef SDL_VIDEO_DRIVER_X11 +#include +#endif #endif @@ -762,16 +765,34 @@ ButtonConfigBegin() // reactivate the video subsystem if(!SDL_WasInit(SDL_INIT_VIDEO)) { - if(SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) { + if(!bcpv) { + InitVideo(GameInfo); + } + else { +#if defined(_GTK) && defined(SDL_VIDEO_DRIVER_X11) + int noGui; + g_config->getOption("SDL.NoGUI", &noGui); + if(noGui == 0) + { + while (gtk_events_pending()) + gtk_main_iteration_do(FALSE); + + char SDL_windowhack[128]; + sprintf(SDL_windowhack, "SDL_WINDOWID=%u", (unsigned int)GDK_WINDOW_XWINDOW(gtk_widget_get_window(socket))); + SDL_putenv(SDL_windowhack); + } +#endif + if(SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) { FCEUD_Message(SDL_GetError()); return(0); + } + + // set the screen and notify the user of button configuration + screen = SDL_SetVideoMode(420, 200, 8, 0); + SDL_WM_SetCaption("Button Config",0); } } - // set the screen and notify the user of button configuration - screen = SDL_SetVideoMode(420, 200, 8, 0); - SDL_WM_SetCaption("Button Config",0); - // XXX soules - why did we shut this down? // initialize the joystick subsystem InitJoysticks(); @@ -791,12 +812,12 @@ ButtonConfigEnd() // shutdown the joystick and video subsystems KillJoysticks(); - SDL_QuitSubSystem(SDL_INIT_VIDEO); + //SDL_QuitSubSystem(SDL_INIT_VIDEO); // re-initialize joystick and video subsystems if they were active before - if(!bcpv) { + /*if(!bcpv) { InitVideo(GameInfo); - } + }*/ if(!bcpj) { InitJoysticks(); } @@ -1259,11 +1280,68 @@ UpdateFTrainer() } } +/** + * Get the display name of the key or joystick button mapped to a specific + * NES gamepad button. + * @param bc the NES gamepad's button config + * @param which the index of the button + */ +const char* +ButtonName(const ButtConfig* bc, int which) +{ + static char name[256]; + + switch(bc->ButtType[which]) + { + case BUTTC_KEYBOARD: + return SDL_GetKeyName((SDLKey)bc->ButtonNum[which]); + case BUTTC_JOYSTICK: + int joyNum, inputNum; + const char* inputType, *inputDirection; + + joyNum = bc->DeviceNum[which]; + + if(bc->ButtonNum[which] & 0x8000) + { + inputType = "Axis"; + inputNum = bc->ButtonNum[which] & 0x3FFF; + inputDirection = bc->ButtonNum[which] & 0x4000 ? "-" : "+"; + } + else if(bc->ButtonNum[which] & 0x2000) + { + int inputValue; + char direction[128] = ""; + + inputType = "Hat"; + inputNum = (bc->ButtonNum[which] >> 8) & 0x1F; + inputValue = bc->ButtonNum[which] & 0xF; + + if(inputValue & SDL_HAT_UP) strncat(direction, "Up ", sizeof(direction)); + if(inputValue & SDL_HAT_DOWN) strncat(direction, "Down ", sizeof(direction)); + if(inputValue & SDL_HAT_LEFT) strncat(direction, "Left ", sizeof(direction)); + if(inputValue & SDL_HAT_RIGHT) strncat(direction, "Right ", sizeof(direction)); + + if(direction[0]) + inputDirection = direction; + else + inputDirection = "Center"; + } + else + { + inputType = "Button"; + inputNum = bc->ButtonNum[which]; + inputDirection = ""; + } + } + + return name; +} + /** * Waits for a button input and returns the information as to which * button was pressed. Used in button configuration. */ -static int +int DWaitButton(const uint8 *text, ButtConfig *bc, int wb) @@ -1281,44 +1359,60 @@ DWaitButton(const uint8 *text, } } - while(SDL_WaitEvent(&event)) { - switch(event.type) { - case SDL_KEYDOWN: - bc->ButtType[wb] = BUTTC_KEYBOARD; - bc->DeviceNum[wb] = 0; - bc->ButtonNum[wb] = event.key.keysym.sym; - return(1); - case SDL_JOYBUTTONDOWN: - bc->ButtType[wb] = BUTTC_JOYSTICK; - bc->DeviceNum[wb] = event.jbutton.which; - bc->ButtonNum[wb] = event.jbutton.button; - return(1); - case SDL_JOYHATMOTION: - if(event.jhat.value != SDL_HAT_CENTERED) { - bc->ButtType[wb] = BUTTC_JOYSTICK; - bc->DeviceNum[wb] = event.jhat.which; - bc->ButtonNum[wb] = (0x2000 | ((event.jhat.hat & 0x1F) << 8) | - event.jhat.value); + while(1) { + int done = 0; +#ifdef _GTK + while(gtk_events_pending()) + gtk_main_iteration_do(FALSE); +#endif + while(SDL_PollEvent(&event)) { + done++; + switch(event.type) { + case SDL_KEYDOWN: + bc->ButtType[wb] = BUTTC_KEYBOARD; + bc->DeviceNum[wb] = 0; + bc->ButtonNum[wb] = event.key.keysym.sym; 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; - } - } else { - if(abs(LastAx[event.jaxis.which][event.jaxis.axis] - event.jaxis.value) >= 8192) { + case SDL_JOYBUTTONDOWN: + bc->ButtType[wb] = BUTTC_JOYSTICK; + bc->DeviceNum[wb] = event.jbutton.which; + bc->ButtonNum[wb] = event.jbutton.button; + return(1); + case SDL_JOYHATMOTION: + if(event.jhat.value == SDL_HAT_CENTERED) + done--; + else { bc->ButtType[wb] = BUTTC_JOYSTICK; - bc->DeviceNum[wb] = event.jaxis.which; - bc->ButtonNum[wb] = (0x8000 | event.jaxis.axis | - ((event.jaxis.value < 0) - ? 0x4000 : 0)); + bc->DeviceNum[wb] = event.jhat.which; + bc->ButtonNum[wb] = (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[wb] = BUTTC_JOYSTICK; + bc->DeviceNum[wb] = event.jaxis.which; + bc->ButtonNum[wb] = (0x8000 | event.jaxis.axis | + ((event.jaxis.value < 0) + ? 0x4000 : 0)); + return(1); + } + else + done--; + } + break; + default: + done--; } - break; } + if(done) break; } return(0); diff --git a/src/drivers/sdl/input.h b/src/drivers/sdl/input.h index 5dd5a128..abe5ddf5 100644 --- a/src/drivers/sdl/input.h +++ b/src/drivers/sdl/input.h @@ -19,6 +19,7 @@ void setHotKeys(); int ButtonConfigBegin(); void ButtonConfigEnd(); void ConfigButton(char *text, ButtConfig *bc); +int DWaitButton(const uint8 *text, ButtConfig *bc, int wb); #define BUTTC_KEYBOARD 0x00 #define BUTTC_JOYSTICK 0x01 @@ -49,5 +50,6 @@ void UpdateInput(Config *config); void InputCfg(const std::string &); std::string GetUserText(const char* title); +const char* ButtonName(const ButtConfig* bc, int which); #endif