Skip to content

Commit c135007

Browse files
authored
Stability improvements
-New game launch function that checks for the started MakeCode Arcade game's process instead of waiting for a predetermined time. Faster startup and avoids race condition where launCharc some times could terminate before the MakeCode game had started, loosing controls and screen freezing. -Also included some error/pxt-pid file checks in above launch function -1 second longer exit time, to more often avoid race condition where launCharc termniates before game has reset in-game with into new process with sporadic delayed load times, which could cause loosing controls and screen freezing -More accurate keyboard discovery, where lowest input event handler should be chosen as default among multiple keyboard choices. Still some issues with wireless mouse only receiver dongles where the same also ship with keyboard event handlers in other keyboard/mouse bundels for sale(e.g. my Lenovo Ultraslim Plus Wireless Keyboard&Mouse", which may have had a keyboard connected earlier or was included also in combined keyboard/mouse packages). -Included a "keybswap" command line option argument to swap to the last discovered keyboard event handler instead, for cases like above(The one I use @home for my separate bluetooth keyboard along with the Lenovo keyboard/mouse receiver dongle, as described above...) -Widened the joystick event handler discovery by including search for "BTN_START", hoping I have not introduced a problem with/false positives for the few keyboards/input devices that are not controllers, which may also have this button. -Controlled char array indices and attempted to remov newline trailing characters consistently from the input instead of the potentially troublesome "charArray[strlen(charArray)-1] = 0;" function. -Cleaned up std output messages and code comments a bit
1 parent c964a75 commit c135007

File tree

2 files changed

+115
-41
lines changed

2 files changed

+115
-41
lines changed

McAirpos/launCharc/launCharc

6.93 KB
Binary file not shown.

McAirpos/launCharc/launCharc.c

+115-41
Original file line numberDiff line numberDiff line change
@@ -83,35 +83,38 @@ int main(int argc, char** argv) {
8383
game = argv[2];
8484
options = argv[1];
8585
} else if ((argc > 3) || (argc < 2)) {
86-
printf("usage: launchArcade [nomap] [/path/to/arcadegame.elf]\n");
86+
printf("usage: launchArcade [nomap / keybswap] [/path/to/arcadegame.elf]\n");
8787
return 1;
8888
}
8989

9090

9191
// Check for nomap option
9292
if (!strcmp(options, "nomap")) {
93-
printf("%s argument detected,\nStarting %s with no automatic gamepad mappings...\n", options, game);
93+
printf("%s argument detected,\nlaunCharc starting %s with no automatic gamepad mappings...\n", options, game);
9494
sleep(1);
9595
} else {
9696
// Determine the number of connected gamepads
97-
printf("Starting %s with automatic gamepad mappings...\n\n", game);
97+
printf("launCharc starting %s with automatic gamepad mappings...\n", game);
9898
char eventPaths[100];
9999
memset (eventPaths, 0, sizeof(eventPaths));
100100
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);
101+
int numberOfEvents = 1 + atoi(getSystemOutput("ls /dev/input | sed 's/event//' | sort -n | tail -1 | tr -d [:cntrl:]"));
102+
printf("\nHighest found input event number: %d\n", numberOfEvents);
103103
char padEvent[2][20];
104+
memset (padEvent, 0, sizeof(padEvent));
104105
for (int i = 0; i < numberOfEvents; i++) {
105106
if (numberOfPads < 2) {
106107
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+
memset (padCommand, 0, sizeof(padCommand));
109+
snprintf(padCommand, 150, "/home/pi/McAirpos/McAirpos/uinput-mapper/input-read -vp /dev/input/event%d 2>&1 | grep -e BTN_START -e BTN_SOUTH -e BTN_PINKIE", i);
108110
char* event = getSystemOutput(padCommand);
109111
if (strcmp(event, "")) {
110112
if (numberOfPads == 0) {
111-
printf("\nGamepad search hits:\n");
113+
printf("Gamepad search hits:\n");
112114
}
113115
printf("%s, Output:%s", padCommand, getSystemOutput(padCommand));
114116
char iString[20];
117+
memset (iString, 0, sizeof(iString));
115118
sprintf(iString, "%d", i);
116119
strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " ");
117120
strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " ");
@@ -123,17 +126,23 @@ int main(int argc, char** argv) {
123126
}
124127

125128
// Determine if keyboard is connected
126-
char keybCommand[] = "cat /proc/bus/input/devices | grep -B 7 EV=12001 | tr ' ' '\\n' | grep event";
129+
char keybCommand[300];
130+
memset (keybCommand, 0, sizeof(keybCommand));
131+
if (!strcmp(options, "keybswap")) {
132+
strcat(keybCommand, "cat /proc/bus/input/devices | grep -B 5 -A 5 Handlers=sysrq | grep -B 7 -A 3 -e EV=12001 -e EV=10001 | grep -B 2 -A 8 -E 'Phys=(usb\\S+\\/input0|[a-zA-Z0-9]{2}(:[a-zA-Z0-9]{2}){5}.*)' | tr ' ' '\\n' | grep event | tail -1 | tr -d [:cntrl:]");
133+
} else {
134+
strcat(keybCommand, "cat /proc/bus/input/devices | grep -B 5 -A 5 Handlers=sysrq | grep -B 7 -A 3 -e EV=12001 -e EV=10001 | grep -B 2 -A 8 -E 'Phys=(usb\\S+\\/input0|[a-zA-Z0-9]{2}(:[a-zA-Z0-9]{2}){5}.*)' | tr ' ' '\\n' | grep event | head -1 | tr -d [:cntrl:]");
135+
}
127136
char* keybEvent = getSystemOutput(keybCommand);
128137
if (strcmp(keybEvent, "")) {
129-
printf("\nKeyboard search hit:\n");
138+
printf("Keyboard search hit:\n");
130139
printf("%s, Output:%s", keybCommand, getSystemOutput(keybCommand));
131140
}
132141

133142

134143
// Automatically set up uinput-mapper with keyboard, 1 or 2 gamepads
135-
// printf("\n%s\n", eventPaths);
136144
char stringNumberOfPads[20];
145+
memset (stringNumberOfPads, 0, sizeof(stringNumberOfPads));
137146
char uiMapCommand[400];
138147
memset (uiMapCommand, 0, sizeof(uiMapCommand));
139148
char uinputMapperOrKeyboard[20];
@@ -156,14 +165,12 @@ int main(int argc, char** argv) {
156165
printf("\nFound %d gamepad to configure on:\n%s\n", numberOfPads, padEvent[0]);
157166
printf("\nFound 1 keyboard to configure on:\n/dev/input/%s\n\n", keybEvent);
158167
strcat(strcat(eventPaths, "/dev/input/"), keybEvent);
159-
eventPaths[strlen(eventPaths)-1] = 0;
160168
strcat(eventPaths, " ");
161169
sprintf(stringNumberOfPads, "%d.py)&",numberOfPads);
162170
}
163171
else if (strcmp(keybEvent, "")) {
164172
printf("\nFound 1 keyboard to configure on:\n/dev/input/%s\n\n", keybEvent);
165173
strcat(defaultEvent, keybEvent);
166-
defaultEvent[strlen(defaultEvent)-1] = 0;
167174
strcat(uinputMapperOrKeyboard, "keyboard");
168175
}
169176
else if (numberOfPads == 1) {
@@ -182,7 +189,7 @@ int main(int argc, char** argv) {
182189
if (numberOfPads > 0) {
183190
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);
184191
if (system(uiMapCommand) == 0) {
185-
printf("\nStarting UInput-Mapper with command:\n%s\n", uiMapCommand);
192+
printf("Starting UInput-Mapper with command:\n%s\n", uiMapCommand);
186193
int whileCount = 0;
187194
while (!strcmp("", getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event"))) {
188195
if (whileCount > 500) {
@@ -192,9 +199,8 @@ int main(int argc, char** argv) {
192199
}
193200
whileCount++;
194201
}
195-
char* uinputEvent = getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event");
202+
char* uinputEvent = getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event | tr -d [:cntrl:]");
196203
strcat(defaultEvent, uinputEvent);
197-
defaultEvent[strlen(defaultEvent)-1] = 0;
198204
strcat(uinputMapperOrKeyboard, "UInputMapper");
199205
} else {
200206
printf("\nUInput-Mapper failed to start...\n");
@@ -209,25 +215,39 @@ int main(int argc, char** argv) {
209215
}
210216

211217

212-
// Set default default event for MakeCode Arcade elf game file
218+
// Set default input event for MakeCode Arcade elf game file
213219
char sedCommand[100];
220+
memset (sedCommand, 0, sizeof(sedCommand));
214221
snprintf(sedCommand, 100, "sed -i \"1s&.*&\"%s\"&\" /sd/arcade.cfg", defaultEvent);
215222
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);
223+
printf("Setting up %s in MakeCode Arcade game's /sd/arcade.cfg with:\n%s\n\n", uinputMapperOrKeyboard, defaultEvent);
217224
} else {
218-
printf("\n\nPlease check path or write permissions for /sd/arcade.cfg and try again.\n\n");
225+
printf("Please check path or write permissions for /sd/arcade.cfg and try again.\n\n");
219226
goto cleanup;
220227
}
221228
}
222-
system("stty -ixon"); //Disable pause in terminal with key combo CTRL+S
223229

224230

225-
// Fork game execution on launch, so that it is executed
226-
// the same way it's done in-game on reset and finish
231+
// Ready terminal and game files
232+
int foundPxtFile = 0;
233+
system("stty -ixon intr undef susp undef"); //Disable pause(CTRL+S), suspend(CTRL+Z) and interrupt(CTRL+C) in terminal
234+
if (0 == system("head -1 /tmp/pxt-pid >>/dev/null 2>&1")) {
235+
system("sed -i \"1s&.*&\"\"&\" /tmp/pxt-pid"); //Clear old pid from /tmp/pxt if present
236+
foundPxtFile = 1;
237+
}
238+
if (strcmp("", getSystemOutput("ps -A | grep pulse"))) {
239+
system("sudo killall pulseaudio"); //Kill PulseAudio if running, can sometimes halt game looking for ALSA
240+
}
227241
char* path = "/dev/tty";
228-
int fd = open(path, O_RDWR, 0);
242+
int fd;
243+
fflush(stdout);
244+
245+
// Fork game execution on launch, so that it is executed
246+
// the same way it's done in-game on reset
229247
if (!fork()) {
248+
230249
//Switch console to graphics mode to avoid disturbing text output in borders
250+
fd = open(path, O_RDWR, 0);
231251
if (fd < 0) {
232252
perror("unable to open tty");
233253
return 1;
@@ -236,41 +256,95 @@ int main(int argc, char** argv) {
236256
perror("warn: ioctl KDSETMODE failed");
237257
}
238258
close(fd);
239-
system("sudo killall pulseaudio");
259+
260+
// Launch the game
240261
if (system(game) == 36608) {
241-
printf("\n\n%s was exited by the user\n\n\n", game);
262+
printf("%s was exited by the user\n\n", game);
242263
} else {
243-
printf("\n\nPlease check path to and executable permissions for game_file.elf and try again.\n\n\n");
264+
printf("Please check path to and executable permissions for game_file.elf and try again.\n\n");
244265
}
245-
//Alternative way to launch game
266+
//Alternative way to launch game, but I need to spawn new process in fork, not replace fork process
246267
//execl(game, game, NULL);
268+
269+
// Main thread continues
247270
}else {
248-
sleep(1); // Wait for fork/game to launch and get pid
249271

250-
// Get running game's process name
251-
char* processID = getSystemOutput("head -1 /tmp/pxt-pid");
272+
// Wait for fork/game to launch and get running game's process name
273+
struct stat pxtFileBuffer;
274+
char processID[20];
275+
memset (processID, 0, sizeof(processID));
252276
char processCommand[100];
253-
snprintf(processCommand, 100, "head -1 /proc/%s/comm", processID);
254-
char* processName = getSystemOutput(processCommand);
255-
char processCheckCmd[100];
256-
snprintf(processCheckCmd, 100, "pgrep %s", processName);
277+
memset (processCommand, 0, sizeof(processCommand));
278+
int whileCount = 0;
279+
int maxCount = 500;
280+
do {
281+
sleep(0.1);
282+
whileCount++;
283+
snprintf(processCommand, 100, "head -1 /proc/%s/comm >>/dev/null 2>&1", processID);
284+
if (whileCount > maxCount) {
285+
printf("\nTimed out trying to find game's process ID...\n");
286+
printf("If stuck, please read or open a related issue at https://github.com/Vegz78/McAirpos.\n");
287+
goto cleanup;
288+
}
289+
// Check until found if /tmp/pxt-pid file exists without opening it, time out otherwise
290+
if (0 == foundPxtFile) {
291+
if (0 != stat("/tmp/pxt-pid", &pxtFileBuffer)) {
292+
if (whileCount > maxCount -1) {
293+
printf("\nDid not find the file /tmp/pxt-pid...\n");
294+
}
295+
continue;
296+
} else {
297+
foundPxtFile = 1;
298+
whileCount = 0;
299+
}
300+
}
301+
// Set new game's processID from file /tmp/pxt-pid
302+
snprintf(processID, 20, "%s", getSystemOutput("head -1 /tmp/pxt-pid | tr -d [:cntrl:]"));
303+
}
304+
// Check whether processID from /tmp/pxt-pid is running/registered on system, time out otherwise
305+
while (0 != system(processCommand));
306+
307+
// Get name of game process
308+
snprintf(processCommand, 100, "head -1 /proc/%s/comm 2>&1 | tr -d [:cntrl:]", processID);
309+
char processName[50];
310+
memset (processName, 0, sizeof(processName));
311+
snprintf(processName, 50, "%s", getSystemOutput(processCommand));
312+
printf("\nFound running game process \"%s\" on PID=%s\n\n", processName, processID);
313+
257314

258315
// Check every 2 secounds if a game process is still active
259-
// Busy waiting to continue and cleanup when game is exited
316+
// Busy waiting to continue and clean up when game is exited
260317
// Why does this not work without the printf?!?
318+
char processCheckCmd[100];
319+
memset (processCheckCmd, 0, sizeof(processCheckCmd));
320+
snprintf(processCheckCmd, 100, "pgrep -n %s 2>&1 | tr -d [:cntrl:]", processName);
321+
char newProcessID[20];
322+
memset (newProcessID, 0, sizeof(newProcessID));
261323
besure:
262-
while (0 == system(processCheckCmd)) {
263-
printf("%s\n", getSystemOutput(processCheckCmd));
324+
while (strcmp(processID, "")) {
325+
//printf("%s@PID=%s is keeping launCharc alive...\n", processName, processID);
264326
sleep(2);
327+
snprintf(newProcessID, 20, getSystemOutput(processCheckCmd));
328+
if (strcmp(processID, newProcessID)) {
329+
if (strcmp(newProcessID, "")) {
330+
printf("\"%s\" restarted on PID=%s\n", processName, newProcessID);
331+
} else {
332+
printf("PID not found for \"%s\", trying again...\n", processName);
333+
}
334+
snprintf(processID, 20, "%s", newProcessID);
335+
}
265336
}
266337

267-
// Double check that game really has exited
268-
sleep(1);
269-
if (0 == system(processCheckCmd)) {goto besure;}
338+
// Doublecheck that the game really has exited
339+
sleep(3);
340+
snprintf(processID, 20, getSystemOutput(processCheckCmd));
341+
if (strcmp(processID, "")) {goto besure;}
342+
else {printf("PID still not found for \"%s\", game exited?\nTerminating launCharc...\n", processName);}
270343

271344

272345
// Kill any remaining/orphaned game processes before exit
273346
char killAllCmd[100];
347+
memset (killAllCmd, 0, sizeof(killAllCmd));
274348
snprintf(killAllCmd, 100, "killall %s 2>&1", processName);
275349
system(killAllCmd);
276350
cleanup:
@@ -286,7 +360,7 @@ int main(int argc, char** argv) {
286360
return 1;
287361
}
288362

289-
// This one fails without sudo, but doesn't seem needed for
363+
// This one fails without sudo, but doesn't seem needed for
290364
// MakeCode Arcade games(comment out or leave as and option?).
291365
if (ioctl(fd, VT_UNLOCKSWITCH, 1) < 0) {
292366
//perror("warn: ioctl VT_UNLOCKSWITCH failed");
@@ -300,7 +374,7 @@ int main(int argc, char** argv) {
300374
perror("warn: ioctl KBSKBMODE failed");
301375
}
302376

303-
system("stty ixon");
377+
system("stty sane");
304378
//system("clear");
305379
}
306380

0 commit comments

Comments
 (0)