]> Shamusworld >> Repos - virtualjaguar/blob - src/gui/profile.cpp
05d173d84407f716eda3c4adf6a29bf7dc5f5ced
[virtualjaguar] / src / gui / profile.cpp
1 //
2 // profile.cpp - Global profile storage/definition/manipulation
3 //
4 // by James Hammons
5 // (C) 2013 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -------------------------------------------------------------
11 // JLH  05/01/2013  Created this file
12 //
13
14
15 #include "profile.h"
16 #include <QtGui>
17 #include "gamepad.h"
18 #include "settings.h"
19
20
21 #define MAX_DEVICES  64
22
23
24 Profile profile[MAX_PROFILES];
25 int controller1Profile;
26 int controller2Profile;
27 int gamepad1Slot;
28 int gamepad2Slot;
29 int numberOfProfiles;
30 int numberOfDevices;
31 char deviceNames[MAX_DEVICES][128];
32
33 // This is so that new devices have something reasonable to show for default
34 uint32_t defaultMap[21] = {
35         'S', 'X', 'Z', 'C', '-','7', '4', '1', '0', '8', '5', '2', '=', '9', '6',
36         '3', 'L', 'K', 'J', 'O', 'P'
37 };
38
39
40 void ReadProfiles(QSettings * set)
41 {
42         // Assume no profiles, until we read them
43         numberOfProfiles = 0;
44
45         // There is always at least one device present, and it's the keyboard
46         // (hey, we're PC centric here ;-)
47         numberOfDevices = 1;
48         strcpy(deviceNames[0], "Keyboard");
49
50         // Read the rest of the devices (if any)
51         numberOfDevices += set->beginReadArray("devices");
52
53         for(int i=1; i<numberOfDevices; i++)
54         {
55                 set->setArrayIndex(i - 1);
56                 strcpy(deviceNames[i], set->value("deviceName").toString().toAscii().data());
57 //printf("Read device name: %s\n", deviceNames[i]);
58         }
59
60         set->endArray();
61         numberOfProfiles = set->beginReadArray("profiles");
62 //printf("Number of profiles: %u\n", numberOfProfiles);
63
64         for(int i=0; i<numberOfProfiles; i++)
65         {
66                 set->setArrayIndex(i);
67                 profile[i].device = set->value("deviceNum").toInt();
68                 strcpy(profile[i].mapName, set->value("mapName").toString().toAscii().data());
69                 profile[i].preferredController = set->value("preferredController").toInt();
70
71                 for(int j=0; j<21; j++)
72                 {
73                         QString string = QString("map%1").arg(j);
74                         profile[i].map[j] = set->value(string).toInt();
75                 }
76 //printf("Profile #%u: device=%u (%s)\n", i, profile[i].device, deviceNames[profile[i].device]);
77         }
78
79         set->endArray();
80
81 //printf("Number of profiles found: %u\n", numberOfProfiles);
82         // Set up a reasonable default if no profiles were found
83         if (numberOfProfiles == 0)
84         {
85 //printf("Setting up default profile...\n");
86                 numberOfProfiles++;
87                 profile[0].device = 0;  // Keyboard is always device #0
88                 strcpy(profile[0].mapName, "Default");
89                 profile[0].preferredController = CONTROLLER1;
90
91                 for(int i=0; i<21; i++)
92                         profile[0].map[i] = defaultMap[i];
93         }
94 }
95
96
97 void WriteProfiles(QSettings * set)
98 {
99 #if 0
100         // Don't write anything for now...
101         return;
102 #endif
103         // NB: Should only do this if something changed; otherwise, no need to do
104         //     this.
105         set->beginWriteArray("devices");
106
107         for(int i=1; i<numberOfDevices; i++)
108         {
109                 set->setArrayIndex(i - 1);
110                 set->setValue("deviceName", deviceNames[i]);
111         }
112
113         set->endArray();
114         set->beginWriteArray("profiles");
115
116         for(int i=0; i<numberOfProfiles; i++)
117         {
118                 set->setArrayIndex(i);
119                 set->setValue("deviceNum", profile[i].device);
120                 set->setValue("mapName", profile[i].mapName);
121                 set->setValue("preferredController", profile[i].preferredController);
122
123                 for(int j=0; j<21; j++)
124                 {
125                         QString string = QString("map%1").arg(j);
126                         set->setValue(string, profile[i].map[j]);
127                 }
128         }
129
130         set->endArray();
131 }
132
133
134 int FindDeviceNumberForName(const char * name)
135 {
136         for(int i=0; i<numberOfDevices; i++)
137         {
138                 if (strcmp(deviceNames[i], name) == 0)
139                         return i;
140         }
141
142         if (numberOfDevices == MAX_DEVICES)
143                 return -1;
144
145         // If the device wasn't found, it must be new; so add it to the list.
146         int deviceNum = numberOfDevices;
147         deviceNames[deviceNum][127] = 0;
148         strncpy(deviceNames[deviceNum], name, 127);
149         numberOfDevices++;
150
151         return deviceNum;
152 }
153
154
155 int FindMappingsForDevice(int deviceNum, QComboBox * combo)
156 {
157         int found = 0;
158
159         for(int i=0; i<numberOfProfiles; i++)
160         {
161                 if (profile[i].device == -1)
162                         continue;
163
164                 if (profile[i].device == deviceNum)
165                 {
166                         combo->addItem(profile[i].mapName, i);
167                         found++;
168                 }
169         }
170
171         // If no mappings were found, create a default one for it
172         if (found == 0)
173         {
174                 profile[numberOfProfiles].device = deviceNum;
175                 strcpy(profile[numberOfProfiles].mapName, "Default");
176                 profile[numberOfProfiles].preferredController = CONTROLLER1;
177
178                 for(int i=0; i<21; i++)
179                         profile[numberOfProfiles].map[i] = defaultMap[i];
180
181                 combo->addItem(profile[numberOfProfiles].mapName, numberOfProfiles);
182                 numberOfProfiles++;
183                 found++;
184         }
185
186         return found;
187 }
188
189
190 bool ConnectProfileToController(int profileNum, int controllerNum)
191 {
192         if (profile[profileNum].device == -1)
193                 return false;
194
195         if (controllerNum < 0 || controllerNum > 2)
196                 return false;
197
198         uint32_t * dest = (controllerNum == 0 ? &vjs.p1KeyBindings[0] : &vjs.p2KeyBindings[0]);
199
200         for(int i=0; i<21; i++)
201                 dest[i] = profile[profileNum].map[i];
202
203 printf("Successfully mapped device '%s' (%s) to controller #%u...\n", deviceNames[profile[profileNum].device], profile[profileNum].mapName, controllerNum);
204         return true;
205 }
206
207
208 //
209 // This is a pretty crappy way of doing autodetection. What it does is scan for
210 // keyboard profiles first, then look for plugged in gamepads next. If more
211 // than one plugged in gamepad matches a preferred controller slot, the last
212 // one found is chosen.
213 //
214 // There has to be a better way to do this, I just can't think of what it
215 // should be ATM... :-P
216 //
217 /*
218 Also, there is a problem with this approach and having multiple devices
219 that are the same. Currently, if two of the same device are plugged in
220 and the profile is set to both controllers, it will broadcast buttons
221 pressed from either gamepad, no matter who is pressing them. This is
222 BAD(tm). [Not true, but there's a different problem described under 'How to
223 solve?', so GOOD(tm).]
224
225 Also, the gamepad logic doesn't distinguish inputs by controller, it just
226 grabs them all regardless. This is also BAD(tm). [Actually, it doesn't. It
227 properly segregates the inputs. So this is GOOD(tm).]
228
229 How to solve?
230
231 Seems there's yet ANOTHER dimension to all this: The physical gamepads
232 plugged into their ports. Now the device # can map these fine if they're
233 different, but we still run into problems with the handling in the MainWin
234 because it's hardwired to take pad 0 in slot 0 and pad 1 in slot 1. If you have
235 them configured other than this, you won't get anything. So we need to also
236 map the physical devices to their respective slots.
237 */
238 void AutoConnectProfiles(void)
239 {
240         int controller1Profile = -1;
241         int controller2Profile = -1;
242
243         // Nothing plugged in, we fall back to the default keyboard device profiles
244 //      if (Gamepad::numJoysticks == 0)
245         {
246 // Check for Keyboard device first, if anything else is plugged in it will
247 // default to it instead
248                 for(int i=0; i<numberOfProfiles; i++)
249                 {
250                         // Skip profile if it's not Keyboard device
251                         if (profile[i].device != 0)
252                                 continue;
253
254                         if (profile[i].preferredController & CONTROLLER1)
255                                 controller1Profile = i;
256
257                         if (profile[i].preferredController & CONTROLLER2)
258                                 controller2Profile = i;
259                 }
260         }
261 //      else
262         {
263 //printf("Number of gamepads found: %u\n", Gamepad::numJoysticks);
264                 for(int i=0; i<Gamepad::numJoysticks; i++)
265                 {
266                         int deviceNum = FindDeviceNumberForName(Gamepad::GetJoystickName(i));
267 //printf("Attempting to find valid gamepad profile. Device=%u\n", deviceNum);
268
269                         for(int j=0; j<numberOfProfiles; j++)
270                         {
271                                 // Skip profile if it's not discovered device
272                                 if (profile[j].device != deviceNum)
273                                         continue;
274
275                                 if (profile[j].preferredController & CONTROLLER1)
276                                         controller1Profile = j;
277
278                                 if (profile[j].preferredController & CONTROLLER2)
279                                         controller2Profile = j;
280                         }
281                 }
282         }
283
284         if (controller1Profile != -1)
285                 ConnectProfileToController(controller1Profile, 0);
286
287         if (controller2Profile != -1)
288                 ConnectProfileToController(controller2Profile, 1);
289 }
290