Skip to content

Commit

Permalink
Stability improvements
Browse files Browse the repository at this point in the history
-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
  • Loading branch information
Vegz78 committed Jan 24, 2021
1 parent c964a75 commit c135007
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 41 deletions.
Binary file modified McAirpos/launCharc/launCharc
Binary file not shown.
156 changes: 115 additions & 41 deletions McAirpos/launCharc/launCharc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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), " ");
Expand All @@ -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];
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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");
Expand All @@ -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;
Expand All @@ -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:
Expand All @@ -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");
Expand All @@ -300,7 +374,7 @@ int main(int argc, char** argv) {
perror("warn: ioctl KBSKBMODE failed");
}

system("stty ixon");
system("stty sane");
//system("clear");
}

Expand Down

0 comments on commit c135007

Please sign in to comment.