diff --git a/src/raylib.h b/src/raylib.h index bdca64329..2a8c247c5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -961,6 +961,12 @@ typedef enum { NPATCH_THREE_PATCH_HORIZONTAL // Npatch layout: 3x1 tiles } NPatchLayout; +// Process info +typedef struct Process { + int pid; // Process unique identifier + int exitCode; // Process exit code +} Process; + // Callbacks to hook some internal functions // WARNING: These callbacks are intended for advanced users typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages @@ -1241,6 +1247,13 @@ RLAPI Vector2 GetTouchPosition(int index); // Get touch posit RLAPI int GetTouchPointId(int index); // Get touch point identifier for given index RLAPI int GetTouchPointCount(void); // Get number of touch points +// Process execution functions +RLAPI Process InitProcess(const char *command, char *const args[]); // Initialize a new process, returns a Process struct +RLAPI bool CheckProcess(Process *process); // Check if a process is still running, updates Process struct +RLAPI void PauseProcess(Process *process); // Pause process execution +RLAPI void ResumeProcess(Process *process); // Resume paused process execution +RLAPI void CloseProcess(Process *process); // Close process and free resources + //------------------------------------------------------------------------------------ // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ diff --git a/src/rcore.c b/src/rcore.c index c3d61f3da..919a91ad4 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -210,6 +210,12 @@ #define ACCESS(fn) access(fn, F_OK) #endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + #include // Required for: waitpid() [Used in CheckProcess()] + #include // Required for: kill() [Used in PauseProcess(), ResumeProcess(), CloseProcess()] + #include // Required for: pid_t [Used as function local variable type and for Process PID type] +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -4211,6 +4217,153 @@ int GetTouchPointCount(void) return CORE.Input.Touch.pointCount; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Process Execution +//---------------------------------------------------------------------------------- + +// Initialize a new process, returns a Process struct +RLAPI Process InitProcess(const char *command, char *const args[]) +{ + // The last element of the args array must be NULL + + Process process = { 0 }; + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + pid_t pid = fork(); + + if (pid < 0) + { + TRACELOG(LOG_WARNING, "PROCESS: Failed to create process"); + } + else if (pid == 0) + { + // Child process + execvp(command, args); + + // If execvp returns, it must have failed + _exit(1); + } + else + { + process.pid = pid; + } +#else + TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform"); +#endif + + return process; +} + +// Check if a process is still running, updates Process struct +RLAPI bool CheckProcess(Process *process) +{ + if ((process == NULL) || (process->pid <= 0)) + { + return false; + } + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + int status = 0; + pid_t result = waitpid(process->pid, &status, WNOHANG); + + if (result == 0) + { + // Still running + return true; + } + else if (result == process->pid) + { + // Not running + if (WIFEXITED(status)) + { + process->exitCode = WEXITSTATUS(status); + } + else if (WIFSIGNALED(status)) + { + // Terminated by signal + process->exitCode = -1; + } + + // Mark as completed + process->pid = 0; + + return false; + } + else + { + // Error occurred + TRACELOG(LOG_WARNING, "PROCESS: Failed to check process status"); + return false; + } +#else + TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform"); +#endif + + return false; +} + +// Pause process execution +RLAPI void PauseProcess(Process *process) +{ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + if (kill(process->pid, SIGSTOP) != 0) + { + TRACELOG(LOG_WARNING, "PROCESS: Failed to pause process"); + } +#else + TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform"); +#endif +} + +// Resume paused process execution +RLAPI void ResumeProcess(Process *process) +{ +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + if (kill(process->pid, SIGCONT) != 0) + { + TRACELOG(LOG_WARNING, "PROCESS: Failed to resume process"); + } +#else + TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform"); +#endif +} + +// Close process and free resources +RLAPI void CloseProcess(Process *process) +{ + if ((process == NULL) || (process->pid <= 0)) + { + return; + } + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + if (!CheckProcess(process)) + { + return; + } + + // In case that the process is paused + kill(process->pid, SIGCONT); + + if (kill(process->pid, SIGTERM) != 0) + { + TRACELOG(LOG_WARNING, "PROCESS: Failed to terminate process"); + + // Kill the process anyway + kill(process->pid, SIGKILL); + } + + // Wait process termination + waitpid(process->pid, NULL, 0); + + // Align as CheckProcess() would do for a killed process + process->exitCode = -1; + process->pid = 0; +#else + TRACELOG(LOG_WARNING, "PROCESS: Process management not supported on this platform"); +#endif +} + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //----------------------------------------------------------------------------------