/* * snes9express * controller.cc * Copyright (C) 1998-2004 David Nordlund * * 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. * * For further details, please read the included COPYING file, * or go to http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include "frend.h" #include "interface.h" # include # include # include #ifdef HAVE_LINUX_JOYSTICK_H # include #endif static const char*Default_JS_Devices[] = { "/dev/js0", "/dev/js1", "/dev/js2", "/dev/js3" }; static const char*JS_Labels[] = { "Pad 1", "Pad 2", "Pad 3", "Pad 4" }; static const char*devJSargs[] = { "-joydev1", "-joydev2", "-joydev3", "-joydev4" }; static const int DefaultMap[8] = { 1, 0, 4, 3, 6, 7, 8, 9 }; static const char*ButtonLabels[] = { "A", "B", "X", "Y", "L", "R", "Start", "Select" }; static const char*MapArgs[] = { "-joymap1", "-joymap2", "-joymap3", "-joymap4" }; static const char*JS_Skin_Color_Props[] = { "a-btn", "b-btn", "x-btn", "y-btn", "l-btn", "r-btn", "start-btn", "select-btn", "pad-color", "btn-color", "text-color" }; void s9x_Joystick__DeviceEvent(void*JMptr, int fd, GdkInputCondition IC); s9x_Controller::s9x_Controller(fr_Notebook*parent): s9x_Notepage(parent, "Controllers"), DevWindow(this, "Joystick Devices"), MapWindow(this, "Button Maps"), devJS0(this, JS_Labels[0], (char*)0, Default_JS_Devices[0]), devJS1(this, JS_Labels[1], (char*)0, Default_JS_Devices[1]), devJS2(this, JS_Labels[2], (char*)0, Default_JS_Devices[2]), devJS3(this, JS_Labels[3], (char*)0, Default_JS_Devices[3]), DevBtn(this, "Devices ..."), DevClose(this, "Close"), MapBtn(this, "Configure Button Maps..."), MapCloseBtn(this, "Close"), Buttons(this, "Buttons"), Old(this, "Old-style", false), UseJoystick(this, "Use Joystick", true), Swap(this, "Swap", false), MapDiagram(0), detailbox(this), MapButtonBox(this) { int i; SetGridSize(4, 2, true); SetStretch(Grow, Fill); UseJoystick.NotArgs << "-j" << fr_CaseInsensitive << "-nojoy"; UseJoystick.SetTooltip("Read input from joystick(s). (analog joystick polling can slow things down)"); UseJoystick.AddListener(this); Pack(UseJoystick); Swap.Args << fr_CaseInsensitive << "-swapjoypads" << "-sw" << "-s"; Swap.SetKeyStroke("6"); Swap.SetTooltip("Cross Joystick 1 over to Joystick 2 and vice-versa"); Pack(Swap); Old.Args << fr_CaseInsensitive << "-old" << "-o"; Old.SetTooltip("Use old-style joystick emulation"); Pack(Old); fr_MenuItem *MI; MI = new fr_MenuItem(&Buttons, "2 buttons"); MI->SetTooltip("for a 2-button joystick/gamepad"); MI = new fr_MenuItem(&Buttons, "4 buttons"); MI->Args << fr_CaseInsensitive << "-4" << "-four"; MI->SetTooltip("for a 4-button joystick/gamepad"); MI = new fr_MenuItem(&Buttons, "6 buttons"); MI->Args << fr_CaseInsensitive << "-6" << "-six"; MI->SetTooltip("for a 6-button joystick/gamepad"); Pack(Buttons, 3, 0, 4, 1); DevBtn.SetTooltip("Set the joystick devices."); DevBtn.AddListener(this); Pack(DevBtn, 3, 0, 4, 1); DevWindow.SetGridSize(1, S9X_JS_COUNT + 1, false); DevWindow.AddListener(this); devJS[0] = &devJS0; devJS[1] = &devJS1; devJS[2] = &devJS2; devJS[3] = &devJS3; for(int d=0; dAddLabel(JS_Labels[d]); devJS[d]->Args << fr_CaseInsensitive << devJSargs[d]; devJS0.SetTooltip("Select the device to use for this joystick"); DevWindow.Pack(*(devJS[d])); } DevWindow.Pack(DevClose, 2, 1); DevClose.AddListener(this); MapBtn.SetTooltip("Map the buttons for the game controllers"); MapBtn.AddListener(this); SetStretch(Fill, Fill); Pack(MapBtn, 4, 1); MapWindow.SetGridSize(4, 4, false); MapWindow.AddListener(this); /* defaultpadcolors[A_BTN] = fr_Colors(0xff0000,0x800000); defaultpadcolors[B_BTN] = fr_Colors(0xffff00,0x808000); defaultpadcolors[X_BTN] = fr_Colors(0xccccff,0x000080); defaultpadcolors[Y_BTN] = fr_Colors(0x00ff00,0x008000);*/ for(i=A_BTN; idetails); MapCloseBtn.AddListener(this); MapButtonBox.AddButton(MapCloseBtn, true); MapWindow.Pack(detailbox, 0, 1, 4, 3); MapWindow.Pack(MapButtonBox, 2, 3, 4, 4); } s9x_Controller::~s9x_Controller() { int i; for(i=0; iSetToDefault(); Map[i]->SetToDefault(); } } void s9x_Controller::Set9xVersion(float version) { bool cond = (version >= 1.10); Old.EnableIf(!cond); Buttons.EnableIf(!cond); Old.SetVisibility(!cond); Buttons.SetVisibility(!cond); DevBtn.SetVisibility(cond); for(int i=0; iEnableIf(cond); MapBtn.SetEditable(cond); } void s9x_Controller::SiftArgs(fr_ArgList& L) { UseJoystick.SiftArgs(L); Swap.SiftArgs(L); Old.SiftArgs(L); Buttons.SiftArgs(L); for(int i=0; iSiftArgs(L); Map[i]->SiftArgs(L); } } void s9x_Controller::CompileArgs(fr_ArgList& L) { if(!UseJoystick.GetState()) { UseJoystick.CompileArgs(L); return; }; Swap.CompileArgs(L); Old.CompileArgs(L); Buttons.CompileArgs(L); for(int i=0; iCompileArgs(L); Map[i]->CompileArgs(L); } } void s9x_Controller::ApplySkin(s9x_Skin*S) { s9x_Notepage::ApplySkin(S); int i; s9x_SkinSection* section = S?S->getSection("icons"):NULL; s9x_SkinSection* c_section = S?S->getSection("controller"):NULL; fr_Image* icon = section?section->getImage(getTabIconID()):NULL; fr_Image* controller = c_section?c_section->getImage("image"):NULL; MapWindow.setIcon(icon); if(MapDiagram) { MapWindow.Remove(*MapDiagram); delete MapDiagram; MapDiagram = 0; } if(controller) { MapDiagram = new fr_Element(&MapWindow, *controller); MapWindow.Pack(*MapDiagram, 0, 0, 4, 1); } if(c_section) { for(i=A_BTN; igetColors(JS_Skin_Color_Props[i]); if(c.size()) padcolors[i] = c; else padcolors[i] = defaultpadcolors[i]; } } else for(i=A_BTN; iupdateDetails(); } void s9x_Controller::EventOccurred(fr_Event*e) { bool s; int i; if(e->Is(UseJoystick)) { s = UseJoystick.GetState(); Swap.SetEditable(s); Buttons.SetEditable(s); Old.SetEditable(s); DevBtn.SetEditable(s); MapBtn.SetEditable(s); } else if(e->Is(DevBtn)) { DevWindow.SetVisibility(true); } else if(e->Is(DevClose)||e->Is(DevWindow, fr_Destroy)) { DevWindow.SetVisibility(false); } else if(e->Is(MapBtn)) { MapWindow.SetVisibility(true); for(i=0; iOpenDevice(devJS[i]->GetFileName()); } else if(e->Is(MapCloseBtn)||e->Is(MapWindow, fr_Destroy)) { MapWindow.SetVisibility(false); for(i=0; iCloseDevice(); }; } /* ############################# s9x_JoyMap ############################### */ s9x_JoyMap::s9x_JoyMap(s9x_Controller*parent, int j, fr_Box&b): fr_Option(parent, JS_Labels[j]), Index(j), Row(2*j), SelectedBtn(A_BTN), details(this), clr(parent->getColors()) { device_fd = device_id = -1; IsVisible = false; Args << MapArgs[j]; details.setSizeForText(30, 10); b.Pack(details); } /// Set the mapped value for a button void s9x_JoyMap::SetBtn(int btn, int val) { if((btn >= A_BTN) && (btn < GAMEPAD)) buttonmap[btn] = val; } /// @return the mapped value for a button int s9x_JoyMap::GetBtn(int btn) { if((btn >= A_BTN) && (btn < GAMEPAD)) return buttonmap[btn]; return -1; } /// Set the mapped value for the selected button void s9x_JoyMap::SetSelectedBtnVal(int val) { SetBtn(SelectedBtn, val); } /// @return the mapped value for the selected button int s9x_JoyMap::GetSelectedBtnVal() { return GetBtn(SelectedBtn); } /// Reset the button map to the snes9x default map void s9x_JoyMap::SetToDefault() { for(int i=0; i<8; i++) SetBtn(i, DefaultMap[i]); } /// Check to see if the current map is the default map bool s9x_JoyMap::IsDefault() { for(int i=0; i= GAMEPAD) SelectedBtn %= GAMEPAD; if(SelectedBtn < 0) SelectedBtn += GAMEPAD; updateDetails(); } void s9x_JoyMap::updateDetails() { int b = SelectedBtn, i; fr_TextArea& d(details); fr_Colors t_clr(clr[PADTEXT]), c_clr(clr[GAMEPAD]), b_clr(clr[BUTTONPAD]); d.setDefaultColors(t_clr); d.clear(); d << JS_Labels[Index] << ": "; if(jserr.size()) { d << jsdev << "\n" << jserr << "\n"; return; } d << "\"" << jsname << "\"\n"; d << "Press a button for " << clr[b] << " " << ButtonLabels[b] << " "; d << t_clr << "\n" << " "; if(b==L_BTN) d << clr[b] << "( L )" << c_clr; else d << c_clr << "( L )"; d << " "; if(b==R_BTN) d << clr[b] << "( R )" << c_clr; else d << "( R )"; d << t_clr << "\n " << c_clr << " SNES9EXPRESS " << t_clr << "\n "; d << c_clr << " " << b_clr << "/\\" << c_clr << " "; d << b_clr << " "; if(b==X_BTN) d << clr[b] << "(X)" << b_clr << " "; else d << " X "; d << c_clr << " " << t_clr << "\n "; d << c_clr << " " << b_clr << "< >" << c_clr << " "; if(b==Y_BTN) d << clr[b] << "(Y)" << b_clr << " "; else d << b_clr << " Y "; if(b==A_BTN) d << clr[b] << "(A)"; else d << " A "; d << c_clr << " " << t_clr << "\n "; d << c_clr << " " << b_clr << "\\/" << c_clr << " "; if(b==SELECT_BTN) d << clr[b] << "/" << c_clr << " "; else d << "/ "; if(b==START_BTN) d << clr[b] << "/" << c_clr << " "; else d << "/ "; d << b_clr << " "; if(b==B_BTN) d << clr[b] << "(B)" << b_clr << " "; else d << " B "; d << c_clr << " " << t_clr << "\n "; d << " " << c_clr << " " << t_clr << "\n "; d << " " << c_clr << " " << t_clr << " " << c_clr << " " << t_clr << "\n"; d << MapArgs[Index]; for(i=A_BTN; i= 0) { axes = buttons = 2; version = 0x000800; ioctl(device_fd, JSIOCGVERSION, &version); ioctl(device_fd, JSIOCGAXES, &axes); ioctl(device_fd, JSIOCGBUTTONS, &buttons); ioctl(device_fd, JSIOCGNAME(sizeof(JSname)), JSname); v1 = (version >> 16); v2 = (version >> 8) & 0xFF; jsname = JSname; if( v1 < 1 ) // joystick driver too old { if(device_fd >= 0) close(device_fd); device_fd = -1; jserr = "Joystick driver too old."; } else device_id = gdk_input_add(device_fd, GDK_INPUT_READ, s9x_Joystick__DeviceEvent, this); } else jserr = fr_syserr(); #else jserr = "Joystick support not compiled in."; #endif updateDetails(); return device_fd; } void s9x_JoyMap::CloseDevice() { #ifdef S9X_INCLUDE_JOYSTICK if(device_id >= 0) gdk_input_remove(device_id); if(device_fd >= 0) close(device_fd); #endif device_fd = device_id = -1; } void s9x_Joystick__DeviceEvent(void*JMptr, int fd, GdkInputCondition IC) { #ifdef S9X_INCLUDE_JOYSTICK static __u32 last_time = 0; struct js_event js; int size, dir; s9x_JoyMap*JM; if (IC != GDK_INPUT_READ) return; JM = (s9x_JoyMap*)JMptr; size = sizeof(struct js_event); if (read(fd, &js, size) != size) return; switch(js.type) { case JS_EVENT_AXIS: if(js.time - last_time < 80) //enforce a delay js.value = 0; last_time = js.time; js.value /= (1<<14); if(js.value) dir = js.value / abs(js.value); else dir = 0; if(js.number == 0) JM->moveSelector(dir); break; case JS_EVENT_BUTTON: if(js.value) // pressed JM->SetSelectedBtnVal(js.number); else // released JM->moveSelector(1); break; }; #endif }