Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/raylib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
//------------------------------------------------------------------------------------
Expand Down
351 changes: 351 additions & 0 deletions src/rcore.c
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,62 @@
#define ACCESS(fn) access(fn, F_OK)
#endif

// Platform specific defines for process execution API
#if defined(_WIN32)
struct _STARTUPINFOA {
unsigned long cb;
char *lpReserved;
char *lpDesktop;
char *lpTitle;
unsigned long dwX;
unsigned long dwY;
unsigned long dwXSize;
unsigned long dwYSize;
unsigned long dwXCountChars;
unsigned long dwYCountChars;
unsigned long dwFillAttribute;
unsigned long dwFlags;
unsigned short wShowWindow;
unsigned short cbReserved2;
unsigned char *lpReserved2;
void *hStdInput;
void *hStdOutput;
void *hStdError;
};

struct _PROCESS_INFORMATION {
void *hProcess;
void *hThread;
unsigned long dwProcessId;
unsigned long dwThreadId;
};

__declspec(dllimport) void *__stdcall GetModuleHandleA(const char *lpModuleName);
__declspec(dllimport) void *__stdcall GetProcAddress(void *hModule, const char *lpProcName);
__declspec(dllimport) void *__stdcall OpenProcess(unsigned long dwDesiredAccess, int bInheritHandle, unsigned long dwProcessId);
__declspec(dllimport) int __stdcall CloseHandle(void *hObject);
__declspec(dllimport) int __stdcall GetExitCodeProcess(void *hProcess, unsigned long *lpExitCode);
__declspec(dllimport) int __stdcall TerminateProcess(void *hProcess, unsigned int uExitCode);
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void *hHandle, unsigned long dwMilliseconds);
__declspec(dllimport) unsigned long __stdcall SuspendThread(void *hThread);
__declspec(dllimport) unsigned long __stdcall ResumeThread(void *hThread);
__declspec(dllimport) int __stdcall CreateProcessA(
const char *lpApplicationName,
char *lpCommandLine,
void *lpProcessAttributes,
void *lpThreadAttributes,
int bInheritHandles,
unsigned long dwCreationFlags,
void *lpEnvironment,
const char *lpCurrentDirectory,
struct _STARTUPINFOA *lpStartupInfo,
struct _PROCESS_INFORMATION *lpProcessInformation);
#elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
#include <sys/wait.h> // Required for: waitpid() [Used in CheckProcess()]
#include <signal.h> // Required for: kill() [Used in PauseProcess(), ResumeProcess(), CloseProcess()]
#include <sys/types.h> // Required for: pid_t [Used as function local variable type and for Process PID type]
#endif

//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
Expand Down Expand Up @@ -4211,6 +4267,301 @@ 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(_WIN32)
// Windows requires a single command line string
char cmdLine[MAX_PATH * 4] = { 0 };
int position = 0;

TextAppend(cmdLine, command, &position);

if (args != NULL)
{
for (int i = 1; args[i] != NULL; i++)
{
// TODO: Maybe use dynamic allocation for command line instead of aborting?
if (position + TextLength(args[i]) + 1 >= sizeof(cmdLine))
{
TRACELOG(LOG_WARNING, "PROCESS: Command line too long, process will not be created");
return process;
}

TextAppend(cmdLine, " ", &position);
TextAppend(cmdLine, args[i], &position);
}
}

struct _STARTUPINFOA si = { 0 };
si.cb = sizeof(struct _STARTUPINFOA);
struct _PROCESS_INFORMATION pi = { 0 };

// Application name is specified as the first command line argument, so pass NULL to CreateProcessA here
if (CreateProcessA(NULL, cmdLine, NULL, NULL, 0, 0, NULL, NULL, &si, &pi))
{
process.pid = (int)pi.dwProcessId;
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
else
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to create process");
}

return process;
#elif 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(_WIN32)
// PROCESS_QUERY_INFORMATION
void *hProcess = OpenProcess(0x0400, 0, (unsigned long)process->pid);
if (hProcess == NULL) return false;

unsigned long exitCode = 0;
if (GetExitCodeProcess(hProcess, &exitCode))
{
CloseHandle(hProcess);
// STILL_ACTIVE (259) means the process is still running
if (exitCode == 259) return true;

process->exitCode = (int)exitCode;
process->pid = 0;
return false;
}

CloseHandle(hProcess);
return false;
#elif 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 ((process == NULL) || (process->pid <= 0))
{
return;
}
#if defined(_WIN32)
typedef long (*PFN_NtSuspendProcess)(void *handle);
static PFN_NtSuspendProcess NtSuspendProcess = NULL;

// Load NtSuspendProcess from ntdll.dll
if (NtSuspendProcess == NULL)
{
void *hNtDll = GetModuleHandleA("ntdll.dll");
if (hNtDll == NULL)
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to get handle for ntdll.dll");
return;
}

NtSuspendProcess = (PFN_NtSuspendProcess)GetProcAddress(hNtDll, "NtSuspendProcess");
if (NtSuspendProcess == NULL) {
TRACELOG(LOG_WARNING, "PROCESS: Failed to get address for NtSuspendProcess");
return;
}
}

// PROCESS_SUSPEND_RESUME
void *hProcess = OpenProcess(0x0800, 0, (unsigned long)process->pid);
if (hProcess == NULL) {
TRACELOG(LOG_WARNING, "PROCESS: Failed to open process handle");
return;
}

if (NtSuspendProcess(hProcess) != 0)
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to pause process");
}
CloseHandle(hProcess);
#elif 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 ((process == NULL) || (process->pid <= 0))
{
return;
}
#if defined(_WIN32)
typedef long (*PFN_NtResumeProcess)(void *handle);
static PFN_NtResumeProcess NtResumeProcess = NULL;

// Load NtResumeProcess from ntdll.dll
if (NtResumeProcess == NULL)
{
void *hNtDll = GetModuleHandleA("ntdll.dll");
if (hNtDll == NULL)
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to get handle for ntdll.dll");
return;
}

NtResumeProcess = (PFN_NtResumeProcess)GetProcAddress(hNtDll, "NtResumeProcess");
if (NtResumeProcess == NULL)
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to get address for NtResumeProcess");
return;
}
}

// PROCESS_SUSPEND_RESUME
void *hProcess = OpenProcess(0x0800, 0, (unsigned long)process->pid);
if (hProcess == NULL)
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to open process handle");
return;
}

if (NtResumeProcess(hProcess) != 0)
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to resume process");
}
CloseHandle(hProcess);
#elif 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(_WIN32)
// PROCESS_TERMINATE
void *hProcess = OpenProcess(0x0001, 0, (unsigned long)process->pid);
if (hProcess == NULL)
{
TRACELOG(LOG_WARNING, "PROCESS: Failed to open process handle");
return;
}

TerminateProcess(hProcess, 0);
CloseHandle(hProcess);

process->exitCode = -1;
process->pid = 0;
#elif 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
//----------------------------------------------------------------------------------
Expand Down
Loading