From 69effddb777c2009d32f70e3315d5570973446ef Mon Sep 17 00:00:00 2001 From: Shamus Hammons Date: Fri, 3 May 2013 22:53:35 -0500 Subject: [PATCH] Fixed updated joystick handling, first major stab at gamepad profiles. There was a problem in JERRY's read byte routine, it was getting bad data as a result of the conversion of the joystick read routines to word data size only. Apparently some programs (like Rayman) read JOYSTICK a byte at a time. :-P Also, this marks the first stable commit of the the new gamepad profile system. There's still work to do, but this should make it much nicer for people who use VJ with multiple input devices. --- src/gui/app.cpp | 2 + src/gui/configdialog.cpp | 37 +++-- src/gui/configdialog.h | 2 +- src/gui/controllertab.cpp | 179 +++++++++++++++++++-- src/gui/controllertab.h | 23 ++- src/gui/controllerwidget.cpp | 3 + src/gui/controllerwidget.h | 3 + src/gui/gamepad.cpp | 13 +- src/gui/mainwin.cpp | 17 +- src/gui/mainwin.h | 1 + src/gui/profile.cpp | 290 +++++++++++++++++++++++++++++++++++ src/gui/profile.h | 41 +++++ src/jerry.cpp | 14 +- src/joystick.cpp | 6 +- virtualjaguar.pro | 2 + virtualjaguar_fr.ts | 126 +++++++-------- virtualjaguar_gr.ts | 126 +++++++-------- 17 files changed, 725 insertions(+), 160 deletions(-) create mode 100644 src/gui/profile.cpp create mode 100644 src/gui/profile.h diff --git a/src/gui/app.cpp b/src/gui/app.cpp index a951e64..bbe1333 100644 --- a/src/gui/app.cpp +++ b/src/gui/app.cpp @@ -22,6 +22,7 @@ #include "gamepad.h" #include "log.h" #include "mainwin.h" +#include "profile.h" #include "settings.h" #include "version.h" @@ -108,6 +109,7 @@ int main(int argc, char * argv[]) WriteLog("VJ: SDL (joystick, audio) successfully initialized.\n"); App app(argc, argv); // Declare an instance of the application Gamepad::AllocateJoysticks(); + AutoConnectProfiles(); retVal = app.exec(); // And run it! Gamepad::DeallocateJoysticks(); diff --git a/src/gui/configdialog.cpp b/src/gui/configdialog.cpp index 1c89367..752713b 100644 --- a/src/gui/configdialog.cpp +++ b/src/gui/configdialog.cpp @@ -22,22 +22,28 @@ #include "settings.h" -ConfigDialog::ConfigDialog(QWidget * parent/*= 0*/): QDialog(parent) +ConfigDialog::ConfigDialog(QWidget * parent/*= 0*/): QDialog(parent), + tabWidget(new QTabWidget), + generalTab(new GeneralTab(this)), + controllerTab1(new ControllerTab(this)) { - tabWidget = new QTabWidget; - generalTab = new GeneralTab(this); - controllerTab1 = new ControllerTab(this); - controllerTab2 = new ControllerTab(this); +// tabWidget = new QTabWidget; +// generalTab = new GeneralTab(this); +// controllerTab1 = new ControllerTab(this); +//// controllerTab2 = new ControllerTab(this); - if (vjs.hardwareTypeAlpine) - alpineTab = new AlpineTab(this); +// if (vjs.hardwareTypeAlpine) +// alpineTab = new AlpineTab(this); tabWidget->addTab(generalTab, tr("General")); - tabWidget->addTab(controllerTab1, tr("Controller #1")); - tabWidget->addTab(controllerTab2, tr("Controller #2")); + tabWidget->addTab(controllerTab1, tr("Controllers")); +// tabWidget->addTab(controllerTab2, tr("Controller #2")); if (vjs.hardwareTypeAlpine) + { + alpineTab = new AlpineTab(this); tabWidget->addTab(alpineTab, tr("Alpine")); + } buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); @@ -50,10 +56,7 @@ ConfigDialog::ConfigDialog(QWidget * parent/*= 0*/): QDialog(parent) setLayout(mainLayout); setWindowTitle(tr("Virtual Jaguar Settings")); - LoadDialogFromSettings(); -// controllerTab1->UpdateLabel(); // Now it's safe to do this... ;-) -// controllerTab2->UpdateLabel(); // Now it's safe to do this... ;-) } @@ -82,11 +85,15 @@ void ConfigDialog::LoadDialogFromSettings(void) alpineTab->writeROM->setChecked(vjs.allowWritesToROM); } +#warning "!!! Need to load settings from controller profile !!!" +#if 0 for(int i=0; i<21; i++) { +// We need to find the right profile and load it up here... controllerTab1->controllerWidget->keys[i] = vjs.p1KeyBindings[i]; - controllerTab2->controllerWidget->keys[i] = vjs.p2KeyBindings[i]; +// controllerTab2->controllerWidget->keys[i] = vjs.p2KeyBindings[i]; } +#endif } @@ -112,10 +119,12 @@ void ConfigDialog::UpdateVJSettings(void) vjs.allowWritesToROM = alpineTab->writeROM->isChecked(); } +#warning "!!! Need to save settings to controller profile !!!" for(int i=0; i<21; i++) { +// We need to find the right profile and load it up here... vjs.p1KeyBindings[i] = controllerTab1->controllerWidget->keys[i]; - vjs.p2KeyBindings[i] = controllerTab2->controllerWidget->keys[i]; +// vjs.p2KeyBindings[i] = controllerTab2->controllerWidget->keys[i]; } } diff --git a/src/gui/configdialog.h b/src/gui/configdialog.h index 532c47f..295ebdf 100644 --- a/src/gui/configdialog.h +++ b/src/gui/configdialog.h @@ -34,7 +34,7 @@ class ConfigDialog: public QDialog public: GeneralTab * generalTab; ControllerTab * controllerTab1; - ControllerTab * controllerTab2; +// ControllerTab * controllerTab2; AlpineTab * alpineTab; }; diff --git a/src/gui/controllertab.cpp b/src/gui/controllertab.cpp index a5b5328..b9145f6 100644 --- a/src/gui/controllertab.cpp +++ b/src/gui/controllertab.cpp @@ -19,19 +19,46 @@ #include "gamepad.h" #include "joystick.h" #include "keygrabber.h" +#include "profile.h" ControllerTab::ControllerTab(QWidget * parent/*= 0*/): QWidget(parent), - label(new QLabel(tr("Controller:"))), - profileList(new QComboBox(this)), + label1(new QLabel(tr("Host Device:"))), + label2(new QLabel(tr("Map Name:"))), + label3(new QLabel(tr("Maps to:"))), + deviceList(new QComboBox(this)), + mapNameList(new QComboBox(this)), + controller1(new QCheckBox(tr("Jaguar Controller #1"))), + controller2(new QCheckBox(tr("Jaguar Controller #2"))), + addMapName(new QPushButton(tr("+"))), + deleteMapName(new QPushButton(tr("-"))), redefineAll(new QPushButton(tr("Define All Inputs"))), controllerWidget(new ControllerWidget(this)) { +// mapNameList->setEditable(true); + QVBoxLayout * layout = new QVBoxLayout; QHBoxLayout * top = new QHBoxLayout; + QVBoxLayout * left = new QVBoxLayout; + QVBoxLayout * right = new QVBoxLayout; + QHBoxLayout * middle = new QHBoxLayout; + top->addLayout(left, 0); + top->addLayout(right, 1); layout->addLayout(top); - top->addWidget(label); - top->addWidget(profileList, 0, Qt::AlignLeft); + left->addWidget(label1, 0, Qt::AlignRight); + left->addWidget(label2, 0, Qt::AlignRight); + left->addWidget(label3, 0, Qt::AlignRight); + left->addWidget(new QLabel); + right->addWidget(deviceList); + + right->addLayout(middle); + middle->addWidget(mapNameList, 1); + middle->addWidget(addMapName, 0); + middle->addWidget(deleteMapName, 0); +// right->addWidget(mapNameList); + + right->addWidget(controller1); + right->addWidget(controller2); layout->addWidget(controllerWidget); layout->addWidget(redefineAll, 0, Qt::AlignHCenter); setLayout(layout); @@ -40,15 +67,58 @@ ControllerTab::ControllerTab(QWidget * parent/*= 0*/): QWidget(parent), setFixedWidth(sizeHint().width()); connect(redefineAll, SIGNAL(clicked()), this, SLOT(DefineAllKeys())); - connect(profileList, SIGNAL(currentIndexChanged(int)), this, SLOT(ChangeProfile(int))); + connect(deviceList, SIGNAL(currentIndexChanged(int)), this, SLOT(ChangeDevice(int))); + connect(mapNameList, SIGNAL(currentIndexChanged(int)), this, SLOT(ChangeMapName(int))); + connect(addMapName, SIGNAL(clicked()), this, SLOT(AddMapName())); + connect(deleteMapName, SIGNAL(clicked()), this, SLOT(DeleteMapName())); + connect(controllerWidget, SIGNAL(KeyDefined(int, uint32_t)), this, SLOT(UpdateProfileKeys(int, uint32_t))); + connect(controller1, SIGNAL(clicked()), this, SLOT(UpdateProfileConnections())); + connect(controller2, SIGNAL(clicked()), this, SLOT(UpdateProfileConnections())); - // Set up the profile combobox (Keyboard is the default, and always + // Set up the device combobox (Keyboard is the default, and always // present) - profileList->addItem(tr("Keyboard")); + deviceList->addItem(tr("Keyboard"), 0); + // Set up map name combobox (Default is default, and always present) +// mapNameList->addItem(tr("Default")); for(int i=0; iaddItem(Gamepad::GetJoystickName(i)); + { + int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i)); + deviceList->addItem(Gamepad::GetJoystickName(i), deviceNum); + } } +/* +So now we come to implementation. When changing devices, could have a helper function +in profile.cpp that fills the mapNameList combobox with the appropriate names/profile +numbers. + +There needs to be some way of getting data from the ControllerWidget and the current +profile. + +Gamepad will have to have some way of knowing which profile is mapped to which +Jaguar controllers and filtering out everything else. + +Will have to have some intelligent handling of profiles when first run, to see first +what is connected and second, to assign profiles to Jaguar controllers. In this +case, keyboard is the lowest priority--if a controller is plugged in and assigned to +the same Jaguar controller as a keyboard, the controller is used. Not sure what to +do in the case of multiple controllers plugged in and assigned to the same Jaguar +controller. + +Also, need a way to load/save profiles. + +Meaning of checkboxes: None checked == profile not used. +1 checked == prefer connection to Jaguar controller X. +2 checked == no preference, use any available. + +Single mapping cannot be deleted ("-" will be disabled). Can always add, up to the max +limit of profiles (MAX_PROFILES). + +------------------------------ + +Now the main window passes in/removes the last edited profile #. From here, when starting +up, we need to pull that number from the profile store and populate all our boxes. +*/ ControllerTab::~ControllerTab() @@ -56,6 +126,19 @@ ControllerTab::~ControllerTab() } +void ControllerTab::SetupLastUsedProfile(void) +{ + int deviceNum = deviceList->findData(profile[profileNum].device); + int mapNum = mapNameList->findText(profile[profileNum].mapName); + + if (deviceNum == -1 || mapNum == -1) + return; + + ChangeDevice(deviceNum); + ChangeMapName(mapNum); +} + + void ControllerTab::DefineAllKeys(void) { // char jagButtonName[21][10] = { "Up", "Down", "Left", "Right", @@ -66,7 +149,6 @@ void ControllerTab::DefineAllKeys(void) for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++) { -// keyGrab.SetText(jagButtonName[orderToDefine[i]]); keyGrab.SetKeyText(orderToDefine[i]); keyGrab.exec(); int key = keyGrab.key; @@ -77,16 +159,60 @@ void ControllerTab::DefineAllKeys(void) // Otherwise, populate the appropriate spot in the settings & update screen... controllerWidget->keys[orderToDefine[i]] = key; controllerWidget->update(); + profile[profileNum].map[orderToDefine[i]] = key; } } -void ControllerTab::ChangeProfile(int profile) +void ControllerTab::UpdateProfileKeys(int mapPosition, uint32_t key) { -printf("You selected profile: %s\n", (profile == 0 ? "Keyboard" : Gamepad::GetJoystickName(profile - 1))); + profile[profileNum].map[mapPosition] = key; } -#if 0 + +void ControllerTab::UpdateProfileConnections(void) +{ + profile[profileNum].preferredController = (controller1->isChecked() ? CONTROLLER1 : 0) | (controller2->isChecked() ? CONTROLLER2 : 0); +} + + +void ControllerTab::ChangeDevice(int selection) +{ + int deviceNum = deviceList->itemData(selection).toInt(); + mapNameList->clear(); + int numberOfMappings = FindMappingsForDevice(deviceNum, mapNameList); + deleteMapName->setDisabled(numberOfMappings == 1 ? true : false); +//printf("Found %i mappings for device #%u...\n", numberOfMappings, deviceNum); +} + + +void ControllerTab::ChangeMapName(int selection) +{ + profileNum = mapNameList->itemData(selection).toInt(); +//printf("You selected mapping: %s (profile #%u)\n", (mapNameList->itemText(selection)).toAscii().data(), profileNum); + + for(int i=BUTTON_FIRST; i<=BUTTON_LAST; i++) + controllerWidget->keys[i] = profile[profileNum].map[i]; + + controllerWidget->update(); + controller1->setChecked(profile[profileNum].preferredController & CONTROLLER1); + controller2->setChecked(profile[profileNum].preferredController & CONTROLLER2); +} + + +void ControllerTab::AddMapName(void) +{ +printf("Add new mapping (TODO)...\n"); +} + + +void ControllerTab::DeleteMapName(void) +{ +printf("Delete current mapping (TODO)...\n"); +} + + +/* The profiles need the following: - The name of the controller @@ -129,5 +255,32 @@ data structure. When a new controller is plugged in with no profiles attached, it defaults to a set keyboard layout which the user can change. So every new controller will always have at least one profile. -#endif + +Data structures: +The Gamepad class has the name of the controller (except for Keyboard) +The profile list is just a list +The controller name index + profile index makes a unique key +Probably the best way to deal with it is to stuff the name/profile indices +into the key definition structure. + +#define CONTROLLER1 0x01 +#define CONTROLLER2 0x02 + +struct Profile +{ + int device; // Host device number + char mapName[32]; // Human readable map name + int preferredController; // CONTROLLER1 and/or CONTROLLER2 + int map[21]; // Keys/buttons/axes +}; + +NOTE that device is an int, and the list is maintained elsewhere. It is +*not* the same as what you see in GetJoystickName(); the device names have +to be able to persist even when not available. + +Where to store the master profile list? It has to be accessible to this class. +vjs.profile[x] would be good, but it's not really a concern for the Jaguar core. +So it shouldn't go there. There should be a separate global setting place for +GUI stuff... +*/ diff --git a/src/gui/controllertab.h b/src/gui/controllertab.h index 998d193..d0749d2 100644 --- a/src/gui/controllertab.h +++ b/src/gui/controllertab.h @@ -14,19 +14,32 @@ class ControllerTab: public QWidget ControllerTab(QWidget * parent = 0); ~ControllerTab(); + void SetupLastUsedProfile(void); + protected slots: void DefineAllKeys(void); - void ChangeProfile(int); + void UpdateProfileKeys(int, uint32_t); + void UpdateProfileConnections(void); + void ChangeDevice(int); + void ChangeMapName(int); + void AddMapName(void); + void DeleteMapName(void); private: - QLabel * label; - QComboBox * controllerList; - QComboBox * profileList; + QLabel * label1; + QLabel * label2; + QLabel * label3; + QComboBox * deviceList; + QComboBox * mapNameList; + QCheckBox * controller1; + QCheckBox * controller2; + QPushButton * addMapName; + QPushButton * deleteMapName; QPushButton * redefineAll; public: ControllerWidget * controllerWidget; - int profile; + int profileNum; }; #endif // __CONTROLLERTAB_H__ diff --git a/src/gui/controllerwidget.cpp b/src/gui/controllerwidget.cpp index eb91c34..53a747a 100644 --- a/src/gui/controllerwidget.cpp +++ b/src/gui/controllerwidget.cpp @@ -182,7 +182,10 @@ void ControllerWidget::mouseReleaseEvent(QMouseEvent * /*event*/) int key = keyGrab.key; if (key != Qt::Key_Escape) + { keys[keyToHighlightSave] = key; + emit(KeyDefined(keyToHighlightSave, key)); + } keyToHighlight = keyToHighlightSave; update(); diff --git a/src/gui/controllerwidget.h b/src/gui/controllerwidget.h index b1065cd..d506071 100644 --- a/src/gui/controllerwidget.h +++ b/src/gui/controllerwidget.h @@ -24,6 +24,9 @@ class ControllerWidget: public QWidget private: void DrawBorderedText(QPainter &, int, int, QString); + signals: + void KeyDefined(int, uint32_t); + public: uint32_t keys[21]; diff --git a/src/gui/gamepad.cpp b/src/gui/gamepad.cpp index 8706e17..93989ee 100644 --- a/src/gui/gamepad.cpp +++ b/src/gui/gamepad.cpp @@ -52,7 +52,7 @@ void Gamepad::AllocateJoysticks(void) { pad[i] = SDL_JoystickOpen(i); padName[i] = SDL_JoystickName(i); - numButtons[i] = numHats[i] = 0; + numButtons[i] = numHats[i] = numAxes[i] = 0; if (pad[i]) { @@ -104,14 +104,21 @@ bool Gamepad::GetState(int joystickID, int buttonID) { int axisNum = (buttonID & JOY_AXISNUM_MASK) >> 1; int direction = (buttonID & JOY_AXISDIR_MASK); +//printf("Checking pad #%u axis %u: axis = %i, direction = %u\n", joystickID, axisNum, axis[joystickID][axisNum], direction); if (axis[joystickID][axisNum] != 0) { - if (axis[joystickID][axisNum] > 16000 && (direction == 0)) + if ((axis[joystickID][axisNum] > 32000) && (direction == 0)) +//{ +//printf("Axis + hit!\n"); return true; +//} - if (axis[joystickID][axisNum] < -16000 && (direction == 1)) + if ((axis[joystickID][axisNum] < -32000) && (direction == 1)) +//{ +//printf("Axis - hit!\n"); return true; +//} } } diff --git a/src/gui/mainwin.cpp b/src/gui/mainwin.cpp index 43ec937..3c93d03 100644 --- a/src/gui/mainwin.cpp +++ b/src/gui/mainwin.cpp @@ -38,11 +38,13 @@ #include "app.h" #include "about.h" #include "configdialog.h" +#include "controllertab.h" #include "filepicker.h" #include "gamepad.h" #include "generaltab.h" #include "glwidget.h" #include "help.h" +#include "profile.h" #include "settings.h" #include "version.h" #include "debug/cpubrowser.h" @@ -564,7 +566,10 @@ void MainWin::HandleGamepads(void) { if (vjs.p1KeyBindings[i] & (JOY_BUTTON | JOY_HAT | JOY_AXIS)) joypad0Buttons[i] = (Gamepad::GetState(0, vjs.p1KeyBindings[i]) ? 0x01 : 0x00); - +/*{ +if (vjs.p1KeyBindings[i] & JOY_AXIS) + printf("Axis state (HandleGamepads): %i\n", joypad0Buttons[i]); +}*/ if (vjs.p2KeyBindings[i] & (JOY_BUTTON | JOY_HAT | JOY_AXIS)) joypad1Buttons[i] = (Gamepad::GetState(1, vjs.p2KeyBindings[i]) ? 0x01 : 0x00); } @@ -582,6 +587,8 @@ void MainWin::Configure(void) ConfigDialog dlg(this); //ick. dlg.generalTab->useUnknownSoftware->setChecked(allowUnknownSoftware); + dlg.controllerTab1->profileNum = lastEditedProfile; + dlg.controllerTab1->SetupLastUsedProfile(); if (dlg.exec() == false) return; @@ -601,6 +608,7 @@ void MainWin::Configure(void) bool allowOld = allowUnknownSoftware; //ick. allowUnknownSoftware = dlg.generalTab->useUnknownSoftware->isChecked(); + lastEditedProfile = dlg.controllerTab1->profileNum; // We rescan the "software" folder if the user either changed the path or // checked/unchecked the "Allow unknown files" option in the config dialog. @@ -1074,6 +1082,7 @@ void MainWin::ReadSettings(void) zoomLevel = settings.value("zoom", 2).toInt(); allowUnknownSoftware = settings.value("showUnknownSoftware", false).toBool(); + lastEditedProfile = settings.value("lastEditedProfile", 0).toInt(); vjs.useJoystick = settings.value("useJoystick", false).toBool(); vjs.joyport = settings.value("joyport", 0).toInt(); @@ -1146,6 +1155,8 @@ WriteLog("Pipelined DSP = %s\n", (vjs.usePipelinedDSP ? "ON" : "off")); vjs.p2KeyBindings[BUTTON_9] = settings.value("p2k_9", Qt::Key_9).toInt(); vjs.p2KeyBindings[BUTTON_d] = settings.value("p2k_pound", Qt::Key_Slash).toInt(); vjs.p2KeyBindings[BUTTON_s] = settings.value("p2k_star", Qt::Key_Asterisk).toInt(); + + ReadProfiles(&settings); } @@ -1158,6 +1169,7 @@ void MainWin::WriteSettings(void) settings.setValue("zoom", zoomLevel); settings.setValue("showUnknownSoftware", allowUnknownSoftware); + settings.setValue("lastEditedProfile", lastEditedProfile); settings.setValue("useJoystick", vjs.useJoystick); settings.setValue("joyport", vjs.joyport); @@ -1224,6 +1236,8 @@ void MainWin::WriteSettings(void) settings.setValue("p2k_9", vjs.p2KeyBindings[BUTTON_9]); settings.setValue("p2k_pound", vjs.p2KeyBindings[BUTTON_d]); settings.setValue("p2k_star", vjs.p2KeyBindings[BUTTON_s]); + + WriteProfiles(&settings); } @@ -1236,3 +1250,4 @@ void MainWin::WriteUISettings(void) settings.setValue("zoom", zoomLevel); } + diff --git a/src/gui/mainwin.h b/src/gui/mainwin.h index c87ce99..06ca405 100644 --- a/src/gui/mainwin.h +++ b/src/gui/mainwin.h @@ -108,6 +108,7 @@ class MainWin: public QMainWindow private: QPoint mainWinPosition; // QSize mainWinSize; + int lastEditedProfile; QMenu * fileMenu; QMenu * helpMenu; QMenu * debugMenu; diff --git a/src/gui/profile.cpp b/src/gui/profile.cpp new file mode 100644 index 0000000..05d173d --- /dev/null +++ b/src/gui/profile.cpp @@ -0,0 +1,290 @@ +// +// profile.cpp - Global profile storage/definition/manipulation +// +// by James Hammons +// (C) 2013 Underground Software +// +// JLH = James Hammons +// +// Who When What +// --- ---------- ------------------------------------------------------------- +// JLH 05/01/2013 Created this file +// + + +#include "profile.h" +#include +#include "gamepad.h" +#include "settings.h" + + +#define MAX_DEVICES 64 + + +Profile profile[MAX_PROFILES]; +int controller1Profile; +int controller2Profile; +int gamepad1Slot; +int gamepad2Slot; +int numberOfProfiles; +int numberOfDevices; +char deviceNames[MAX_DEVICES][128]; + +// This is so that new devices have something reasonable to show for default +uint32_t defaultMap[21] = { + 'S', 'X', 'Z', 'C', '-','7', '4', '1', '0', '8', '5', '2', '=', '9', '6', + '3', 'L', 'K', 'J', 'O', 'P' +}; + + +void ReadProfiles(QSettings * set) +{ + // Assume no profiles, until we read them + numberOfProfiles = 0; + + // There is always at least one device present, and it's the keyboard + // (hey, we're PC centric here ;-) + numberOfDevices = 1; + strcpy(deviceNames[0], "Keyboard"); + + // Read the rest of the devices (if any) + numberOfDevices += set->beginReadArray("devices"); + + for(int i=1; isetArrayIndex(i - 1); + strcpy(deviceNames[i], set->value("deviceName").toString().toAscii().data()); +//printf("Read device name: %s\n", deviceNames[i]); + } + + set->endArray(); + numberOfProfiles = set->beginReadArray("profiles"); +//printf("Number of profiles: %u\n", numberOfProfiles); + + for(int i=0; isetArrayIndex(i); + profile[i].device = set->value("deviceNum").toInt(); + strcpy(profile[i].mapName, set->value("mapName").toString().toAscii().data()); + profile[i].preferredController = set->value("preferredController").toInt(); + + for(int j=0; j<21; j++) + { + QString string = QString("map%1").arg(j); + profile[i].map[j] = set->value(string).toInt(); + } +//printf("Profile #%u: device=%u (%s)\n", i, profile[i].device, deviceNames[profile[i].device]); + } + + set->endArray(); + +//printf("Number of profiles found: %u\n", numberOfProfiles); + // Set up a reasonable default if no profiles were found + if (numberOfProfiles == 0) + { +//printf("Setting up default profile...\n"); + numberOfProfiles++; + profile[0].device = 0; // Keyboard is always device #0 + strcpy(profile[0].mapName, "Default"); + profile[0].preferredController = CONTROLLER1; + + for(int i=0; i<21; i++) + profile[0].map[i] = defaultMap[i]; + } +} + + +void WriteProfiles(QSettings * set) +{ +#if 0 + // Don't write anything for now... + return; +#endif + // NB: Should only do this if something changed; otherwise, no need to do + // this. + set->beginWriteArray("devices"); + + for(int i=1; isetArrayIndex(i - 1); + set->setValue("deviceName", deviceNames[i]); + } + + set->endArray(); + set->beginWriteArray("profiles"); + + for(int i=0; isetArrayIndex(i); + set->setValue("deviceNum", profile[i].device); + set->setValue("mapName", profile[i].mapName); + set->setValue("preferredController", profile[i].preferredController); + + for(int j=0; j<21; j++) + { + QString string = QString("map%1").arg(j); + set->setValue(string, profile[i].map[j]); + } + } + + set->endArray(); +} + + +int FindDeviceNumberForName(const char * name) +{ + for(int i=0; iaddItem(profile[i].mapName, i); + found++; + } + } + + // If no mappings were found, create a default one for it + if (found == 0) + { + profile[numberOfProfiles].device = deviceNum; + strcpy(profile[numberOfProfiles].mapName, "Default"); + profile[numberOfProfiles].preferredController = CONTROLLER1; + + for(int i=0; i<21; i++) + profile[numberOfProfiles].map[i] = defaultMap[i]; + + combo->addItem(profile[numberOfProfiles].mapName, numberOfProfiles); + numberOfProfiles++; + found++; + } + + return found; +} + + +bool ConnectProfileToController(int profileNum, int controllerNum) +{ + if (profile[profileNum].device == -1) + return false; + + if (controllerNum < 0 || controllerNum > 2) + return false; + + uint32_t * dest = (controllerNum == 0 ? &vjs.p1KeyBindings[0] : &vjs.p2KeyBindings[0]); + + for(int i=0; i<21; i++) + dest[i] = profile[profileNum].map[i]; + +printf("Successfully mapped device '%s' (%s) to controller #%u...\n", deviceNames[profile[profileNum].device], profile[profileNum].mapName, controllerNum); + return true; +} + + +// +// This is a pretty crappy way of doing autodetection. What it does is scan for +// keyboard profiles first, then look for plugged in gamepads next. If more +// than one plugged in gamepad matches a preferred controller slot, the last +// one found is chosen. +// +// There has to be a better way to do this, I just can't think of what it +// should be ATM... :-P +// +/* +Also, there is a problem with this approach and having multiple devices +that are the same. Currently, if two of the same device are plugged in +and the profile is set to both controllers, it will broadcast buttons +pressed from either gamepad, no matter who is pressing them. This is +BAD(tm). [Not true, but there's a different problem described under 'How to +solve?', so GOOD(tm).] + +Also, the gamepad logic doesn't distinguish inputs by controller, it just +grabs them all regardless. This is also BAD(tm). [Actually, it doesn't. It +properly segregates the inputs. So this is GOOD(tm).] + +How to solve? + +Seems there's yet ANOTHER dimension to all this: The physical gamepads +plugged into their ports. Now the device # can map these fine if they're +different, but we still run into problems with the handling in the MainWin +because it's hardwired to take pad 0 in slot 0 and pad 1 in slot 1. If you have +them configured other than this, you won't get anything. So we need to also +map the physical devices to their respective slots. +*/ +void AutoConnectProfiles(void) +{ + int controller1Profile = -1; + int controller2Profile = -1; + + // Nothing plugged in, we fall back to the default keyboard device profiles +// if (Gamepad::numJoysticks == 0) + { +// Check for Keyboard device first, if anything else is plugged in it will +// default to it instead + for(int i=0; i + +class QComboBox; +class QSettings; + +#define MAX_PROFILES 64 +#define CONTROLLER1 0x01 +#define CONTROLLER2 0x02 + + +struct Profile +{ + int device; // Host device number (-1 == invalid profile) + char mapName[32]; // Human readable map name + int preferredController; // CONTROLLER1 and/or CONTROLLER2 + uint32_t map[21]; // Keys/buttons/axes +}; + + +// Function prototypes +void ReadProfiles(QSettings *); +void WriteProfiles(QSettings *); +int FindDeviceNumberForName(const char *); +int FindMappingsForDevice(int, QComboBox *); +bool ConnectProfileToController(int, int); +void AutoConnectProfiles(void); + + +// Exported variables +extern Profile profile[]; +extern int controller1Profile; +extern int controller2Profile; +extern int gamepad1Slot; +extern int gamepad2Slot; +//extern int numberOfProfiles; + +#endif // __PROFILE_H__ + diff --git a/src/jerry.cpp b/src/jerry.cpp index 3237d7b..e0255c4 100644 --- a/src/jerry.cpp +++ b/src/jerry.cpp @@ -436,7 +436,17 @@ WriteLog("JERRY: Unhandled timer read (BYTE) at %08X...\n", offset); // return anajoy_byte_read(offset); else if (offset >= 0xF14000 && offset <= 0xF14003) // return JoystickReadByte(offset) | EepromReadByte(offset); - return JoystickReadWord(offset & 0xFE) | EepromReadByte(offset); + { + uint16_t value = JoystickReadWord(offset & 0xFE); + + if (offset & 0x01) + value &= 0xFF; + else + value >>= 8; + + // This is wrong, should only have the lowest bit from $F14001 + return value | EepromReadByte(offset); + } else if (offset >= 0xF14000 && offset <= 0xF1A0FF) return EepromReadByte(offset); @@ -569,8 +579,10 @@ WriteLog("JERRY: Unhandled timer write (BYTE) at %08X...\n", offset); }*/ else if ((offset >= 0xF14000) && (offset <= 0xF14003)) { +WriteLog("JERRYWriteByte: Unhandled byte write to JOYSTICK by %s.\n", whoName[who]); // JoystickWriteByte(offset, data); JoystickWriteWord(offset & 0xFE, (uint16_t)data); +// This is wrong, EEPROM is never written here EepromWriteByte(offset, data); return; } diff --git a/src/joystick.cpp b/src/joystick.cpp index e662bf1..91cfbbb 100644 --- a/src/joystick.cpp +++ b/src/joystick.cpp @@ -136,7 +136,7 @@ uint16_t JoystickReadWord(uint32_t offset) return ~data; #else if (!joysticksEnabled) - return 0xFF; + return 0xFFFF; // Joystick data returns active low for buttons pressed, high for non- // pressed. @@ -147,6 +147,7 @@ uint16_t JoystickReadWord(uint32_t offset) if (offset0 != 0xFF) { uint16_t mask[4] = { 0xFEFF, 0xFDFF, 0xFBFF, 0xF7FF }; +// uint16_t mask[4] = { 0xFFFE, 0xFFFD, 0xFFFB, 0xFFF7 }; for(uint8_t i=0; i<4; i++) data &= (joypad0Buttons[offset0 + i] ? mask[i] : 0xFFFF); @@ -155,6 +156,7 @@ uint16_t JoystickReadWord(uint32_t offset) if (offset1 != 0xFF) { uint16_t mask[4] = { 0xEFFF, 0xDFFF, 0xBFFF, 0x7FFF }; +// uint16_t mask[4] = { 0xFFEF, 0xFFDF, 0xFFBF, 0xFF7F }; for(uint8_t i=0; i<4; i++) data &= (joypad1Buttons[offset1 + i] ? mask[i] : 0xFFFF); @@ -252,7 +254,7 @@ uint16_t JoystickReadWord(uint32_t offset) } // return joystick_ram[offset]; - return 0xFF; + return 0xFFFF; } diff --git a/virtualjaguar.pro b/virtualjaguar.pro index 31bf263..7eb8778 100644 --- a/virtualjaguar.pro +++ b/virtualjaguar.pro @@ -78,6 +78,7 @@ HEADERS = \ src/gui/imagedelegate.h \ src/gui/keygrabber.h \ src/gui/mainwin.h \ + src/gui/profile.h \ src/gui/debug/cpubrowser.h \ src/gui/debug/m68kdasmbrowser.h \ src/gui/debug/memorybrowser.h \ @@ -102,6 +103,7 @@ SOURCES = \ src/gui/imagedelegate.cpp \ src/gui/keygrabber.cpp \ src/gui/mainwin.cpp \ + src/gui/profile.cpp \ src/gui/debug/cpubrowser.cpp \ src/gui/debug/m68kdasmbrowser.cpp \ src/gui/debug/memorybrowser.cpp \ diff --git a/virtualjaguar_fr.ts b/virtualjaguar_fr.ts index 26f7b13..8d12ab8 100644 --- a/virtualjaguar_fr.ts +++ b/virtualjaguar_fr.ts @@ -4,7 +4,8 @@ AboutWindow - + + @@ -16,7 +17,7 @@ - + <img src=':/res/vj_title_small.png' style='float: right'><table><tr><td align='right'><b>Version: </b></td><td> @@ -68,8 +69,13 @@ ControllerTab - - Define All Keys + + Define All Inputs + + + + + Keyboard @@ -181,274 +187,274 @@ MainWin - + Virtual Jaguar - + - Alpine Mode - + E&xit - + Ctrl+q - + Quit Virtual Jaguar - + &Power - + Powers Jaguar on/off - + Pause - + Toggles the running state - + Esc - + Zoom 100% - + Set window zoom to 100% - + Zoom 200% - + Set window zoom to 200% - + Zoom 300% - + Set window zoom to 300% - + NTSC - + Sets Jaguar to NTSC mode - + PAL - + Sets Jaguar to PAL mode - + Blur - + Sets OpenGL rendering to GL_NEAREST - + &About... - + Blatant self-promotion - + &Contents... - + Help is available, if you should need it - + &Insert Cartridge... - + Insert a cartridge into Virtual Jaguar - + Ctrl+i - + &Configure - + Configure options for Virtual Jaguar - + Ctrl+c - + &Use CD Unit - + Use Jaguar Virtual CD unit - + &Frame Advance - + F7 - + F&ull Screen - + F9 - + Memory Browser - + Shows the Jaguar memory browser window - + CPU Browser - + Shows the Jaguar CPU browser window - + OP Browser - + Shows the Jaguar OP browser window - + 68K Listing Browser - + Shows the 68K disassembly browser window - + RISC Listing Browser - + Shows the RISC disassembly browser window - + &Jaguar - - + + &Debug - + &Help - + Stuff - + Ready - - + + Could not load file "%1"! diff --git a/virtualjaguar_gr.ts b/virtualjaguar_gr.ts index a40cce8..dfa51ea 100644 --- a/virtualjaguar_gr.ts +++ b/virtualjaguar_gr.ts @@ -4,7 +4,8 @@ AboutWindow - + + @@ -16,7 +17,7 @@ - + <img src=':/res/vj_title_small.png' style='float: right'><table><tr><td align='right'><b>Version: </b></td><td> @@ -68,8 +69,13 @@ ControllerTab - - Define All Keys + + Define All Inputs + + + + + Keyboard @@ -181,274 +187,274 @@ MainWin - + Virtual Jaguar - + - Alpine Mode - + E&xit - + Ctrl+q - + Quit Virtual Jaguar - + &Power - + Powers Jaguar on/off - + Pause - + Toggles the running state - + Esc - + Zoom 100% - + Set window zoom to 100% - + Zoom 200% - + Set window zoom to 200% - + Zoom 300% - + Set window zoom to 300% - + NTSC - + Sets Jaguar to NTSC mode - + PAL - + Sets Jaguar to PAL mode - + Blur - + Sets OpenGL rendering to GL_NEAREST - + &About... - + Blatant self-promotion - + &Contents... - + Help is available, if you should need it - + &Insert Cartridge... - + Insert a cartridge into Virtual Jaguar - + Ctrl+i - + &Configure - + Configure options for Virtual Jaguar - + Ctrl+c - + &Use CD Unit - + Use Jaguar Virtual CD unit - + &Frame Advance - + F7 - + F&ull Screen - + F9 - + Memory Browser - + Shows the Jaguar memory browser window - + CPU Browser - + Shows the Jaguar CPU browser window - + OP Browser - + Shows the Jaguar OP browser window - + 68K Listing Browser - + Shows the 68K disassembly browser window - + RISC Listing Browser - + Shows the RISC disassembly browser window - + &Jaguar - - + + &Debug - + &Help - + Stuff - + Ready - - + + Could not load file "%1"! -- 2.37.2