diff --git a/McAirpos/launCharc/launCharc b/McAirpos/launCharc/launCharc index 1f3033e..03a5477 100755 Binary files a/McAirpos/launCharc/launCharc and b/McAirpos/launCharc/launCharc differ diff --git a/McAirpos/launCharc/launCharc.c b/McAirpos/launCharc/launCharc.c index fa47572..4bde6fc 100644 --- a/McAirpos/launCharc/launCharc.c +++ b/McAirpos/launCharc/launCharc.c @@ -83,35 +83,38 @@ int main(int argc, char** argv) { game = argv[2]; options = argv[1]; } else if ((argc > 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"); }