Skip to content

Commit 932740f

Browse files
authored
Bugfixes and improvements
Bugfixes and improvements: - Fixed occasional mismatch bug where uinput-mapper chose any free /dev/input/eventX number, whereas launCharc always chose highest +1. - Possible to map only 1 or 2 controllers(no keyboard needed), detects and exits if no usable control devices are connected. - Faster and smoother(no blinking cursor) game launch. - Tidied up code some, added some error handling. - Meaningful status and error messages printed to console on start and in background, not cleared on exit to help diagnosis. - Exits uinput-mapper on more errors and exit conditions. - Kill pulseaudio on game start(blank screen and not detected controller on some older devices(RPi3) and distributions(Raspbian stretch)). - More extensive testing: - Works best on newer devices(RPi4) with fake/firmware fkms driver and newer Raspbian and RetroPie distros. - Works ok on older devices(RPi3) or with legacy graphics driver or on older Raspbian and RetroPie distros. - Very low framerate on full kms. - Some instability where game sometimes suddenly exits, increases with older devices and distros. Not sure why yet and if some input key combinations(e.g. CTRL+D) from the mappings are to blame, as well.
1 parent 4b020ab commit 932740f

File tree

2 files changed

+151
-75
lines changed

2 files changed

+151
-75
lines changed

McAirpos/launCharc/launCharc

1.89 KB
Binary file not shown.

McAirpos/launCharc/launCharc.c

+151-75
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ char* getSystemOutput(char* inputCommand) {
7272
// Main function
7373
int main(int argc, char** argv) {
7474

75+
7576
// Read game file argument to execute
77+
system("clear");
7678
char* game = "";
7779
char* options = "";
7880
if (argc == 2) {
@@ -86,92 +88,164 @@ int main(int argc, char** argv) {
8688
}
8789

8890

91+
// Check for nomap option
8992
if (!strcmp(options, "nomap")) {
90-
printf("%s, %s\n", game, options);
93+
printf("%s argument detected,\nStarting %s with no automatic gamepad mappings...\n", options, game);
94+
sleep(1);
9195
} else {
92-
// Determine the number of connected gamepads
93-
printf("%s\n", game);
94-
char eventPaths[100];
95-
memset (eventPaths, 0, sizeof(eventPaths));
96-
int numberOfPads = 0;
97-
int numberOfEvents = 1 + atoi(getSystemOutput("ls /dev/input | sed 's/event//' | sort -n | tail -1"));
98-
for (int i = 0; i < numberOfEvents; i++) {
99-
if (numberOfPads < 2) {
100-
char processCommand[120];
101-
snprintf(processCommand, 120, "/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -vp /dev/input/event%d | grep -e BTN_SOUTH -e BTN_PINKIE", i);
102-
char* event = getSystemOutput(processCommand);
103-
if (strcmp(event, "")) {
104-
printf("%s, Output:%s\n", processCommand, getSystemOutput(processCommand));
105-
printf("%d Possible gamepads\n", numberOfEvents);
106-
char iString[20];
107-
sprintf(iString, "%d", i);
108-
strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " ");
109-
strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " ");
110-
numberOfPads++;
111-
}
112-
}
113-
}
114-
115-
116-
// Set up MakeCode Arcade and uinput-mapper with keyboard, 1 or 2 gamepads
117-
printf("\n%s\n", eventPaths);
118-
char defaultEvent[67];
119-
memset (defaultEvent, 0, sizeof(defaultEvent));
120-
strcat(defaultEvent, "SCAN_CODES=/dev/input/");
121-
char sedCommand[100];
122-
char* tempString = getSystemOutput("cat /proc/bus/input/devices | grep -B 7 EV=12001 | tr ' ' '\n' | grep event");
123-
if ((numberOfPads == 1) || (numberOfPads == 2)) {
124-
if (numberOfPads == 1) {
125-
strcat(strcat(eventPaths, "/dev/input/"), tempString);
126-
eventPaths[strlen(eventPaths)-1] = 0;
127-
strcat(eventPaths, " ");
128-
}
129-
char newEventNo[20];
130-
sprintf(newEventNo, "%d", numberOfEvents);
131-
strcat(strcat(defaultEvent, "event"), newEventNo);
132-
char uiMapCommand[400];
133-
memset (uiMapCommand, 0, sizeof(uiMapCommand));
134-
sprintf(newEventNo, "%d.py)&",numberOfPads);
135-
strcat(strcat(strcat(strcat(uiMapCommand, "(/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -C -D "), eventPaths), "| sudo /home/pi/McAirpos/McAirpos/uinput-mapper/input-create -C -S /home/pi/McAirpos/McAirpos/uinput-mapper/configs/arcade"), newEventNo);
136-
printf("%s\n", uiMapCommand);
137-
/*if (!fork()) {
138-
setpgid(0, 0);
139-
system(uiMapCommand);
140-
}else {}*/
141-
system(uiMapCommand);
142-
}
143-
if (numberOfPads == 0) {
144-
strcat(defaultEvent, tempString);
145-
defaultEvent[strlen(defaultEvent)-1] = 0;
146-
}
147-
snprintf(sedCommand, 100, "sed -i \"1s&.*&\"%s\"&\" /sd/arcade.cfg", defaultEvent);
148-
system(sedCommand);
149-
}
150-
system("stty -ixon");
96+
// Determine the number of connected gamepads
97+
printf("Starting %s with automatic gamepad mappings...\n\n", game);
98+
char eventPaths[100];
99+
memset (eventPaths, 0, sizeof(eventPaths));
100+
int numberOfPads = 0;
101+
int numberOfEvents = 1 + atoi(getSystemOutput("ls /dev/input | sed 's/event//' | sort -n | tail -1"));
102+
printf("\nHighest found input event number: %d\n\n", numberOfEvents);
103+
char padEvent[2][20];
104+
for (int i = 0; i < numberOfEvents; i++) {
105+
if (numberOfPads < 2) {
106+
char padCommand[150];
107+
snprintf(padCommand, 150, "/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -vp /dev/input/event%d 2>&1 | grep -e BTN_SOUTH -e BTN_PINKIE", i);
108+
char* event = getSystemOutput(padCommand);
109+
if (strcmp(event, "")) {
110+
if (numberOfPads == 0) {
111+
printf("\nGamepad search hits:\n");
112+
}
113+
printf("%s, Output:%s", padCommand, getSystemOutput(padCommand));
114+
char iString[20];
115+
sprintf(iString, "%d", i);
116+
strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " ");
117+
strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " ");
118+
memset (padEvent[numberOfPads], 0, sizeof(padEvent[numberOfPads]));
119+
strcat(strcat(padEvent[numberOfPads], "/dev/input/event"), iString);
120+
numberOfPads++;
121+
}
122+
}
123+
}
124+
125+
// Determine if keyboard is connected
126+
char keybCommand[] = "cat /proc/bus/input/devices | grep -B 7 EV=12001 | tr ' ' '\\n' | grep event";
127+
char* keybEvent = getSystemOutput(keybCommand);
128+
if (strcmp(keybEvent, "")) {
129+
printf("\nKeyboard search hit:\n");
130+
printf("%s, Output:%s", keybCommand, getSystemOutput(keybCommand));
131+
}
132+
133+
134+
// Automatically set up uinput-mapper with keyboard, 1 or 2 gamepads
135+
// printf("\n%s\n", eventPaths);
136+
char stringNumberOfPads[20];
137+
char uiMapCommand[400];
138+
memset (uiMapCommand, 0, sizeof(uiMapCommand));
139+
char uinputMapperOrKeyboard[20];
140+
memset (uinputMapperOrKeyboard, 0, sizeof(uinputMapperOrKeyboard));
141+
char defaultEvent[67];
142+
memset (defaultEvent, 0, sizeof(defaultEvent));
143+
strcat(defaultEvent, "SCAN_CODES=/dev/input/");
144+
printf("\n");
145+
if ((numberOfPads == 0) && !strcmp(keybEvent, "")) {
146+
printf("\nFound no gamepads or keyboards to configure...\n");
147+
printf("\nPlease try the \"nomap\" option and configure /sd/arcade.cfg manually. If stuck, please read or open a related issue at https://github.com/Vegz78/McAirpos.\n");
148+
sleep(1);
149+
exit(1);
150+
}
151+
else if (numberOfPads == 2) {
152+
printf("\nFound %d gamepads to configure on:\n%s, and\n%s\n\n", numberOfPads, padEvent[0], padEvent[1]);
153+
sprintf(stringNumberOfPads, "%d.py)&",numberOfPads);
154+
}
155+
else if ((numberOfPads == 1) && strcmp(keybEvent, "")) {
156+
printf("\nFound %d gamepad to configure on:\n%s\n", numberOfPads, padEvent[0]);
157+
printf("\nFound 1 keyboard to configure on:\n/dev/input/%s\n\n", keybEvent);
158+
strcat(strcat(eventPaths, "/dev/input/"), keybEvent);
159+
eventPaths[strlen(eventPaths)-1] = 0;
160+
strcat(eventPaths, " ");
161+
sprintf(stringNumberOfPads, "%d.py)&",numberOfPads);
162+
}
163+
else if (strcmp(keybEvent, "")) {
164+
printf("\nFound 1 keyboard to configure on:\n/dev/input/%s\n\n", keybEvent);
165+
strcat(defaultEvent, keybEvent);
166+
defaultEvent[strlen(defaultEvent)-1] = 0;
167+
strcat(uinputMapperOrKeyboard, "keyboard");
168+
}
169+
else if (numberOfPads == 1) {
170+
printf("\nFound %d gamepad to configure on:\n%s\n\n", numberOfPads, padEvent[0]);
171+
sprintf(stringNumberOfPads, "%d.py)&",numberOfPads);
172+
}
173+
else {
174+
printf("\nSomething went wrong, exiting...\n");
175+
printf("If stuck, please read or open a related issue at https://github.com/Vegz78/McAirpos.\n");
176+
sleep(1);
177+
exit(1);
178+
}
179+
180+
181+
// Launching uinput-mapper
182+
if (numberOfPads > 0) {
183+
strcat(strcat(strcat(strcat(uiMapCommand, "(/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -C -D "), eventPaths), "| sudo /home/pi/McAirpos/McAirpos/uinput-mapper/input-create -C -S /home/pi/McAirpos/McAirpos/uinput-mapper/configs/arcade"), stringNumberOfPads);
184+
if (system(uiMapCommand) == 0) {
185+
printf("\nStarting UInput-Mapper with command:\n%s\n", uiMapCommand);
186+
int whileCount = 0;
187+
while (!strcmp("", getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event"))) {
188+
if (whileCount > 500) {
189+
printf("\nTimed out trying to set up UInput-Mapper...\n");
190+
printf("If stuck, please read or open a related issue at https://github.com/Vegz78/McAirpos.\n");
191+
exit(1);
192+
}
193+
whileCount++;
194+
}
195+
char* uinputEvent = getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event");
196+
strcat(defaultEvent, uinputEvent);
197+
defaultEvent[strlen(defaultEvent)-1] = 0;
198+
strcat(uinputMapperOrKeyboard, "UInputMapper");
199+
} else {
200+
printf("\nUInput-Mapper failed to start...\n");
201+
printf("If stuck, please read or open a related issue at https://github.com/Vegz78/McAirpos.\n");
202+
exit(1);
203+
}
204+
// Alternative way to launch uinput-mapper
205+
/*if (!fork()) {
206+
setpgid(0, 0);
207+
system(uiMapCommand);
208+
}else {}*/
209+
}
210+
211+
212+
// Set default default event for MakeCode Arcade elf game file
213+
char sedCommand[100];
214+
snprintf(sedCommand, 100, "sed -i \"1s&.*&\"%s\"&\" /sd/arcade.cfg", defaultEvent);
215+
if (system(sedCommand) == 0) {
216+
printf("\n\nSetting up %s in MakeCode Arcade game's /sd/arcade.cfg with:\n%s\n\n", uinputMapperOrKeyboard, defaultEvent);
217+
} else {
218+
printf("\n\nPlease check path or write permissions for /sd/arcade.cfg and try again.\n\n");
219+
goto cleanup;
220+
}
221+
}
222+
system("stty -ixon"); //Disable pause in terminal with key combo CTRL+S
151223

152224

153225
// Fork game execution on launch, so that it is executed
154226
// the same way it's done in-game on reset and finish
227+
char* path = "/dev/tty";
228+
int fd = open(path, O_RDWR, 0);
155229
if (!fork()) {
156-
sleep(1);
157-
system(game);
158-
//execl(game, game, NULL);
159-
}else {
160-
sleep(2); // Wait for fork/game to launch and get pid
161-
162230
//Switch console to graphics mode to avoid disturbing text output in borders
163-
char* path = "/dev/tty";
164-
int fd = open(path, O_RDWR, 0);
165231
if (fd < 0) {
166232
perror("unable to open tty");
167233
return 1;
168234
}
169-
system("clear");
170235
if (ioctl(fd, KDSETMODE, KD_GRAPHICS) < 0) {
171236
perror("warn: ioctl KDSETMODE failed");
172237
}
173238
close(fd);
174-
system("clear");
239+
system("sudo killall pulseaudio");
240+
if (system(game) == 36608) {
241+
printf("\n\n%s was exited by the user\n\n\n", game);
242+
} else {
243+
printf("\n\nPlease check path to and executable permissions for game_file.elf and try again.\n\n\n");
244+
}
245+
//Alternative way to launch game
246+
//execl(game, game, NULL);
247+
}else {
248+
sleep(1); // Wait for fork/game to launch and get pid
175249

176250
// Get running game's process name
177251
char* processID = getSystemOutput("head -1 /tmp/pxt-pid");
@@ -185,7 +259,7 @@ int main(int argc, char** argv) {
185259
// Busy waiting to continue and cleanup when game is exited
186260
// Why does this not work without the printf?!?
187261
besure:
188-
while (0 == system(processCheckCmd)) {
262+
while (0 == system(processCheckCmd)) {
189263
printf("%s\n", getSystemOutput(processCheckCmd));
190264
sleep(2);
191265
}
@@ -194,11 +268,13 @@ int main(int argc, char** argv) {
194268
sleep(1);
195269
if (0 == system(processCheckCmd)) {goto besure;}
196270

271+
197272
// Kill any remaining/orphaned game processes before exit
198273
char killAllCmd[100];
199-
snprintf(killAllCmd, 100, "killall %s", processName);
274+
snprintf(killAllCmd, 100, "killall %s 2>&1", processName);
200275
system(killAllCmd);
201-
system("sudo killall input-create&&sudo killall input-read");
276+
cleanup:
277+
system("sudo killall input-create 2>&1 & sudo killall input-read 2>&1");
202278

203279

204280
// The following code is borrowed from https://github.com/hobbitalistair/termfix:
@@ -213,7 +289,7 @@ int main(int argc, char** argv) {
213289
// This one fails without sudo, but doesn't seem needed for
214290
// MakeCode Arcade games(comment out or leave as and option?).
215291
if (ioctl(fd, VT_UNLOCKSWITCH, 1) < 0) {
216-
perror("warn: ioctl VT_UNLOCKSWITCH failed");
292+
//perror("warn: ioctl VT_UNLOCKSWITCH failed");
217293
}
218294

219295
if (ioctl(fd, KDSETMODE, KD_TEXT) < 0) {
@@ -225,7 +301,7 @@ int main(int argc, char** argv) {
225301
}
226302

227303
system("stty ixon");
228-
system("clear");
304+
//system("clear");
229305
}
230306

231307
return 0;

0 commit comments

Comments
 (0)