/* --- Copyright University of Sussex 1996. All rights reserved. ----------
 * File:			C.win32/extern/src/process.c
 * Purpose:			Process and thread functions
 * Author:			Robert John Duncan, Jun 14 1994 (see revisions)
 * Documentation:
 * Related Files:
 */

#include "popcore.h"

	/*	Set up a std i/o handle for the child process
	*/
static void set_handle(HANDLE old, HANDLE* new) {
	HANDLE self = GetCurrentProcess();
	if (old == NULL || old == INVALID_HANDLE_VALUE ||
		!DuplicateHandle(self, old, self, new, 0, TRUE, DUPLICATE_SAME_ACCESS))
		*new = INVALID_HANDLE_VALUE;
}

BOOL pop_create_process(
		TCHAR *path,
		TCHAR *command_line,
		DWORD flags,
		BOOL useStdHandles,
		POP_PROCESS_INFO *proc
	)
{
	BOOL status;
	BOOL inheritHandles;
	PROCESS_INFORMATION pi;
	STARTUPINFO info;

	ZeroMemory(&info, sizeof info);
	info.cb = sizeof info;

	if (useStdHandles) {
		info.dwFlags = STARTF_USESTDHANDLES;
		set_handle(proc->hStdInput,  &info.hStdInput);
		set_handle(proc->hStdOutput, &info.hStdOutput);
		set_handle(proc->hStdError,  &info.hStdError);
	}

	/*	normally it's safe to inherit handles because Poplog handles by
		default are opened non-inheritable */
	inheritHandles = TRUE;
#ifdef __NUTC__
	/*	but with NuTCRACKER there can be problems because it seems as though
		the X connection is inherited, and that can confuse the server, so
		we choose to inherit only if we know we need to */
	inheritHandles = useStdHandles;
#endif

	status = CreateProcess(
			path,					/* image name */
			command_line,			/* command line */
			NULL,					/* process security */
			NULL,					/* thread security */
			inheritHandles,			/* inherit handles */
			flags,					/* create flags */
			NULL,					/* environment */
			NULL,					/* current directory */
			&info,					/* startup info */
			&pi						/* process info */
		);

	if (status) {
		/* copy process and thread handles to pop structure */
		proc->dwProcessId = pi.dwProcessId;
		proc->hProcess = pi.hProcess;
		proc->hThread = pi.hThread;
	}

	if (useStdHandles) {
		/* close handles duplicated above */
		if (info.hStdInput != INVALID_HANDLE_VALUE)
			CloseHandle(info.hStdInput);
		if (info.hStdOutput != INVALID_HANDLE_VALUE)
			CloseHandle(info.hStdOutput);
		if (info.hStdError != INVALID_HANDLE_VALUE)
			CloseHandle(info.hStdError);
	}

	return status;
}

BOOL pop_get_exit_code_process(HANDLE proc, DWORD* status) {
	return GetExitCodeProcess(proc, status);
}

BOOL pop_terminate_process(HANDLE proc) {
	return TerminateProcess(proc, 1);
}

	/*	Try to kill a process politely by sending a WM_CLOSE message
	 */
struct _enum_info {
	HANDLE	hproc;
	DWORD	pid;
};

static BOOL CALLBACK enum_windows_proc(HWND hwnd, LPARAM lParam) {
	DWORD thread, pid, status;
	struct _enum_info *info = (struct _enum_info *)lParam;
	thread = GetWindowThreadProcessId(hwnd, &pid);
	return pid != info->pid
		|| GetExitCodeProcess(info->hproc, &status)
			&& status == STILL_ACTIVE
			&& PostMessage(hwnd, WM_CLOSE, 0, 0);
}

BOOL pop_close_process_windows(HANDLE hproc, DWORD pid) {
	struct _enum_info info = { hproc, pid };
	return EnumWindows(enum_windows_proc, (LPARAM)&info);
}

	/*	Suspend/resume threads
	*/
DWORD pop_suspend_thread(HANDLE thread) {
	return SuspendThread(thread);
}

DWORD pop_resume_thread(HANDLE thread) {
	return ResumeThread(thread);
}


/* --- Revision History ---------------------------------------------------
--- Robert Duncan, Nov 18 1996
		Fixed inheritance of handles under NuTCRACKER
--- Robert Duncan, May 24 1996
		Simplified process creation and added new functions for killing,
		suspending, etc.
--- Robert John Duncan, Jan  8 1996
		Change to the way pop_create_process sets the standard I/O handles
		for the newly-created process: now uses the STARTUPINFO structure
		instead of temporarily redefining the standard handles of the caller
		(these redefinitions were seemingly ignored by Windows when run from
		a GUI process).
 */
