From c1350073bc3ba21be16c098a44f46469a3658fd4 Mon Sep 17 00:00:00 2001 From: Vegz78 <49032025+Vegz78@users.noreply.github.com> Date: Sun, 24 Jan 2021 04:30:22 +0100 Subject: [PATCH] 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 --- McAirpos/launCharc/launCharc | Bin 23840 -> 30940 bytes McAirpos/launCharc/launCharc.c | 156 ++++++++++++++++++++++++--------- 2 files changed, 115 insertions(+), 41 deletions(-) diff --git a/McAirpos/launCharc/launCharc b/McAirpos/launCharc/launCharc index 1f3033e29fa6087b999a4b6ac9d613fc74832e29..03a547781ba2ff3b6771bc6ce5cbe1825c05c3b1 100755 GIT binary patch literal 30940 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 3) || (argc < 2)) { - printf("usage: launchArcade [nomap] [/path/to/arcadegame.elf]\n"); + printf("usage: launchArcade [nomap / keybswap] [/path/to/arcadegame.elf]\n"); return 1; } // Check for nomap option if (!strcmp(options, "nomap")) { - printf("%s argument detected,\nStarting %s with no automatic gamepad mappings...\n", options, game); + printf("%s argument detected,\nlaunCharc starting %s with no automatic gamepad mappings...\n", options, game); sleep(1); } else { // Determine the number of connected gamepads - printf("Starting %s with automatic gamepad mappings...\n\n", game); + printf("launCharc starting %s with automatic gamepad mappings...\n", game); char eventPaths[100]; memset (eventPaths, 0, sizeof(eventPaths)); int numberOfPads = 0; - int numberOfEvents = 1 + atoi(getSystemOutput("ls /dev/input | sed 's/event//' | sort -n | tail -1")); - printf("\nHighest found input event number: %d\n\n", numberOfEvents); + int numberOfEvents = 1 + atoi(getSystemOutput("ls /dev/input | sed 's/event//' | sort -n | tail -1 | tr -d [:cntrl:]")); + printf("\nHighest found input event number: %d\n", numberOfEvents); char padEvent[2][20]; + memset (padEvent, 0, sizeof(padEvent)); for (int i = 0; i < numberOfEvents; i++) { if (numberOfPads < 2) { char padCommand[150]; - 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); + memset (padCommand, 0, sizeof(padCommand)); + 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); char* event = getSystemOutput(padCommand); if (strcmp(event, "")) { if (numberOfPads == 0) { - printf("\nGamepad search hits:\n"); + printf("Gamepad search hits:\n"); } printf("%s, Output:%s", padCommand, getSystemOutput(padCommand)); char iString[20]; + memset (iString, 0, sizeof(iString)); sprintf(iString, "%d", i); strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " "); strcat(strcat(strcat(eventPaths, "/dev/input/event"), iString), " "); @@ -123,17 +126,23 @@ int main(int argc, char** argv) { } // Determine if keyboard is connected - char keybCommand[] = "cat /proc/bus/input/devices | grep -B 7 EV=12001 | tr ' ' '\\n' | grep event"; + char keybCommand[300]; + memset (keybCommand, 0, sizeof(keybCommand)); + if (!strcmp(options, "keybswap")) { + 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:]"); + } else { + 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:]"); + } char* keybEvent = getSystemOutput(keybCommand); if (strcmp(keybEvent, "")) { - printf("\nKeyboard search hit:\n"); + printf("Keyboard search hit:\n"); printf("%s, Output:%s", keybCommand, getSystemOutput(keybCommand)); } // Automatically set up uinput-mapper with keyboard, 1 or 2 gamepads - // printf("\n%s\n", eventPaths); char stringNumberOfPads[20]; + memset (stringNumberOfPads, 0, sizeof(stringNumberOfPads)); char uiMapCommand[400]; memset (uiMapCommand, 0, sizeof(uiMapCommand)); char uinputMapperOrKeyboard[20]; @@ -156,14 +165,12 @@ int main(int argc, char** argv) { printf("\nFound %d gamepad to configure on:\n%s\n", numberOfPads, padEvent[0]); printf("\nFound 1 keyboard to configure on:\n/dev/input/%s\n\n", keybEvent); strcat(strcat(eventPaths, "/dev/input/"), keybEvent); - eventPaths[strlen(eventPaths)-1] = 0; strcat(eventPaths, " "); sprintf(stringNumberOfPads, "%d.py)&",numberOfPads); } else if (strcmp(keybEvent, "")) { printf("\nFound 1 keyboard to configure on:\n/dev/input/%s\n\n", keybEvent); strcat(defaultEvent, keybEvent); - defaultEvent[strlen(defaultEvent)-1] = 0; strcat(uinputMapperOrKeyboard, "keyboard"); } else if (numberOfPads == 1) { @@ -182,7 +189,7 @@ int main(int argc, char** argv) { if (numberOfPads > 0) { 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); if (system(uiMapCommand) == 0) { - printf("\nStarting UInput-Mapper with command:\n%s\n", uiMapCommand); + printf("Starting UInput-Mapper with command:\n%s\n", uiMapCommand); int whileCount = 0; while (!strcmp("", getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event"))) { if (whileCount > 500) { @@ -192,9 +199,8 @@ int main(int argc, char** argv) { } whileCount++; } - char* uinputEvent = getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event"); + char* uinputEvent = getSystemOutput("cat /proc/bus/input/devices | grep -A 8 \"UInputMapper: MakeCode_Arcade\" | tr ' ' '\n' | grep event | tr -d [:cntrl:]"); strcat(defaultEvent, uinputEvent); - defaultEvent[strlen(defaultEvent)-1] = 0; strcat(uinputMapperOrKeyboard, "UInputMapper"); } else { printf("\nUInput-Mapper failed to start...\n"); @@ -209,25 +215,39 @@ int main(int argc, char** argv) { } - // Set default default event for MakeCode Arcade elf game file + // Set default input event for MakeCode Arcade elf game file char sedCommand[100]; + memset (sedCommand, 0, sizeof(sedCommand)); snprintf(sedCommand, 100, "sed -i \"1s&.*&\"%s\"&\" /sd/arcade.cfg", defaultEvent); if (system(sedCommand) == 0) { - printf("\n\nSetting up %s in MakeCode Arcade game's /sd/arcade.cfg with:\n%s\n\n", uinputMapperOrKeyboard, defaultEvent); + printf("Setting up %s in MakeCode Arcade game's /sd/arcade.cfg with:\n%s\n\n", uinputMapperOrKeyboard, defaultEvent); } else { - printf("\n\nPlease check path or write permissions for /sd/arcade.cfg and try again.\n\n"); + printf("Please check path or write permissions for /sd/arcade.cfg and try again.\n\n"); goto cleanup; } } - system("stty -ixon"); //Disable pause in terminal with key combo CTRL+S - // Fork game execution on launch, so that it is executed - // the same way it's done in-game on reset and finish + // Ready terminal and game files + int foundPxtFile = 0; + system("stty -ixon intr undef susp undef"); //Disable pause(CTRL+S), suspend(CTRL+Z) and interrupt(CTRL+C) in terminal + if (0 == system("head -1 /tmp/pxt-pid >>/dev/null 2>&1")) { + system("sed -i \"1s&.*&\"\"&\" /tmp/pxt-pid"); //Clear old pid from /tmp/pxt if present + foundPxtFile = 1; + } + if (strcmp("", getSystemOutput("ps -A | grep pulse"))) { + system("sudo killall pulseaudio"); //Kill PulseAudio if running, can sometimes halt game looking for ALSA + } char* path = "/dev/tty"; - int fd = open(path, O_RDWR, 0); + int fd; + fflush(stdout); + + // Fork game execution on launch, so that it is executed + // the same way it's done in-game on reset if (!fork()) { + //Switch console to graphics mode to avoid disturbing text output in borders + fd = open(path, O_RDWR, 0); if (fd < 0) { perror("unable to open tty"); return 1; @@ -236,41 +256,95 @@ int main(int argc, char** argv) { perror("warn: ioctl KDSETMODE failed"); } close(fd); - system("sudo killall pulseaudio"); + + // Launch the game if (system(game) == 36608) { - printf("\n\n%s was exited by the user\n\n\n", game); + printf("%s was exited by the user\n\n", game); } else { - printf("\n\nPlease check path to and executable permissions for game_file.elf and try again.\n\n\n"); + printf("Please check path to and executable permissions for game_file.elf and try again.\n\n"); } - //Alternative way to launch game + //Alternative way to launch game, but I need to spawn new process in fork, not replace fork process //execl(game, game, NULL); + + // Main thread continues }else { - sleep(1); // Wait for fork/game to launch and get pid - // Get running game's process name - char* processID = getSystemOutput("head -1 /tmp/pxt-pid"); + // Wait for fork/game to launch and get running game's process name + struct stat pxtFileBuffer; + char processID[20]; + memset (processID, 0, sizeof(processID)); char processCommand[100]; - snprintf(processCommand, 100, "head -1 /proc/%s/comm", processID); - char* processName = getSystemOutput(processCommand); - char processCheckCmd[100]; - snprintf(processCheckCmd, 100, "pgrep %s", processName); + memset (processCommand, 0, sizeof(processCommand)); + int whileCount = 0; + int maxCount = 500; + do { + sleep(0.1); + whileCount++; + snprintf(processCommand, 100, "head -1 /proc/%s/comm >>/dev/null 2>&1", processID); + if (whileCount > maxCount) { + printf("\nTimed out trying to find game's process ID...\n"); + printf("If stuck, please read or open a related issue at https://github.com/Vegz78/McAirpos.\n"); + goto cleanup; + } + // Check until found if /tmp/pxt-pid file exists without opening it, time out otherwise + if (0 == foundPxtFile) { + if (0 != stat("/tmp/pxt-pid", &pxtFileBuffer)) { + if (whileCount > maxCount -1) { + printf("\nDid not find the file /tmp/pxt-pid...\n"); + } + continue; + } else { + foundPxtFile = 1; + whileCount = 0; + } + } + // Set new game's processID from file /tmp/pxt-pid + snprintf(processID, 20, "%s", getSystemOutput("head -1 /tmp/pxt-pid | tr -d [:cntrl:]")); + } + // Check whether processID from /tmp/pxt-pid is running/registered on system, time out otherwise + while (0 != system(processCommand)); + + // Get name of game process + snprintf(processCommand, 100, "head -1 /proc/%s/comm 2>&1 | tr -d [:cntrl:]", processID); + char processName[50]; + memset (processName, 0, sizeof(processName)); + snprintf(processName, 50, "%s", getSystemOutput(processCommand)); + printf("\nFound running game process \"%s\" on PID=%s\n\n", processName, processID); + // Check every 2 secounds if a game process is still active - // Busy waiting to continue and cleanup when game is exited + // Busy waiting to continue and clean up when game is exited // Why does this not work without the printf?!? + char processCheckCmd[100]; + memset (processCheckCmd, 0, sizeof(processCheckCmd)); + snprintf(processCheckCmd, 100, "pgrep -n %s 2>&1 | tr -d [:cntrl:]", processName); + char newProcessID[20]; + memset (newProcessID, 0, sizeof(newProcessID)); besure: - while (0 == system(processCheckCmd)) { - printf("%s\n", getSystemOutput(processCheckCmd)); + while (strcmp(processID, "")) { + //printf("%s@PID=%s is keeping launCharc alive...\n", processName, processID); sleep(2); + snprintf(newProcessID, 20, getSystemOutput(processCheckCmd)); + if (strcmp(processID, newProcessID)) { + if (strcmp(newProcessID, "")) { + printf("\"%s\" restarted on PID=%s\n", processName, newProcessID); + } else { + printf("PID not found for \"%s\", trying again...\n", processName); + } + snprintf(processID, 20, "%s", newProcessID); + } } - // Double check that game really has exited - sleep(1); - if (0 == system(processCheckCmd)) {goto besure;} + // Doublecheck that the game really has exited + sleep(3); + snprintf(processID, 20, getSystemOutput(processCheckCmd)); + if (strcmp(processID, "")) {goto besure;} + else {printf("PID still not found for \"%s\", game exited?\nTerminating launCharc...\n", processName);} // Kill any remaining/orphaned game processes before exit char killAllCmd[100]; + memset (killAllCmd, 0, sizeof(killAllCmd)); snprintf(killAllCmd, 100, "killall %s 2>&1", processName); system(killAllCmd); cleanup: @@ -286,7 +360,7 @@ int main(int argc, char** argv) { return 1; } - // This one fails without sudo, but doesn't seem needed for + // This one fails without sudo, but doesn't seem needed for // MakeCode Arcade games(comment out or leave as and option?). if (ioctl(fd, VT_UNLOCKSWITCH, 1) < 0) { //perror("warn: ioctl VT_UNLOCKSWITCH failed"); @@ -300,7 +374,7 @@ int main(int argc, char** argv) { perror("warn: ioctl KBSKBMODE failed"); } - system("stty ixon"); + system("stty sane"); //system("clear"); }