#define _WIN32_WINNT 0xFFFF		// Required by MinGW
#include "Core/Core.h"

#if defined(PLATFORM_WIN32) 
	#include <shellapi.h>
	#include <Tlhelp32.h>
	#include <psapi.h>
	#include <Winioctl.h>
	#define CY tagCY
	// To compile in MinGW copy files Rpcsal.h, DispEx.h, WbemCli.h, WbemDisp.h, Wbemidl.h, 
	// WbemProv.h and WbemTran.h from /plugin to MinGW/include and 
	// wbemuuid.lib from /plugin to MinGW/lib
	#include <rpcsal.h>	
	#include <Wbemidl.h>
	#include <winnls.h> 
	#include <vfw.h>
	typedef ACCESS_MASK REGSAM;
	#include <PowrProf.h>
#endif
#ifdef PLATFORM_POSIX
	#include <sys/time.h>
	#include <sys/resource.h>
	
	#include <fcntl.h>
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <sys/vfs.h> 
	#include <sys/utsname.h>
	#include <dirent.h>
	#include <pwd.h>
	#include <grp.h>
	#include <time.h>
	#include <signal.h>
	#include <sys/reboot.h>
//#include <linux/kd.h>
//#include <sys/ioctl.h>
	
	#include <X11/Xlib.h>
	#include <X11/Xos.h>
	#include <X11/Xfuncs.h>
	#include <X11/Xutil.h>
	#include <X11/Xatom.h>
	#include <X11/extensions/XTest.h>
#endif

using namespace Upp;

#define _Browser_Browser_h
#include "SysInfo.h"


/////////////////////////////////////////////////////////////////////
// Hardware Info
#if defined(PLATFORM_WIN32) 
		
bool GetWMIInfo(String system, Array <String> &data, Array <Value> *ret[], String nameSpace = "root\\cimv2")
{
	HRESULT hRes;
	
	hRes = CoInitialize(NULL);
	if (hRes != S_OK && hRes != S_FALSE)
		return false;

	hRes = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT,
			RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, 0);
	if (hRes != S_OK && hRes != RPC_E_TOO_LATE) {
		CoUninitialize();
		return false;
	}
	IWbemLocator* pIWbemLocator = NULL;
	if (CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, 
		CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IUnknown, (void **)&pIWbemLocator) != S_OK) {
		CoUninitialize();
		return false;
	}
	
	BSTR bstrNamespace = SysAllocString(nameSpace.ToWString());
	IWbemServices* pWbemServices = NULL;
	if (pIWbemLocator->ConnectServer(bstrNamespace, NULL, NULL, NULL, 0, NULL, NULL,
		&pWbemServices) != S_OK) {
		CoUninitialize();
		return false;
	}
	SysFreeString(bstrNamespace);
	
	hRes = CoSetProxyBlanket(pWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, 
			RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE);
    if (FAILED(hRes)) {
        pWbemServices->Release();
        pIWbemLocator->Release();     
        CoUninitialize();
        return false;
    }
	
	IEnumWbemClassObject* pEnumerator = NULL;
	String query;
	query << "Select * from " << system;
	WCHAR wquery[1024*sizeof(WCHAR)];
	MultiByteToWideChar(CP_UTF8, 0, query, -1, wquery, sizeof(wquery)/sizeof(wquery[0]));
	BSTR strQuery = SysAllocString(wquery);
	BSTR strQL = SysAllocString(L"WQL");
	hRes = pWbemServices->ExecQuery(strQL, strQuery, WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
	if (hRes != S_OK) {
        pWbemServices->Release();
        pIWbemLocator->Release();     
        CoUninitialize();
        return false;
    }
	SysFreeString(strQuery);
	SysFreeString(strQL);

    IWbemClassObject *pClassObject;
    ULONG uReturn = 0;
    int row = 0;
    bool rt = false;
	while (pEnumerator) {
        hRes = pEnumerator->Next(WBEM_INFINITE, 1, &pClassObject, &uReturn);
       	if(0 == uReturn) {
       		if (rt)
       			break;
       		else {
	   			pIWbemLocator->Release();
				pWbemServices->Release();
				pEnumerator->Release();
				CoUninitialize(); 
	            return false;
       		}
       	}
		if(hRes != S_OK) {
	        pWbemServices->Release();
	        pIWbemLocator->Release(); 
	        pEnumerator->Release(); 
	        pClassObject->Release();   
	        CoUninitialize();
	        return false;
	    }
		for (int col = 0; col < data.GetCount(); ++col) {
			VARIANT vProp;
			BSTR strClassProp = SysAllocString(data[col].ToWString());
	        hRes = pClassObject->Get(strClassProp, 0, &vProp, 0, 0);
	        if(hRes != S_OK){
		        pWbemServices->Release();
		        pIWbemLocator->Release(); 
		        pEnumerator->Release(); 
		        pClassObject->Release();   
		        CoUninitialize();
		        return false;
		    }
			SysFreeString(strClassProp);        
			ret[col]->Add(GetVARIANT(vProp));
			rt = true;
		}
		row++;
    }
	pIWbemLocator->Release();
	pWbemServices->Release();
	pEnumerator->Release();
	pClassObject->Release();
	CoUninitialize(); 
	
	return true;
}
bool GetWMIInfo(String system, String data, Value &res, String nameSpace = "root\\cimv2") {
	Array <Value> arrRes;
	Array <Value> *arrResP[1];
	arrResP[0] = &arrRes;
	Array <String> arrData;
	arrData.Add(data);
	bool ret = GetWMIInfo(system, arrData, arrResP, nameSpace);
	if (ret)
		res = arrRes[0];
	return ret;
}
	
void GetSystemInfo(String &manufacturer, String &productName, String &version, int &numberOfProcessors, String &mbSerial) { 
	manufacturer = "";
	Value vmanufacturer;
	if (GetWMIInfo("Win32_ComputerSystem", "manufacturer", vmanufacturer)) 
		manufacturer = Trim(vmanufacturer);
	if (manufacturer.IsEmpty()) 
		manufacturer = FromSystemCharset(GetWinRegString("SystemManufacturer", "HARDWARE\\DESCRIPTION\\System\\BIOS", HKEY_LOCAL_MACHINE));
	if (manufacturer.IsEmpty()) {
		StringParse fileData = LoadFile(AppendFileName(GetSystemFolder(), "oeminfo.ini"));
		fileData.GoAfter("Manufacturer=");
		manufacturer = fileData.GetText("\r\n");
	}
	productName = "";
	Value vproductName;
	if (GetWMIInfo("Win32_ComputerSystem", "model", vproductName)) 
		productName = Trim(vproductName);
	if (productName.IsEmpty())
		productName = FromSystemCharset(GetWinRegString("SystemProductName", "HARDWARE\\DESCRIPTION\\System\\BIOS", HKEY_LOCAL_MACHINE));
	if (productName.IsEmpty())
		productName = FromSystemCharset(GetWinRegString("Model", "SOFTWARE\\Microsoft\\PCHealth\\HelpSvc\\OEMInfo", HKEY_LOCAL_MACHINE));
	
	version = FromSystemCharset(GetWinRegString("SystemVersion", "HARDWARE\\DESCRIPTION\\System\\BIOS", HKEY_LOCAL_MACHINE));
	numberOfProcessors = atoi(GetEnv("NUMBER_OF_PROCESSORS"));
	Value vmbSerial;
	if (GetWMIInfo("Win32_BaseBoard", "SerialNumber", vmbSerial)) 
		mbSerial = Trim(vmbSerial);	
}
void GetBiosInfo(String &biosVersion, Date &biosReleaseDate, String &biosSerial) { 
	// Alternative is "wmic bios get name" and "wmic bios get releasedate"
	String strBios = FromSystemCharset(GetWinRegString("BIOSVersion", "HARDWARE\\DESCRIPTION\\System\\BIOS", HKEY_LOCAL_MACHINE));	
	if (strBios.IsEmpty())
		strBios = FromSystemCharset(GetWinRegString("SystemBiosVersion", "HARDWARE\\DESCRIPTION\\System", HKEY_LOCAL_MACHINE));	
	for (int i = 0; i < strBios.GetLength(); ++i) {
		if (strBios[i] == '\0')
			biosVersion.Cat(". ");
		else
			biosVersion.Cat(strBios[i]);
	}
	String strDate = FromSystemCharset(GetWinRegString("BIOSReleaseDate", "HARDWARE\\DESCRIPTION\\System\\BIOS", HKEY_LOCAL_MACHINE));
	if (strDate.IsEmpty())
		strDate = FromSystemCharset(GetWinRegString("SystemBiosDate", "HARDWARE\\DESCRIPTION\\System", HKEY_LOCAL_MACHINE));
	int lang = GetCurrentLanguage();
	SetLanguage(LNG_ENGLISH);
	if (!StrToDate(biosReleaseDate, strDate))
		biosReleaseDate = Null;
	SetLanguage(lang);
	Value vmbSerial;
	if (GetWMIInfo("Win32_BIOS", "SerialNumber", vmbSerial)) 
		biosSerial = Trim(vmbSerial);	
}
bool GetProcessorInfo(int number, String &vendor, String &identifier, String &architecture, int &speed)	{
	String strReg = Format("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", number);
	vendor = FromSystemCharset(GetWinRegString("VendorIdentifier", strReg, HKEY_LOCAL_MACHINE));	
	identifier = FromSystemCharset(GetWinRegString("ProcessorNameString", strReg, HKEY_LOCAL_MACHINE));	
	architecture = FromSystemCharset(GetWinRegString("Identifier", strReg, HKEY_LOCAL_MACHINE));	
	speed = GetWinRegInt("~MHz", strReg, HKEY_LOCAL_MACHINE);
	
	return true;
}
/*
String GetMacAddressWMI() {
	Value vmac;
	if (!GetWMIInfo("Win32_NetworkAdapterConfiguration", "MacAddress", vmac)) 
		return Null;
	String mac = Trim(vmac);	
	if (mac.GetCount() > 0)
		return mac;
	return Null;
}
*/
#include <winsock2.h>
#include <iphlpapi.h>

Array <NetAdapter> GetAdapterInfo() {
	PIP_ADAPTER_ADDRESSES pAddresses = NULL;
	ULONG family = AF_UNSPEC;
	DWORD flags = GAA_FLAG_INCLUDE_PREFIX;
	ULONG outBufLen = 0;
	Buffer<BYTE> pBuffer;
	Array <NetAdapter> ret;
	
	switch (GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen)) {
	case ERROR_BUFFER_OVERFLOW:
		pBuffer.Alloc(outBufLen);
		pAddresses = (PIP_ADAPTER_ADDRESSES)~pBuffer;
		break;
	case ERROR_NO_DATA:
	case ERROR_INVALID_FUNCTION:
	default:
		return ret;
	}
	if (NO_ERROR != GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen)) 
		return ret;
	
	for (PIP_ADAPTER_ADDRESSES pAdd = pAddresses; pAdd; pAdd = pAdd->Next) {
		NetAdapter &adapter = ret.Add();
		int len = min((DWORD)6, pAdd->PhysicalAddressLength);
		if (len > 0)
			adapter.mac = ToUpper(HexString(pAdd->PhysicalAddress, len, 1, ':'));
		adapter.description = Trim(WideToString(pAdd->Description));
		adapter.fullname = Trim(WideToString(pAdd->FriendlyName));
		switch (pAdd->IfType) {
		case IF_TYPE_ETHERNET_CSMACD: 		adapter.type = "ETHERNET";	break;
		case IF_TYPE_ISO88025_TOKENRING: 	adapter.type = "TOKENRING";	break;
		case IF_TYPE_PPP: 					adapter.type = "MODEM";		break;
		case IF_TYPE_SOFTWARE_LOOPBACK: 	adapter.type = "SOFTWARE_LOOPBACK";		break;
		case IF_TYPE_ATM: 					adapter.type = "ATM";		break;
		case IF_TYPE_IEEE80211: 			adapter.type = "IEEE80211";	break;
		case IF_TYPE_TUNNEL: 				adapter.type = "TUNNEL";	break;
		case IF_TYPE_IEEE1394: 				adapter.type = "IEEE1394";	break;
		default: 							adapter.type = "OTHER";
		}
		if (adapter.type == "ETHERNET" && ToLower(adapter.description).Find("wireless") >= 0)
			adapter.type = "IEEE80211";
	}
	return ret;
}

String GetHDSerial() {
	Value vmbSerial;
	if (!GetWMIInfo("Win32_PhysicalMedia", "SerialNumber", vmbSerial)) 
		return Null;
	String serial = Trim(vmbSerial);	
	if (serial.GetCount() > 0)
		return serial;
	return Null;
}

bool GetVideoInfo(Array <Value> &name, Array <Value> &description, Array <Value> &videoProcessor, Array <Value> &ram, Array <Value> &videoMode) {
	Array <Value> *res[5];
	res[0] = &name;
	res[1] = &description;
	res[2] = &videoProcessor;
	res[3] = &ram;
	res[4] = &videoMode;
	Array <String> data;
	data.Add("Name");
	data.Add("Description");
	data.Add("VideoProcessor");
	data.Add("AdapterRAM");
	data.Add("VideoModeDescription");
	if (!GetWMIInfo("Win32_VideoController", data, res))
		return false;
	
	for (int row = 0; row < ram.GetCount(); ++row) 
		ram[row] = (atoi(ram[row].ToString()) + 524288)/1048576;
	return true;
}
bool GetPackagesInfo(Array <Value> &name, Array <Value> &version, Array <Value> &vendor, 
Array <Value> &installDate, Array <Value> &caption, Array <Value> &description, Array <Value> &state)
{
	Array <Value> *res[7];
	res[0] = &name;
	res[1] = &version;
	res[2] = &vendor;
	res[3] = &installDate;	
	res[4] = &caption;	
	res[5] = &description;	
	res[6] = &state;	
	Array <String> data;
	data.Add("Name");
	data.Add("Version");
	data.Add("Vendor");
	data.Add("InstallDate2");
	data.Add("Caption");
	data.Add("Description");
	data.Add("InstallState");
	if (!GetWMIInfo("Win32_Product", data, res))
		return false;
	
	for (int i = 0; i < installDate.GetCount(); ++i) {
		String sdate = installDate[i];
		Time t(atoi(sdate.Left(4)), atoi(sdate.Mid(4, 2)), atoi(sdate.Mid(6, 2)), 
			   atoi(sdate.Mid(8, 2)), atoi(sdate.Mid(10, 2)), atoi(sdate.Mid(12, 2)));
		installDate[i] = t;
		int istate = state[i];
		switch (istate) {
		case -6:	state[i] = "Bad Configuration";	break;
		case -2:	state[i] = "Invalid Argument";	break;
		case -1:	state[i] = "Unknown Package";	break;
		case 1:		state[i] = "Advertised";		break;
		case 2:		state[i] = "Absent";			break;
		case 5:		state[i] = "Ok";				break;
		default: 	return false;
		}
	}
	return true;
}
double GetCpuTemperature() {
	Value data;
	if (GetWMIInfo("MSAcpi_ThermalZoneTemperature", "CurrentTemperature", data, "root\\wmi"))
		return (double(data) - 2732.) / 10.;
	if (GetWMIInfo("Win32_TemperatureProbe", "CurrentReading", data))
		return data;
	return Null;
}
#endif

String GetMacAddress() {
	Array <NetAdapter> ret = GetAdapterInfo();	
	if (!ret.IsEmpty())
		return ret[0].mac;
	else
		return Null;
}

#if defined (PLATFORM_POSIX)
void GetSystemInfo(String &manufacturer, String &productName, String &version, int &numberOfProcessors, String &mbSerial)
{
	manufacturer = LoadFile_Safe("/sys/devices/virtual/dmi/id/board_vendor");
	productName = LoadFile_Safe("/sys/devices/virtual/dmi/id/board_name");
	version = LoadFile_Safe("/sys/devices/virtual/dmi/id/product_version");
	mbSerial = LoadFile_Safe("/sys/devices/virtual/dmi/id/board_serial");
	if (mbSerial.IsEmpty()) 
		mbSerial = FormatInt(gethostid());	

	StringParse cpu(LoadFile_Safe("/proc/cpuinfo"));	
	numberOfProcessors = 1;
	while (cpu.GoAfter("processor")) {
		cpu.GoAfter(":");
		numberOfProcessors = atoi(cpu.GetText()) + 1;
	} 
}
void GetBiosInfo(String &biosVersion, Date &biosReleaseDate, String &biosSerial)
{
	String biosVendor = LoadFile_Safe("/sys/devices/virtual/dmi/id/bios_vendor");
	biosVersion = LoadFile_Safe("/sys/devices/virtual/dmi/id/bios_version");
	StrToDate(biosReleaseDate, LoadFile_Safe("/sys/devices/virtual/dmi/id/bios_date"));
	biosSerial = LoadFile_Safe("/sys/devices/virtual/dmi/id/chassis_serial");
}
bool GetProcessorInfo(int number, String &vendor, String &identifier, String &architecture, int &speed)	
{
	StringParse cpu(LoadFile_Safe("/proc/cpuinfo"));
	
	int cpuNumber;
	do {
		if (!cpu.GoAfter("processor", ":"))
			return false;
		String sCpu = cpu.GetText();
		if (sCpu == "")
			return false;
		cpuNumber = atoi(sCpu);
	} while (cpuNumber != number);
		
	cpu.GoAfter("vendor_id", ":");
	vendor = cpu.GetText();
	cpu.GoAfter("cpu family", ":");
	String family = cpu.GetText();		// 6 means 686
	cpu.GoAfter("model", ":");
	String model = cpu.GetText();
	cpu.GoAfter("model name", ":");
	identifier = cpu.GetText("\n");
	cpu.GoAfter("stepping", ":");
	String stepping = cpu.GetText();
	architecture = Sys("uname -m");		// CPU type
	architecture << " Family " << family << " Model " << model << " Stepping " << stepping;		// And 64 bits ?? uname -m
	cpu.GoAfter_Init("cpu MHz", ":");
	speed = cpu.GetInt();
}
double GetCpuTemperature() {
/* This is only if acpi package is present
	StringParse data = Sys("acpi -V");	
	data.GoAfter("Thermal", ",");
	return data.GetDouble();
*/
	FindFile ff;
	if(ff.Search("/proc/acpi/thermal_zone/*")) {
		do {
			if (ff.IsDirectory()) {
				String name = ff.GetName();
				if (name != "." && name != "..") {
					StringParse str = LoadFile_Safe(AppendFileName(AppendFileName("/proc/acpi/thermal_zone", name), "temperature"));			
					str.GoAfter("temperature:");
					return str.GetDouble();
				}
			}
		} while(ff.Next());
	}
	return Null;
}

Array <NetAdapter> GetAdapterInfo() {
	Array <NetAdapter> ret;
	
	StringParse data = Sys("ifconfig -a");
	for (StringParse line = data.GetLine(); !data.Eof(); line = data.GetLine()) {
		NetAdapter &adapter = ret.Add();
		String str = line.GetText();				
		if (str.Find("eth") >= 0)
			adapter.type = "ETHERNET";
		else if (str.Find("lo") >= 0)
			adapter.type = "SOFTWARE_LOOPBACK";
		else if (str.Find("ppp") >= 0)
			adapter.type = "MODEM";		
		else if (str.Find("hci") >= 0)
			adapter.type = "BLUETOOTH";
		else if (str.Find("tr") >= 0)
			adapter.type = "TOKENRING";
		else if (str.Find("vbox") >= 0 || str.Find("wifi") >= 0 || str.Find("ath") >= 0)
			adapter.type = "VIRTUALBOX";
		else if (str.Find("wlan") >= 0)
			adapter.type = "IEEE80211";
		else			
			adapter.type = "OTHER";
		line.GoAfter(":");
		int pos = line.GetPos();
		int npos = line.Find("  ", pos);
		if (npos >= 0) {
		 	adapter.fullname = line.Mid(pos, npos-pos);
		 	line.SetPos(npos+1);
		}
		if (line.GetText() != "") 
			adapter.mac = ToUpper(Trim(line.GetText()));
		do {
			data.GoAfter("\n");
		} while (data[data.GetPos()] == ' ');
	}
	return ret;
}

#endif

/////////////////////////////////////////////////////////////////////
// Memory Info

#if defined(PLATFORM_WIN32)

bool GetMemoryInfo(
	int &memoryLoad, 			// percent of memory in use		
	uint64 &totalPhys, 			// physical memory				
	uint64 &freePhys, 			// free physical memory			
	uint64 &totalPageFile,		// total paging file			
	uint64 &freePageFile,		// free paging file				
	uint64 &totalVirtual,		// total virtual memory			
	uint64 &freeVirtual)		// free virtual memory			
{
	MEMORYSTATUSEX status;	
	status.dwLength = sizeof (status);
	if (!GlobalMemoryStatusEx(&status))
		return false;
	memoryLoad          = status.dwMemoryLoad;
	totalPhys			= status.ullTotalPhys;
	freePhys			= status.ullAvailPhys;
	totalPageFile		= status.ullTotalPageFile;
	freePageFile		= status.ullAvailPageFile;
	totalVirtual		= status.ullTotalVirtual;
	freeVirtual	    	= status.ullAvailVirtual;
	
	return true;
}

#else

bool GetMemoryInfo(
int &memoryLoad, 			// percent of memory in use		
uint64 &totalPhys, 			// physical memory				
uint64 &freePhys, 			// free physical memory			
uint64 &totalPageFile,		// total paging file			
uint64 &freePageFile,		// free paging file				
uint64 &totalVirtual,		// total virtual memory			
uint64 &freeVirtual)
{
	StringParse meminfo = LoadFile_Safe("/proc/meminfo");
	if (meminfo == "")
		return false;
	meminfo.GoAfter_Init("MemTotal", ":");	totalPhys = 1024*meminfo.GetUInt64();
	meminfo.GoAfter_Init("MemFree", ":");	freePhys  = 1024*meminfo.GetUInt64();
	memoryLoad = (int)(100.*(totalPhys-freePhys)/totalPhys);
	meminfo.GoAfter_Init("SwapCached", ":");freePageFile = 1024*meminfo.GetUInt64();
	meminfo.GoAfter_Init("Cached", ":");	totalPageFile = 1024*meminfo.GetUInt64() + freePageFile;
	meminfo.GoAfter_Init("SwapTotal", ":");	totalVirtual = 1024*meminfo.GetUInt64();
	meminfo.GoAfter_Init("SwapFree", ":");	freeVirtual = 1024*meminfo.GetUInt64();
	
	return true;
}
#endif


/////////////////////////////////////////////////////////////////////
// Process list

#if defined(PLATFORM_WIN32) 

// Get the list of process identifiers.
bool GetProcessList(Array<long> &pid, Array<String> &pNames)
{
	PROCESSENTRY32 proc;
	HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnap == INVALID_HANDLE_VALUE) 
		return false;
	proc.dwSize = sizeof(proc);
	long f = Process32First(hSnap, &proc);
	while (f) {
		pid.Add(proc.th32ProcessID);
		pNames.Add(proc.szExeFile);
       	f = Process32Next(hSnap, &proc);
	}
	CloseHandle(hSnap);
	return true;	
}
Array<long> GetProcessList()
{
	PROCESSENTRY32 proc;
	Array<long> ret;
	HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnap == INVALID_HANDLE_VALUE) 
		return ret;
	proc.dwSize = sizeof(proc);
	long f = Process32First(hSnap, &proc);
	while (f) {
		ret.Add(proc.th32ProcessID);
       	f = Process32Next(hSnap, &proc);
	}
	CloseHandle(hSnap);
	return ret;	
}
String GetProcessName(long processID)
{
	WCHAR szProcessName[MAX_PATH];
    // Get a handle to the process.
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);

    // Get the process name.
    if (hProcess != NULL) {
        HMODULE hMod;
        DWORD cbNeeded;

        if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
            GetModuleBaseNameW(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(WCHAR));
    }
    CloseHandle(hProcess);

    return FromSystemCharset(WString(szProcessName).ToString());
}
String GetProcessFileName(long processID)
{
	WCHAR szProcessName[MAX_PATH];
    // Get a handle to the process.
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID);

    // Get the process name.
    if (hProcess != NULL) {
        HMODULE hMod;
        DWORD cbNeeded;

        if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded))
            GetModuleFileNameExW(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(WCHAR));
    }
    CloseHandle(hProcess);

    return FromSystemCharset(WString(szProcessName).ToString());
}
BOOL CALLBACK EnumGetWindowsList(HWND hWnd, LPARAM lParam) 
{
	if (!hWnd)
		return TRUE;		// Not a window
	if (GetParent(hWnd) != 0)
		return TRUE;		// Child window
	Array<int> *ret = (Array<int> *)lParam;
	ret->Add((int)hWnd);
	return TRUE;
}
void GetWindowsList(Array<long> &hWnd, Array<long> &processId, Array<String> &name, Array<String> &fileName, Array<String> &caption)
{
	HANDLE hProcess;
	DWORD dwThreadId, dwProcessId;
	HINSTANCE hInstance;
	WCHAR str[MAX_PATH];
	
	EnumWindows(EnumGetWindowsList, (LPARAM)&hWnd);	
	for (int i = 0; i < hWnd.GetCount(); ++i) {
		hInstance = (HINSTANCE)GetWindowLong((HWND)hWnd[i], GWL_HINSTANCE);
		dwThreadId = GetWindowThreadProcessId((HWND)hWnd[i], &dwProcessId);
		processId.Add(dwProcessId);
		hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId);
		if (GetModuleFileNameExW(hProcess, hInstance, str, sizeof(str)/sizeof(WCHAR)))
			fileName.Add(FromSystemCharset(WString(str).ToString()));
		else
			fileName.Add(t_("UNKNOWN"));	
		if (GetModuleBaseNameW(hProcess, hInstance, str, sizeof(str)/sizeof(WCHAR)))
			name.Add(FromSystemCharset(WString(str).ToString()));
		else
			name.Add(t_("UNKNOWN"));		
		CloseHandle(hProcess);
		if (IsWindowVisible((HWND)hWnd[i])) {
			int count = SendMessageW((HWND)hWnd[i], WM_GETTEXT, sizeof(str)/sizeof(WCHAR), (LPARAM)str);
			str[count] = '\0';
			caption.Add(FromSystemCharset(WString(str).ToString()));	
		} else
			caption.Add("");	
	}
}
Array<long> GetWindowsList()
{
	Array<long> ret;
	EnumWindows(EnumGetWindowsList, (LPARAM)&ret);	
	return ret;
}
BOOL CALLBACK TerminateAppEnum(HWND hwnd, LPARAM lParam)
{
	DWORD dwID ;
  	GetWindowThreadProcessId(hwnd, &dwID) ;
  	if(dwID == (DWORD)lParam)
     	PostMessage(hwnd, WM_CLOSE, 0, 0) ;
  	return TRUE ;
}
// pid	 	Process ID of the process to shut down.
// timeout 	Wait time in milliseconds before shutting down the process.
bool ProcessTerminate(long processId, int timeout)
{
  	// If we can't open the process with PROCESS_TERMINATE rights, then we give up immediately.
  	HANDLE hProc = ::OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, processId);
  	if(hProc == NULL)
    	return false ;
   	// TerminateAppEnum() posts WM_CLOSE to all windows whose PID matches your process's.
  	::EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)processId) ;

	int ret;
  	// Wait on the handle. If it signals, great. If it times out,
  	// then you kill it.
  	int state = ::WaitForSingleObject(hProc, timeout);
  	if ((state == WAIT_TIMEOUT) || (state == WAIT_FAILED))
     	ret = ::TerminateProcess(hProc, 0);
	else
		ret = true;
	CloseHandle(hProc) ;
	return ret;
}

int GetProcessPriority(long pid)
{
	int priority;
	HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
  	if(hProc == NULL) 
    	return -1;
	priority = ::GetPriorityClass(hProc);
   	CloseHandle(hProc);
   	
   	switch(priority) {
   	case REALTIME_PRIORITY_CLASS:	priority = 10;	// Process that has the highest possible priority. The threads of a real-time priority class process preempt the threads of all other processes, including operating system processes performing important tasks. For example, a real-time process that executes for more than a very brief interval can cause disk caches not to flush or cause the mouse to be unresponsive.
   									break;
   	case HIGH_PRIORITY_CLASS:		priority = 8;	// Process that performs time-critical tasks that must be executed immediately for it to run correctly. The threads of a high-priority class process preempt the threads of normal or idle priority class processes. An example is the Task List, which must respond quickly when called by the user, regardless of the load on the operating system. Use extreme care when using the high-priority class, because a high-priority class CPU-bound application can use nearly all available cycles.
   									break;
	case ABOVE_NORMAL_PRIORITY_CLASS: priority = 6; // Process that has priority above NORMAL_PRIORITY_CLASS but below HIGH_PRIORITY_CLASS.
   									break;
	case NORMAL_PRIORITY_CLASS:		priority = 5;	// Process with no special scheduling needs.
   									break;	
	case BELOW_NORMAL_PRIORITY_CLASS: priority = 3;	// Process that has priority above IDLE_PRIORITY_CLASS but below NORMAL_PRIORITY_CLASS.
   									break;	
	case IDLE_PRIORITY_CLASS:		priority = 0;	// Process whose threads run only when the system is idle and are preempted by the threads of any process running in a higher priority class. An example is a screen saver. The idle priority class is inherited by child processes.
									break;
	default:						return -1;
   	}
  	return priority;	
}
 
bool SetProcessPriority(long pid, int priority)
{
	HANDLE hProc = ::OpenProcess(PROCESS_SET_INFORMATION , FALSE, pid);
  	if(hProc == NULL)
    	return false;
  	if (priority == 10)
  		priority = REALTIME_PRIORITY_CLASS;
  	else if (priority >= 8)
		priority = HIGH_PRIORITY_CLASS;
  	else if (priority >= 6)
		priority = ABOVE_NORMAL_PRIORITY_CLASS;
  	else if (priority >= 5)
		priority = NORMAL_PRIORITY_CLASS;	
  	else if (priority >= 3)
   		priority = BELOW_NORMAL_PRIORITY_CLASS;
  	else
  		priority = IDLE_PRIORITY_CLASS;
	int ret = ::SetPriorityClass(hProc, priority);		// SetProcessAffinityMask
   	CloseHandle(hProc) ;
  	return ret;	
}

#endif
#ifdef PLATFORM_POSIX			 //Check with ps

bool IsInteger(String s)
{
	for (int i = 0; i < s.GetCount(); ++i) {
		if (!isdigit(s[i]))
			return false;
	}
	return true;
}
bool GetProcessList(Array<long> &pid, Array<String> &pNames)
{
	FindFile ff;
	if(ff.Search("/proc/*")) {
		do {
			if (IsInteger(ff.GetName())) {
				String exe = Format("/proc/%s/exe", ff.GetName());
				StringBuffer exeb;
				exeb = exe;
				char procName[2048];
				int procNameLen = readlink(exeb, procName, sizeof(procName)-1);
				if (procNameLen > 0) {
					procName[procNameLen] = 0;
					pNames.Add(procName);
					pid.Add(atoi(ff.GetName()));
				}
			}
		} while(ff.Next());
	}
	return true;
}
Array<long> GetProcessList()
{
	FindFile ff;
	Array<long> pid; 
	if(ff.Search("/proc/*")) {
		do {
			if (IsInteger(ff.GetName())) {
				String exe = Format("/proc/%s/exe", ff.GetName());
				StringBuffer exeb;
				exeb = exe;
				char procName[2048];
				int procNameLen = readlink(exeb, procName, sizeof(procName)-1);
				if (procNameLen > 0) 
					pid.Add(atoi(ff.GetName()));
			}
		} while(ff.Next());
	}
	return pid;
}
String GetProcessName(long pid)
{
	return GetFileName(GetProcessFileName(pid));
}
// ls -l /proc/%d/fd gets also the files opened by the process
String GetProcessFileName(long pid)
{
	String ret = "";
	String exe = Format("/proc/%s/exe", FormatLong(pid));
	StringBuffer exeb;
	exeb = exe;
	char procName[2048];
	int procNameLen = readlink(exeb, procName, sizeof(procName)-1);
	if (procNameLen > 0) {
		procName[procNameLen] = 0;
		ret = procName;
	} 
	return ret;	
}

#ifdef PLATFORM_POSIX
#ifdef flagGUI
	#define SetX11ErrorHandler() {}
	#define SetSysInfoX11ErrorHandler()	{}
#else
	#define SetX11ErrorHandler() {}
	int SysInfoX11ErrorHandler(_XDisplay *, XErrorEvent *)	{return 0;}
	void SetSysInfoX11ErrorHandler()						{XSetErrorHandler(SysInfoX11ErrorHandler);}
#endif
#endif

void GetWindowsList_Rec (_XDisplay *dpy, Window w, int depth, Array<long> &wid) 
{ 
	if (depth > 3) // 1 is enough for Gnome. 2 is necessary for Xfce and Kde
		return; 

	wid.Add(w);

	Window root, parent; 
	unsigned int nchildren; 
	Window *children = NULL; 
	if (XQueryTree (dpy, w, &root, &parent, &children, &nchildren))  {
		for (int i = 0; i < nchildren; i++) {
			XWindowAttributes windowattr;
			XGetWindowAttributes(dpy, children[i], &windowattr);
			if (windowattr.map_state == IsViewable)
				GetWindowsList_Rec (dpy, children[i], depth + 1, wid); 
		}
	}
	if (children) 
		XFree((char *)children); 
	return; 
}
Array<long> GetWindowsList()
{
	Array<long> ret;	
	SetSysInfoX11ErrorHandler();
	
	_XDisplay *dpy = XOpenDisplay (NULL);
	if (!dpy) {
		SetX11ErrorHandler();
		return ret;
	}
	GetWindowsList_Rec (dpy, RootWindow(dpy, DefaultScreen(dpy)), 0, ret);
	XCloseDisplay (dpy);
	SetX11ErrorHandler();
	return ret;
}
void GetWindowsList(Array<long> &hWnd, Array<long> &processId, Array<String> &nameL, Array<String> &fileName, Array<String> &caption)
{
	SetSysInfoX11ErrorHandler();
	_XDisplay *dpy = XOpenDisplay (NULL);
	if (!dpy) {
		SetX11ErrorHandler();
		return;
	}
	GetWindowsList_Rec(dpy, RootWindow (dpy, DefaultScreen (dpy)), 0, hWnd);
	for (int i = 0; i < hWnd.GetCount(); ++i) {
		// Get window name
		XTextProperty tp;
		if (XGetWMName(dpy, hWnd[i], &tp) == 0) 
	    	caption.Add("");
        else {
        	if (tp.nitems > 0) {
           		int count = 0, i, ret;
           		char **list = NULL;
          		ret = XmbTextPropertyToTextList(dpy, &tp, &list, &count);
            	if((ret == Success || ret > 0) && list != NULL) {
                	String sret;
              		for(i = 0; i < count; i++)
              			sret << list[i]; // << " ";
              		XFreeStringList(list);
              		caption.Add(FromSystemCharset(sret));
          		} else 
              		caption.Add(FromSystemCharset((char *)tp.value));
      		} else 
      			caption.Add("");
        }
		// Get pid
		Atom atomPID = XInternAtom(dpy, "_NET_WM_PID", true);
		unsigned long pid = 0;
		if (atomPID == None)
			processId.Add(0L);
		else {
			Atom type;        
			int format;        
			unsigned long nItems;        
			unsigned long bytesAfter;        
			unsigned char *propPID = 0;
			if (0 == XGetWindowProperty(dpy, hWnd[i], atomPID, 0, 1024, false, XA_CARDINAL, &type, &format, &nItems, &bytesAfter, &propPID)) {
				if(propPID != 0) {
					pid = *((unsigned long *)propPID);
					processId.Add(pid);
					XFree(propPID);
				} else
					processId.Add(0L);
			} else
				processId.Add(0L);
		}
		if (pid != 0L)
			fileName.Add(GetProcessFileName(pid));
		else
			fileName.Add("");
		// Name and class
		XClassHint ch;
		ch.res_name = ch.res_class = NULL; 	
		Status status = XGetClassHint (dpy, hWnd[i], &ch);
		if (status != BadWindow) {
			if (ch.res_name)
				nameL.Add(ch.res_name);
			else
				nameL.Add("");
		} else
			nameL.Add("");
		if (ch.res_name) 
			XFree (ch.res_name); 
		if (ch.res_class) 
			XFree (ch.res_class); 
	}
	XCloseDisplay (dpy);
	SetX11ErrorHandler();
	return;
}    

bool WindowKill(long wid)
{
	if (wid == 0)
		return false;
	
	_XDisplay *dpy = XOpenDisplay (NULL);
	if (!dpy) 
		return false;

	XSync (dpy, 0);			
	XKillClient (dpy, wid);
	XSync (dpy, 0);
	
	XCloseDisplay (dpy);
	return true;	
}

// Also possible to stop or cont
bool ProcessTerminate(long pid, int timeout)
{
	if (!ProcessExists(pid))
		return false;
	long wid = GetWindowIdFromProcessId(pid);		// Just in case
		
	// First... SIGTERM
	kill(pid, SIGTERM);	
	Sleep(timeout/3);
	if (!ProcessExists(pid))
		return true;
	// Second... SIGKILL
	kill(pid, SIGKILL);	
	Sleep(timeout/3);
	if (!ProcessExists(pid))
		return true;
	// Third ... WindowKill
	Sleep((int)(timeout/3));
	return WindowKill(wid);
}

int GetProcessPriority(long pid)
{
	int priority = getpriority(PRIO_PROCESS, pid);
	return 10 - (priority + 20)/4;		// Rescale -20/20 to 10/0
}
bool SetProcessPriority(long pid, int priority)
{
	priority = 20 - 4*priority;
	if (0 == setpriority(PRIO_PROCESS, pid, priority))
 		return true;	
	else
		return false;
}
#endif

long GetWindowIdFromCaption(String windowCaption, bool exactMatch)
{
	Array<long> wid, pid;
	Array<String> name, fileName, caption;
	GetWindowsList(wid, pid, name, fileName, caption);
	for (int i = 0; i < wid.GetCount(); ++i) {
		if (exactMatch) {
			String s = caption[i];
			if (caption[i] == windowCaption)
				return wid[i];
		} else {
			if (caption[i].Find(windowCaption) >= 0)
				return wid[i];
		}
	}
	return -1;
} 
long GetProcessIdFromWindowCaption(String windowCaption, bool exactMatch)
{
	Array<long> wid, pid;
	Array<String> name, fileName, caption;
	GetWindowsList(wid, pid, name, fileName, caption);
	for (int i = 0; i < wid.GetCount(); ++i) {
		if (exactMatch) {
			if (caption[i] == windowCaption)
				return pid[i];
		} else {
			if (caption[i].Find(windowCaption) >= 0)
				return pid[i];
		}
	}
	return -1;
}    
long GetProcessIdFromWindowId(long _wId)
{
	Array<long> wId, pid;
	Array<String> name, fileName, caption;
	GetWindowsList(wId, pid, name, fileName, caption);
	for (int i = 0; i < pid.GetCount(); ++i) {
		if (wId[i] == _wId)
			return pid[i];
	}
	return 0;
} 
long GetWindowIdFromProcessId(long _pid)
{
	Array<long> wId, pid;
	Array<String> name, fileName, caption;
	GetWindowsList(wId, pid, name, fileName, caption);
	for (int i = 0; i < pid.GetCount(); ++i) {
		if (pid[i] == _pid)
			return wId[i];
	}
	return 0;
} 

bool ProcessExists(long pid)
{
	return DirectoryExists(Format("/proc/%s", FormatLong(pid)));
}
/////////////////////////////////////////////////////////////////////
// Os Info

#if defined(PLATFORM_WIN32) 

#if !defined(PRODUCT_ULTIMATE)
//#define PRODUCT_UNDEFINED                       0x00000000
#define PRODUCT_ULTIMATE                        0x00000001
#define PRODUCT_HOME_BASIC                      0x00000002
#define PRODUCT_HOME_PREMIUM                    0x00000003
#define PRODUCT_ENTERPRISE                      0x00000004
#define PRODUCT_HOME_BASIC_N                    0x00000005
#define PRODUCT_BUSINESS                        0x00000006
#define PRODUCT_STANDARD_SERVER                 0x00000007
#define PRODUCT_DATACENTER_SERVER               0x00000008
#define PRODUCT_SMALLBUSINESS_SERVER            0x00000009
#define PRODUCT_ENTERPRISE_SERVER               0x0000000A
#define PRODUCT_STARTER                         0x0000000B
#define PRODUCT_DATACENTER_SERVER_CORE          0x0000000C
#define PRODUCT_STANDARD_SERVER_CORE            0x0000000D
#define PRODUCT_ENTERPRISE_SERVER_CORE          0x0000000E
#define PRODUCT_ENTERPRISE_SERVER_IA64          0x0000000F
#define PRODUCT_BUSINESS_N                      0x00000010
#define PRODUCT_WEB_SERVER                      0x00000011
#define PRODUCT_CLUSTER_SERVER                  0x00000012
#define PRODUCT_HOME_SERVER                     0x00000013
#define PRODUCT_STORAGE_EXPRESS_SERVER          0x00000014
#define PRODUCT_STORAGE_STANDARD_SERVER         0x00000015
#define PRODUCT_STORAGE_WORKGROUP_SERVER        0x00000016
#define PRODUCT_STORAGE_ENTERPRISE_SERVER       0x00000017
#define PRODUCT_SERVER_FOR_SMALLBUSINESS        0x00000018
#define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM    0x00000019
#define PRODUCT_UNLICENSED                      0xABCDABCD
#endif

#if !defined(SM_SERVERR2)
#define SM_SERVERR2             89
#endif
#if !defined(VER_SUITE_WH_SERVER)
#define VER_SUITE_WH_SERVER		0x00008000
#endif
typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO);
typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD);

bool GetOsInfo(String &kernel, String &kerVersion, String &kerArchitecture, String &distro, String &distVersion, String &desktop, String &deskVersion)
{
   	OSVERSIONINFOEX osvi;
   	SYSTEM_INFO si;
   	PGNSI pGNSI;
   	PGPI pGPI;
   	BOOL bOsVersionInfoEx;
   	DWORD dwType;

   	ZeroMemory(&si, sizeof(SYSTEM_INFO));
   	ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));

   	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

   	if(!(bOsVersionInfoEx = GetVersionEx((OSVERSIONINFO *) &osvi)))
      	return false;

   	// Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.
   	pGNSI = (PGNSI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo");
	if(NULL != pGNSI)
   		pGNSI(&si);
   	else 
   		GetSystemInfo(&si);

	kerVersion = Format("%d.%d", (int)osvi.dwMajorVersion, (int)osvi.dwMinorVersion);
	kernel = "Windows";
	if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64)
    	kerArchitecture = "64 bits";
	else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64)
      	kerArchitecture = "Itanium 64 bits";
   	else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_INTEL)
      	kerArchitecture = "32 bits";
	else
		kerArchitecture = "Unknown";	//PROCESSOR_ARCHITECTURE_UNKNOWN
   	if (VER_PLATFORM_WIN32_NT == osvi.dwPlatformId && osvi.dwMajorVersion > 4 ) {
      	// Test for the specific product.
		if (osvi.dwMajorVersion == 6) {
			if (osvi.dwMinorVersion == 1) {
            	if (osvi.wProductType == VER_NT_WORKSTATION)
                	kernel.Cat(" Seven");
            	else 
					kernel.Cat(" Server 2008 R2");
			} else if (osvi.dwMinorVersion == 0) {
         		if (osvi.wProductType == VER_NT_WORKSTATION)
         			kernel.Cat(" Vista");
         		else
         			kernel.Cat(" Server 2008");
			}
         	pGPI = (PGPI) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo");
         	pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 
         		 osvi.wServicePackMajor, osvi.wServicePackMinor, &dwType);
         	switch(dwType) {
         	case PRODUCT_UNLICENSED:
               distro = "Unlicensed";
               break;
            case PRODUCT_ULTIMATE:
               distro = "Ultimate Edition";
               break;
            case PRODUCT_HOME_PREMIUM:
               distro = "Home Premium Edition";
               break;
            case PRODUCT_HOME_BASIC:	case PRODUCT_HOME_BASIC_N:
               distro = "Home Basic Edition";
               break;
            case PRODUCT_ENTERPRISE:
               distro = "Enterprise Edition";
               break;
            case PRODUCT_BUSINESS:		case PRODUCT_BUSINESS_N:
               distro = "Business Edition";
               break;
            case PRODUCT_STARTER:
               distro = "Starter Edition";
               break;
            case PRODUCT_CLUSTER_SERVER:
               distro = "Cluster Server Edition";
               break;
            case PRODUCT_DATACENTER_SERVER:
               distro = "Datacenter Edition";
               break;
            case PRODUCT_DATACENTER_SERVER_CORE:
               distro = "Datacenter Edition (core installation)";
               break;
            case PRODUCT_ENTERPRISE_SERVER:
               distro = "Enterprise Edition";
               break;
            case PRODUCT_ENTERPRISE_SERVER_CORE:
               distro = "Enterprise Edition (core installation)";
               break;
            case PRODUCT_ENTERPRISE_SERVER_IA64:
               distro = "Enterprise Edition for Itanium-based Systems";
               break;
            case PRODUCT_SMALLBUSINESS_SERVER:
               distro = "Small Business Server";
               break;
            case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
               distro = "Small Business Server Premium Edition";
               break;
            case PRODUCT_STANDARD_SERVER:
               distro = "Standard Edition";
               break;
            case PRODUCT_STANDARD_SERVER_CORE:
               distro = "Standard Edition (core installation)";
               break;
            case PRODUCT_WEB_SERVER:
               distro = "Web Server Edition";
               break;
         	}
      	} else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2) {
         	if (GetSystemMetrics(SM_SERVERR2) )
         		kernel.Cat(" Server 2003 R2");
         	else if (osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER)
            	kernel.Cat(" Storage Server 2003");
     		else if (osvi.wSuiteMask & VER_SUITE_WH_SERVER)
            	kernel.Cat(" Home Server");
         	else if( osvi.wProductType == VER_NT_WORKSTATION && si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)
	            kernel.Cat(" XP Professional x64 Edition");
         	else 
         		kernel.Cat(" Server 2003");
         	// Test for the server type.
         	if (osvi.wProductType != VER_NT_WORKSTATION ) {
            	if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_IA64 ) {
                	if(osvi.wSuiteMask & VER_SUITE_DATACENTER )
                   		distro = "Datacenter Edition for Itanium-based Systems";
                	else if(osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
                   		distro = "Enterprise Edition for Itanium-based Systems";
            	}
			} else if (si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64 ) {
            	if(osvi.wSuiteMask & VER_SUITE_DATACENTER )
             		distro = "Datacenter x64 Edition";
                else if(osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
                   	distro = "Enterprise x64 Edition";
                else 
                	distro = "Standard x64 Edition";
            } else {
             	if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER )
                   	distro = "Compute Cluster Edition";
                else if(osvi.wSuiteMask & VER_SUITE_DATACENTER )
                   	distro = "Datacenter Edition";
                else if(osvi.wSuiteMask & VER_SUITE_ENTERPRISE )
                   	distro = "Enterprise Edition";
                else if (osvi.wSuiteMask & VER_SUITE_BLADE )
                   	distro = "Web Edition";
                else 
                	distro = "Standard Edition";
            }
    	} else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) {
         	kernel.Cat(" XP");
         	if(osvi.wSuiteMask & VER_SUITE_PERSONAL )
            	distro = "Home Edition";
         	else 
         		distro = "Professional";
      	} else if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) {
         	kernel.Cat(" 2000");
         	if (osvi.wProductType == VER_NT_WORKSTATION)
            	distro = "Professional";
         	else {
            	if(osvi.wSuiteMask & VER_SUITE_DATACENTER)
               		distro = "Datacenter Server";
            	else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE)
               		distro = "Advanced Server";
            	else 
            		distro = "Server";
         	}
      	} 
       	// Include service pack (if any) and build number.
      	if(osvi.wServicePackMajor > 0)
			kerVersion.Cat(Format(" %s", osvi.szCSDVersion));

      	kerVersion.Cat(Format(" (Build %d)", (int)osvi.dwBuildNumber));
 	} else if (osvi.dwPlatformId == 1) {
 		switch(osvi.dwMinorVersion) {
 		case 0:
 			kernel.Cat(" 95");
 			break;
 		case 10:
 			kernel.Cat(" 98");
 			break;
 		case 90:
 			kernel.Cat(" Millennium");
 			break;
 		}
 		distro = "";
 	} else if (osvi.dwPlatformId == 2) {
    	switch(osvi.dwMajorVersion) {
    	kernel.Cat(" NT");
        case 3:
        	kernel.Cat(" 3.51");
        	break;
        case 4:
         	kernel.Cat(" 4.0");
         	break;
    	}
    	distro = "";
 	}
 	desktop = kernel;
 	distVersion = deskVersion = "";
   	return true;
}
#endif
#ifdef PLATFORM_POSIX

bool GetOsInfo_CheckLsb(String &distro, String &distVersion)
{
	StringParse lsb;			
	lsb = LoadFile_Safe("/etc/lsb-release");		
	if (lsb == "")
		return false;
	if(!lsb.GoAfter("DISTRIB_ID="))
		return false;				
	distro = ToLower(lsb.GetText());
	if (distro == "")
		return false;
	lsb.GoAfter_Init("DISTRIB_RELEASE=");
	distVersion = lsb.GetText();

	return true;
}
bool GetOsInfo(String &kernel, String &kerVersion, String &kerArchitecture, String &distro, String &distVersion, String &desktop, String &deskVersion)
{
	struct utsname buf;
	
	if (0 == uname(&buf)) {
		kernel = buf.sysname;
    	kerVersion = String(buf.release) + " " + String(buf.version);
        kerArchitecture = buf.machine;
	}
	if (kernel == "")
		kernel = LoadFile_Safe("/proc/sys/kernel/ostype");
	if (kernel == "")
		kernel = LoadFile_Safe("/proc/version");
	if (kernel == "") {
		if (Sys("sysctl_cmd -n kern.version").Find("FreeBSD") >= 0)
			kernel = "freebsd";
	}
	if (kerVersion == "")
		kerVersion = LoadFile_Safe("/proc/sys/kernel/osrelease") + " " + LoadFile_Safe("/proc/sys/kernel/version");
	if (kerArchitecture == "")
		kerArchitecture = Sys("uname -m");	// Kernel. See too /proc/version, /proc/version_signature and uname -a looking for architecture
	
	if (kernel == "")
		kernel = kerVersion = kerArchitecture = "UNKNOWN";	
	
	// Desktop
    if(GetEnv("GNOME_DESKTOP_SESSION_ID").GetCount() || GetEnv("GNOME_KEYRING_SOCKET").GetCount()) {
		desktop = "gnome";
		StringParse gnomeVersion = Sys("gnome-about --version");
		gnomeVersion.GoAfter("gnome-about");
		deskVersion = gnomeVersion.GetText();
	} else if(GetEnv("KDE_FULL_SESSION").GetCount() || GetEnv("KDEDIR").GetCount() || GetEnv("KDE_MULTIHEAD").GetCount()) {
        desktop = "kde"; 
        StringParse konsole = Sys("konsole --version");
        konsole.GoAfter("KDE:");
        deskVersion = konsole.GetText("\r\n");						
		if (deskVersion == "")		
			deskVersion = GetEnv("KDE_SESSION_VERSION");        
	} else {
		StringParse desktopStr;
		if (Sys("xprop -root _DT_SAVE_MODE").Find("xfce") >= 0)
			desktop = "xfce";
		else if ((desktopStr = Sys("xprop -root")).Find("ENLIGHTENMENT") >= 0) {
			desktop = "enlightenment";
			desktopStr.GoAfter("ENLIGHTENMENT_VERSION(STRING)", "=");
			desktopStr = desktopStr.GetText();
			if (desktopStr.GetText() == "Enlightenment")
				deskVersion = desktopStr.GetText();
		} else
			desktop = GetEnv("DESKTOP_SESSION");
	}
	if (desktop == "")
		desktop = deskVersion = "UNKNOWN";
	
	// Distro
	if (GetOsInfo_CheckLsb(distro, distVersion))
		;
	else if (FileExists("/usr/share/doc/ubuntu-minimal"))
		distro = "ubuntu";
	else if (FileExists("/etc/fedora-release")) {
		distro = "fedora";
		StringParse strFile = LoadFile_Safe("/etc/fedora-release");
		String str;
		do {
			str = strFile.GetText();
			if ((str != "fedora") && (str != "release"))
				distVersion << str << " ";
		} while (str != "");
	} else if (FileExists("/etc/redhat-release")) {
		distro = "redhat";
		distVersion = LoadFile_Safe("/etc/redhat-release");
	} else if (FileExists("/etc/SuSE-release")) {
		StringParse strFile = LoadFile_Safe("/etc/SuSE-release");
		distro = strFile.GetText();
		strFile.GoAfter_Init("VERSION", "=");
		distVersion = strFile.GetText();
	} else if (FileExists("/etc/mandrake-release")) {
		distro = "mandrake";			
		distVersion = LoadFile_Safe("/etc/mandrake-release");
	} else if (FileExists("/etc/mandriva-release")) {
		distro = "mandriva";	
		distVersion = LoadFile_Safe("/etc/mandriva-release");
	} else if (FileExists("/etc/aurox-release")) {
		distro = "aurox";
		distVersion = LoadFile_Safe("/etc/aurox-release");
	} else if (FileExists("/etc/altlinux-release")) {
		distro = "altlinux";			
		distVersion = LoadFile_Safe("/etc/altlinux-releas");
	} else if (FileExists("/etc/gentoo-release")) {
		distro = "gentoo";
		distVersion = LoadFile_Safe("/etc/gentoo-release");
	} else if (FileExists("/usr/portage")) {
		distro = "gentoo";
		distVersion = LoadFile_Safe("/usr/portage");
	} else if (FileExists("/etc/slackware-version")) {
		distro = "slackware";
		StringParse strFile = LoadFile_Safe("/etc/slackware-version");
		strFile.GetText();
		distVersion = strFile.GetText();
	} else if (FileExists("/etc/debian_version")) {
		distro = "debian";
		distVersion = LoadFile_Safe("/etc/debian_version");
	} else if (LoadFile_Safe("/etc/release").Find("Solaris") >= 0)
		distro = "solaris";
	else if (Sys("uname -r").Find("solaris") >= 0)
		distro = "solaris";
	else {					// If not try with /etc/osname_version
		distro = LoadFile_Safe("/etc/osname_version");
		distVersion = "";
	} 
	if (distro == "") 
		distro = LoadFile_Safe("/etc/issue");
	if (distro == "") 
		distro = distVersion = "UNKNOWN";
	
	return true;
}

#endif

/////////////////////////////////////////////////////////////////////
// Others

long    GetProcessId()			{return getpid();}

/////////////////////////////////////////////////////////////////////
// Drives list
#if defined(PLATFORM_WIN32)

bool GetDriveSpace(String drive, 
	//uint64 &totalBytes,	// To determine the total number of bytes on a disk or volume, use IOCTL_DISK_GET_LENGTH_INFO.
	uint64 &freeBytesUser, 	// Total number of free bytes on a disk that are available to the user who is associated with the calling thread.
	uint64 &totalBytesUser, // Total number of bytes on a disk that are available to the user who is associated with the calling thread.
	uint64 &totalFreeBytes)	// Total number of free bytes on a disk.
{
	StringBuffer sb(drive);	
	
	if(!GetDiskFreeSpaceEx(sb, (PULARGE_INTEGER)&freeBytesUser, (PULARGE_INTEGER)&totalBytesUser, (PULARGE_INTEGER)&totalFreeBytes))
		return false;
	//totalBytes = 0;
	return true;
}
// return true if mounted
bool GetDriveInformation(String drive, String &type, String &volume, /*uint64 &serial, */int &maxName, String &fileSystem)
{
	StringBuffer sb(drive);	
	
	switch (::GetDriveType(sb)) {
	case DRIVE_UNKNOWN: 	type = "Drive unknown";		break;
   	case DRIVE_NO_ROOT_DIR: type = "The root directory does not exist";	break;
	case DRIVE_REMOVABLE:
          switch (*sb) {
          case 'A':
          case 'B': 		type = "Floppy";		
          					volume = fileSystem = "";
          					/*serial = */maxName = 0;
          					return false;
          default: 			type = "Removable";	break;
          }
          break;
   	case DRIVE_FIXED:   	type = "Hard";		break;
    case DRIVE_REMOTE:  	type = "Network";	break;
    case DRIVE_CDROM:   	type = "Optical";	break;
    case DRIVE_RAMDISK: 	type = "RAM";		break;
   	}
   	char vol[MAX_PATH], fs[MAX_PATH];
   	long flags;
   	uint64 serial;
   	uint64 _maxName;
   	if(!::GetVolumeInformation(sb, vol, MAX_PATH, (LPDWORD)&serial, (LPDWORD)&_maxName, (LPDWORD)&flags, fs, MAX_PATH)) {
   		if (type == "Optical") {
   			volume = "";
   			fileSystem = "";
   			maxName = 0;
   			return true;
   		} else
   			return false;
   	}
   	volume = vol;
   	fileSystem = fs;
   	maxName = (int)_maxName;
   	
   	return true;
}

#elif defined(PLATFORM_POSIX)

bool GetDriveSpace(String drive, 
	//uint64 &totalBytes,	// To determine the total number of bytes on a disk or volume, use IOCTL_DISK_GET_LENGTH_INFO.
	uint64 &freeBytesUser, 	// Total number of free bytes on a disk that are available to the user who is associated with the calling thread.
	uint64 &totalBytesUser, // Total number of bytes on a disk that are available to the user who is associated with the calling thread.
	uint64 &totalFreeBytes)	// Total number of free bytes on a disk.
{
	freeBytesUser = totalBytesUser = totalFreeBytes = 0;
	
	StringParse space = Sys("df -T");
	if (space == "")
		return false;
	
	while (drive != space.GetText())
		;
	space.MoveRel(-10);	space.GoBeginLine(); 
	space.GetText();	space.GetText();	// Jumps over device path and filesystem
	totalBytesUser = 1024*space.GetUInt64();
	space.GetText();						// Jumps over used space
	freeBytesUser  = totalFreeBytes = 1024*space.GetUInt64();
	return true;
}

// return true if mounted
bool GetDriveInformation(String drive, String &type, String &volume, /*uint64 &serial, */int &maxName, String &fileSystem)
{
	StringParse info = Sys("mount -l");
	if (info  == "")
		return false;
	
	String straux;
	while (drive != (straux = info.GetText()))
		if (straux == "")
			return false;
	       
	if("type" != info.GetText())		// Jumps over "type"
		return false;

	fileSystem = info.GetText();
	String details = info.GetText();
	info.GoAfter("[");
	volume = info.GetText("]");
	//serial = 0;			// Unknown		
	if ((fileSystem == "udf" || fileSystem == "iso9660") && details.Find("ro") >=0)
		type = "Optical";
	else if (details.Find("flush") >=0)
		type = "Removable";
	else
		type = "Hard";
		
	struct statfs buf;
	if (0 == statfs(drive, &buf)) 
		//puts(Format("%x", buf.f_type)); // Filesystem type
		maxName = buf.f_namelen; 	
	else 
		maxName = 0;
	
	return true;	
}

#endif

#if defined (PLATFORM_WIN32)
unsigned long start, end;
unsigned long nCtr, nFreq, nCtrStop;

#if defined(__MINGW32__)
int GetCpuSpeed()
{
    if(!QueryPerformanceFrequency((LARGE_INTEGER *) &nFreq)) 
    	return 0;
    QueryPerformanceCounter((LARGE_INTEGER *)&nCtrStop);
    nCtrStop += nFreq/10000;
    
    __asm__(".byte 0x0F");
    __asm__(".byte 0x31");
    __asm__("mov %eax,_start");
    __asm__("mov %edx,4+(_start)");
	
    do
        QueryPerformanceCounter((LARGE_INTEGER *)&nCtr);
    while (nCtr < nCtrStop);

    __asm__(".byte 0x0F");
    __asm__(".byte 0x31");
    __asm__("mov %eax,_end");
    __asm__("mov %edx,4+(_end)");

    return int((end-start)/100); 
}
#elif defined(_MSC_VER)
int GetCpuSpeed()
{
    if(!QueryPerformanceFrequency((LARGE_INTEGER *) &nFreq)) 
    	return 0;
    QueryPerformanceCounter((LARGE_INTEGER *) &nCtrStop);
    nCtrStop += nFreq/10000;								
    
    _asm {
     	__asm _emit 0x0f 
     	__asm _emit 0x31
    	mov DWORD PTR start, eax
   		mov DWORD PTR [start + 4], edx
    }
    do 
         QueryPerformanceCounter((LARGE_INTEGER *) &nCtr);
    while (nCtr < nCtrStop);
    _asm {
        __asm _emit 0x0f 
        __asm _emit 0x31
        mov DWORD PTR end, eax
        mov DWORD PTR [end + 4], edx
 	}
	return int((end-start)/100);
}
#endif 
#endif
#if defined(PLATFORM_POSIX)

#define RDTSC_READ(tm) __asm__ __volatile__ (".byte 0x0f; .byte 0x31" :"=a" (tm))
#define COUNT_SEC   (double)tv.tv_sec + (1.e-6)*tv.tv_usec

int GetCpuSpeed()
{
	struct timeval tv;
  	double cnt1, cnt2;
  	unsigned long start, end;

  	RDTSC_READ(start);
  	gettimeofday(&tv, 0);
  	cnt1 = COUNT_SEC + 0.01;

  	do {
    	gettimeofday(&tv, 0);
    	cnt2 = COUNT_SEC;
  	} while(cnt2 < cnt1);

  	RDTSC_READ(end);

  	return int((end-start)/10000);
}
#endif
#if defined(PLATFORM_WIN32)

#define SHTDN_REASON_MINOR_OTHER 0

bool Shutdown(String action) {
	action = ToLower(action);
	
	bool hibernate, suspend;
	hibernate = suspend = false;
	
	unsigned int flag;
	if (action == "logoff")
		flag = EWX_LOGOFF;
	else if (action == "poweroff")
		flag = EWX_POWEROFF;
	else if (action == "reboot")
		flag = EWX_REBOOT;
	else if (action == "shutdown")
		flag = EWX_SHUTDOWN;
	else if (action == "hibernate") 
		hibernate = true;
	else if (action == "suspend")	
		suspend = true;
	else
		return false;
	
   	HANDLE hToken; 
   	TOKEN_PRIVILEGES tkp; 
 
   	// Get a token for this process. 
   	if (!OpenProcessToken(GetCurrentProcess(), 
     	TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) 
 		return false; 
 
   	// Get the LUID for the shutdown privilege. 
   	LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid); 
 
   	tkp.PrivilegeCount = 1;  // one privilege to set    
   	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
 
   	// Get the shutdown privilege for this process. 
   	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, 
        (PTOKEN_PRIVILEGES)NULL, 0); 
 
   	if (GetLastError() != ERROR_SUCCESS) 
      	return false; 
 
	if (hibernate || suspend) {
		if (!SetSuspendState(hibernate, TRUE, FALSE))
			return false;
	} else {
	   	// Shut down the system and force all applications to close. 
	   	if (!ExitWindowsEx(flag | EWX_FORCE, SHTDN_REASON_MINOR_OTHER)) 
	      	return false; 
	}
   	return true;
}
#endif

#ifdef PLATFORM_POSIX
bool Shutdown(String action)
{
	if (action == "logoff") {
		kill(1, SIGTSTP);
		sync();
		signal(SIGTERM, SIG_IGN);
		setpgrp();
		kill(-1, SIGTERM);
		sleep(1);
		kill(-1, SIGHUP); //* Force PPPD's down, too *
		sleep(1);
		kill(-1, SIGKILL);
		sync();
		sleep(1);
	} else if (action == "shutdown") {
#if __GNU_LIBRARY__ > 5
		reboot(0xCDEF0123);
#else
		reboot(0xfee1dead, 672274793, 0xCDEF0123);
#endif
	} else if (action == "reboot") {		// LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2
#if __GNU_LIBRARY__ > 5
		reboot(0x01234567);
#else
		reboot(0xfee1dead, 672274793, 0x01234567);
#endif
	} 
	exit(0);
	return true; 
}
#endif

void GetCompilerInfo(String &name, int &version, String &date)
{
	name = "";
	version = 0;
	date = __DATE__;
	#if defined(WIN32) 
		#if defined(__MINGW32__)
			name = "mingw";
			version = __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__;	// __VERSION__
		#elif defined(COMPILER_MSC) 
			name = "msc";
			version = _MSC_FULL_VER;
		#elif defined (__BORLANDC__)
			name = "borlandc"
			version = __BORLANDC__;
		#elif defined (__WATCOMC__)
			name = "watcomc"
			version = __WATCOMC__;
		#endif
	#elif defined (PLATFORM_POSIX)
	    #if defined(__GNUC__)
	    	name = "gnuc";
	    	version = __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__;
	    #endif
    #elif defined (__APPLE__) 
    	// In a next future?
	#endif
}
	

#ifdef PLATFORM_POSIX
bool GetBatteryStatus(bool &discharging, int &percentage, int &remainingMin)
{
/* This is only if acpi package is present
	StringParse data = Sys("acpi -V");
	
	data.GoAfter("AC Adapter", ":");
	String sacStatus = data.GetText();
	discharging = sacStatus != "on-line";
	data.GoInit();
	data.GoAfter("Battery", ":");
	data.GoAfter(",");
	percentage = data.GetInt("%");
	data.GoAfter(",");
	String remaining;
	if (discharging) {
		remaining = data.GetText(" ");
		int hour, min;
		double secs;
		StringToHMS(remaining, hour, min, secs);	// It is really days:hour:min in this case
		remainingMin = int(secs) + min*60 + hour*24*60;
	} else
		remainingMin = Null;
*/
	percentage = 100;
	Array<String> files = SearchFile("/proc/acpi/battery", "state");
	if (files.GetCount() == 0)
		return false;
	StringParse state = LoadFile_Safe(files[0]);
	if (state == "")
		return false;
	bool present;
	if(!state.GoAfter_Init("present", ":"))
		return false;			
	present = state.GetText() == "yes";
	if (!present)
		return false;	// No battery inserted
	state.GoAfter_Init("charging state", ":");	 	discharging = state.GetText() == "discharging";
	int presentRate, remainingCapacity;
	state.GoAfter_Init("present rate", ":");		presentRate = state.GetInt();
	state.GoAfter_Init("remaining capacity", ":");	remainingCapacity = state.GetInt();
	if (presentRate == 0 || !discharging)
		remainingMin = Null;
	else
		remainingMin = (int)((60.*remainingCapacity)/presentRate);
	
	int designCapacity,lastFullCapacity;
	String vendor, type, model, serial;
	if (!GetBatteryInfo(present/*, designCapacity, lastFullCapacity, vendor, type, model, serial*/))
		percentage = (int)((100.*remainingCapacity)/lastFullCapacity);

	return true;
}
bool GetBatteryInfo(bool &present/*, int &designCapacity, int &lastFullCapacity, String &vendor, String &type, String &model, String &serial*/)
{
	Array<String> files = SearchFile("/proc/acpi/battery", "info");
	if (files.GetCount() == 0)
		return false;
	StringParse info = LoadFile_Safe(files[0]);
	if (info == "")
		return false;
	info.GoAfter_Init("present", ":");			present = info.GetText() == "yes";
	/*
	info.GoAfter_Init("design capacity", ":");	designCapacity = info.GetInt();
	info.GoAfter_Init("last full capacity", ":");lastFullCapacity = info.GetInt();
	info.GoAfter_Init("OEM info", ":");		 	vendor = info.GetText();
	info.GoAfter_Init("battery type", ":");		type = info.GetText();
	info.GoAfter_Init("model number", ":");		model = info.GetText();
	info.GoAfter_Init("serial number", ":");	serial = info.GetText();
	*/
	return true;
}

bool OpenCDTray(String drive)
{
	String dummy;
	return Sys("eject", dummy) > 0;	
}
bool CloseCDTray(String drive)
{
	String dummy;
	return Sys("eject -t", dummy) > 0;	
}

#endif
#if defined(PLATFORM_WIN32)
bool GetBatteryStatus(bool &discharging, int &percentage, int &remainingMin)
{
	SYSTEM_POWER_STATUS power;
	
	if(::GetSystemPowerStatus(&power) != 1)
		return false;
	
	if (power.ACLineStatus == 1)
		discharging = false;
	else
		discharging = true;
	if (power.BatteryLifePercent <= 100)
		percentage = power.BatteryLifePercent;
	else
		percentage = Null;
	if (discharging && power.BatteryLifeTime != -1) 
		remainingMin = int(power.BatteryLifeTime/60);
	else
		remainingMin = Null;
	return true;
}
bool GetBatteryInfo(bool &present/*, int &designCapacity, int &lastFullCapacity, String &vendor, String &type, String &model, String &serial*/)	
{
	SYSTEM_POWER_STATUS power;
	
	if(::GetSystemPowerStatus(&power) == 0)
		return false;
	if (power.BatteryFlag == 128)
		return false;			// No battery
	
	//designCapacity = (int)(power.BatteryFullLifeTime/60.);
	
	present = true;
	//power.ACLineStatus == 0;
	//lastFullCapacity = 0;
	//vendor = type = model = serial = "UNKNOWN";
	
	return true;
}

bool DriveOpenClose(String drive, bool open)
{
	int operation;
	if (open)
		operation = IOCTL_STORAGE_EJECT_MEDIA;
	else
		operation = IOCTL_STORAGE_LOAD_MEDIA;
	if (drive.IsEmpty())
		return false;
	else if (drive.GetCount() == 1)
		drive += ":";
	else {
		drive = drive.Left(2);
		if (drive[1] != ':')
			return false;
	}
	HANDLE hDrive;
	hDrive = CreateFile("\\\\.\\" + drive, GENERIC_READ || GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
	if (hDrive == INVALID_HANDLE_VALUE)
		return false;
	bool ret = false;
	DWORD dummyBytesReturned;
	if (DeviceIoControl(hDrive, operation, 0, 0, 0, 0, &dummyBytesReturned, 0))
		ret = true;
  	CloseHandle(hDrive);
  	return ret;
}

bool OpenCDTray(String drive)
{
	return DriveOpenClose(drive, true);
}

bool CloseCDTray(String drive)
{
	return DriveOpenClose(drive, false);
}

#endif

void Mouse_LeftClick()
{
    Mouse_LeftDown(); 
    Mouse_LeftUp(); 
}
void Mouse_RightClick()
{
    Mouse_RightDown();
    Mouse_RightUp();
}
void Mouse_MiddleClick()
{
    Mouse_MiddleDown();
    Mouse_MiddleUp();
}
void Mouse_LeftDblClick()
{
	Mouse_LeftClick();
	Mouse_LeftClick();
}
void Mouse_MiddleDblClick()
{
	Mouse_MiddleClick();
	Mouse_MiddleClick();
}
void Mouse_RightDblClick()
{
	Mouse_RightClick();
	Mouse_RightClick();
}

struct KeyCodes {
	String key; 
	int code;
}; 

#if defined(PLATFORM_WIN32)

Array <String> GetWinRegSubkeys(const String& key, HKEY base) {
	HKEY hkey;
	Array <String> subkeys;
	if(RegOpenKeyEx(base, key, 0, KEY_READ, &hkey) != ERROR_SUCCESS)
		return subkeys;
	char temp[_MAX_PATH];
	dword len;
	for(dword dw = 0; len = sizeof(temp), RegEnumKeyEx(hkey, dw, temp, &len, 0, 0, 0, 0) == ERROR_SUCCESS; dw++)
		subkeys.Add(temp);
	RegCloseKey(hkey);
	return subkeys;
}

void Mouse_LeftDown()
{
    mouse_event (MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
}
void Mouse_LeftUp()
{
    mouse_event (MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
void Mouse_MiddleDown()
{
    mouse_event (MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, 0);
}
void Mouse_MiddleUp()
{
    mouse_event (MOUSEEVENTF_MIDDLEUP, 0, 0, 0, 0);
}
void Mouse_RightDown()
{
    mouse_event (MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
}
void Mouse_RightUp()
{
    mouse_event (MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
}

bool PutWindowPlacement(HWND hwnd, RECT rcNormalPosition, POINT ptMinPosition, POINT ptMaxPosition, long showcmd, long flags)
{
    WINDOWPLACEMENT place;
        
    place.ptMinPosition = ptMinPosition;
    place.ptMaxPosition = ptMaxPosition;
    place.rcNormalPosition = rcNormalPosition;
    place.showCmd = showcmd;
    place.flags = flags;
    place.length = sizeof(place);
    return SetWindowPlacement(hwnd, &place);
}
bool TakeWindowPlacement(HWND hwnd, RECT &rcNormalPosition, POINT &ptMinPosition, POINT &ptMaxPosition, long &showcmd)
{
    WINDOWPLACEMENT place;
    
    place.length = sizeof(place);
    bool ret = GetWindowPlacement(hwnd, &place);
    ptMinPosition = place.ptMinPosition;
    ptMaxPosition = place.ptMaxPosition;
    rcNormalPosition = place.rcNormalPosition;
    showcmd = place.showCmd;     //SW_SHOWMAXIMIZED, SW_SHOWMINIMIZED, SW_SHOWNORMAL
    //flags = place.flags;        // Always 0
    
    return ret;
}
bool Window_GetRect(long windowId, long &left, long &top, long &right, long &bottom)
{
	RECT rcNormalPosition;
    POINT ptMinPosition, ptMaxPosition;
    long showcmd;	
    
	TakeWindowPlacement((HWND)windowId, rcNormalPosition, ptMinPosition, ptMaxPosition, showcmd);
	
	left = rcNormalPosition.left;
	top = rcNormalPosition.top;
	right = rcNormalPosition.right;
	bottom = rcNormalPosition.bottom;
	
	return true;
}
bool Window_SetRect(long windowId, long left, long top, long right, long bottom)
{
	RECT rcNormalPosition;
    POINT ptMinPosition, ptMaxPosition;
    long showcmd;	
    
	if (!TakeWindowPlacement((HWND)windowId, rcNormalPosition, ptMinPosition, ptMaxPosition, showcmd))
		return false;
	
	rcNormalPosition.left = left;
	rcNormalPosition.top = top;
	rcNormalPosition.right = right;
	rcNormalPosition.bottom = bottom;
	return PutWindowPlacement((HWND)windowId, rcNormalPosition, ptMinPosition, ptMaxPosition, showcmd, 0);
}
bool Mouse_SetPos(long xMove, long yMove, long windowId)
{
    long left, top, right, bottom;
    
    if (windowId != 0) {
        Window_GetRect(windowId, left, top, right, bottom);
        xMove = xMove + left;
        yMove = yMove + top;
    }
    SetCursorPos(xMove, yMove);
    
    return true;
}
bool Mouse_GetPos(long &x, long &y)
{
    POINT p;
    
    GetCursorPos (&p);
    x = p.x;
    y = p.y;
    
    return true;
}

KeyCodes keyCodes[60] = {
	"NUMPAD7", 	VK_NUMPAD7, 	"BACK", 	VK_BACK, 
	"NUMPAD8", 	VK_NUMPAD8,		"TAB", 		VK_TAB,
	"NUMPAD9", 	VK_NUMPAD9, 	"RETURN", 	VK_RETURN,
	"MULTIPLY", VK_MULTIPLY, 	"SHIFT",	VK_SHIFT,
	"ADD",		VK_ADD, 	 	"CONTROL",	VK_CONTROL,
	"SEPARATOR", VK_SEPARATOR,	"MENU", 	VK_MENU,
	"SUBTRACT", VK_SUBTRACT, 	"PAUSE", 	VK_PAUSE,
	"DECIMAL",	VK_DECIMAL,		"CAPITAL", 	VK_CAPITAL,
	"DIVIDE",	VK_DIVIDE, 		"ESCAPE",	VK_ESCAPE,
	"F1", 		VK_F1, 			"SPACE", 	VK_SPACE,
	"F2", 		VK_F2,	 		"END",		VK_END,
	"F3",		VK_F3, 			"HOME",		VK_HOME,
	"F4",		VK_F4, 			"LEFT", 	VK_LEFT,
	"F5",		VK_F5,		 	"UP", 		VK_UP,
	"F6",		VK_F6,		 	"RIGHT",	VK_RIGHT,
	"F7",		VK_F7,		 	"DOWN",		VK_DOWN,
	"F8",		VK_F8,		 	"PRINT",	VK_PRINT,
	"F9",		VK_F9,		 	"SNAPSHOT",	VK_SNAPSHOT,
	"F10",		VK_F10,		 	"INSERT",	VK_INSERT,
	"F11",		VK_F11,		 	"DELETE",	VK_DELETE,
	"F12",		VK_F12,		 	"LWIN",		VK_LWIN,
	"NUMLOCK",	VK_NUMLOCK,	 	"RWIN",		VK_RWIN,
	"SCROLL",	VK_SCROLL,	 	"NUMPAD0",	VK_NUMPAD0,
	"LSHIFT",	VK_LSHIFT,	 	"NUMPAD1", 	VK_NUMPAD1,
	"RSHIFT",	VK_RSHIFT,	 	"NUMPAD2",	VK_NUMPAD2,
	"LCONTROL",	VK_LCONTROL, 	"NUMPAD3",	VK_NUMPAD3,
	"RCONTROL",	VK_RCONTROL, 	"NUMPAD4",	VK_NUMPAD4,
	"LMENU",	VK_LMENU, 		"NUMPAD5",	VK_NUMPAD5,
	"RMENU",	VK_RMENU, 		"NUMPAD6",	VK_NUMPAD6,
	/*"PGUP", 	XK_Page_Up, 	"PGDOWN", 	XK_Page_Down
	"CAPSLOCK", XK_Caps_Lock, 	"BACKSPACE",XK_BackSpace	*/
	""
};

void PressKeyVK(int keyVK, bool hold = false, bool release = false, bool compatible = false)
{    
    long nScan, nExtended;
        
    nScan = MapVirtualKey(keyVK, 2);
    nExtended = 0;
    if (nScan == 0)
        nExtended = KEYEVENTF_EXTENDEDKEY;
    nScan = MapVirtualKey(keyVK, 0);
    
    if (compatible)
        nExtended = 0;
    
    if (!release)
        keybd_event ((BYTE)keyVK, (BYTE)nScan, nExtended, 0);
    
    if (!hold)
        keybd_event ((BYTE)keyVK, (BYTE)nScan, KEYEVENTF_KEYUP | nExtended, 0);
}

#if defined(__MINGW32__)
	#define	MAPVK_VK_TO_VSC		0
	#define	MAPVK_VSC_TO_VK   	1
	#define	MAPVK_VK_TO_CHAR  	2
	#define	MAPVK_VSC_TO_VK_EX 	3
#endif
#define	MAPVK_VK_TO_VSC_EX 	4

// This is less nice but more compatible for Notepad and MSWord for example
void PressKey(wchar key, bool hold = false, bool release = false)
{
	if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9')) {
		char buff[120];
		GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, buff, sizeof(buff));
		HKL hKeyboardLayout = ::LoadKeyboardLayout(buff, KLF_ACTIVATE);  
    	SHORT nVK = VkKeyScanExW(key, hKeyboardLayout);
    	UINT nScan = MapVirtualKeyExW(nVK, MAPVK_VK_TO_CHAR, hKeyboardLayout);
		if (!release) 
        	keybd_event((BYTE)nVK, (BYTE)nScan, 0, 0);
    	if (!hold) 
        	keybd_event((BYTE)nVK, (BYTE)nScan, KEYEVENTF_KEYUP, 0);
	} else {
		String numStr = FormatIntDec(key, 5, '0');
		PressKeyVK(VK_LMENU, true);
		PressKeyVK(VK_NUMPAD0 + numStr[0] - '0');
		PressKeyVK(VK_NUMPAD0 + numStr[1] - '0');
		PressKeyVK(VK_NUMPAD0 + numStr[2] - '0');
		PressKeyVK(VK_NUMPAD0 + numStr[3] - '0');
		PressKeyVK(VK_NUMPAD0 + numStr[4] - '0');
		PressKeyVK(VK_LMENU, false, true);
	}
}
/*
void PressKey(wchar key, bool hold = false, bool release = false)
{
	bool caps, num, scroll;
	if (IsLetter(key)) {
    	GetKeyLockStatus(caps, num, scroll);
    	if (caps) 
    		SetKeyLockStatus(false, num, scroll);	
    }
 	char buff[120];
	GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILANGUAGE, buff, sizeof(buff));
	HKL hKeyboardLayout = ::LoadKeyboardLayout(buff, KLF_ACTIVATE);  
    SHORT nVK = VkKeyScanExW(key, hKeyboardLayout);
	if (nVK == -1) {	// Last resource !!
		String numStr = FormatIntDec(key, 4, '0');
		PressKeyVK(VK_LMENU, true);
		PressKeyVK(VK_NUMPAD0 + numStr[0] - '0');
		PressKeyVK(VK_NUMPAD0 + numStr[1] - '0');
		PressKeyVK(VK_NUMPAD0 + numStr[2] - '0');
		PressKeyVK(VK_NUMPAD0 + numStr[3] - '0');
		PressKeyVK(VK_LMENU, false, true);
    	return;
	}
    UINT nScan = MapVirtualKeyExW(nVK, MAPVK_VK_TO_CHAR, hKeyboardLayout);
    long nExtended = 0;
    if (nScan == 0)
        nExtended = KEYEVENTF_EXTENDEDKEY;
    
    nScan = MapVirtualKeyExW(nVK, MAPVK_VK_TO_VSC, hKeyboardLayout);
    
    bool shift, ctrl, alt;
    
    shift = nVK & 0x100;
    ctrl  = nVK & 0x200;
    alt   = nVK & 0x400;    
    nVK   = nVK & 0xFF;
    
    if (!release) {
        if (shift)
            keybd_event (VK_SHIFT, 0, 0, 0);
        if (ctrl)
            keybd_event (VK_CONTROL, 0, 0, 0);
        if (alt)
            keybd_event (VK_MENU, 0, 0, 0);
    
        keybd_event ((BYTE)nVK, (BYTE)nScan, nExtended, 0);
    }
    if (!hold) {
        keybd_event ((BYTE)nVK, (BYTE)nScan, KEYEVENTF_KEYUP | nExtended, 0);
    
   		if (shift)
            keybd_event (VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
        if (ctrl)
            keybd_event (VK_CONTROL, 0, KEYEVENTF_KEYUP, 0);
        if (alt)
            keybd_event (VK_MENU, 0, KEYEVENTF_KEYUP, 0);
    }
    if (IsLetter(key) && caps) 
    	SetKeyLockStatus(true, num, scroll);	
}
*/

bool GetKeyLockStatus(bool &caps, bool &num, bool &scroll)
{
    caps = GetKeyState(VK_CAPITAL);
    num = GetKeyState(VK_NUMLOCK);
    scroll = GetKeyState(VK_SCROLL);
    return true;
}
bool SetKeyLockStatus(bool caps, bool num, bool scroll)
{
	bool capsnow, numnow, scrollnow;
	
	GetKeyLockStatus(capsnow, numnow, scrollnow);
	if (capsnow != caps)
		PressKeyVK(VK_CAPITAL);
	if (numnow != num)
		PressKeyVK(VK_NUMLOCK);
	if (scrollnow != scroll)
		PressKeyVK(VK_SCROLL);
	return true;
}

#if defined(__MINGW32__) 
	#define labs(x)	labs((Upp::int64)(x))
#elif defined(_MSC_VER)
	#define labs(x)	abs(x)
#endif

bool Window_SaveCapture(long windowId, String fileName, int left, int top, int width, int height)
{
	if (windowId == 0)
		windowId = (long)GetDesktopWindow();

	if (GetFileExt(fileName) != ".bmp")
		fileName += ".bmp";
	 
	RECT rc;
	GetWindowRect ((HWND)windowId, &rc); 

	if (left == -1)
		left = rc.left;
	if (top == -1)
		top = rc.top;
	if (width == -1)
		width	= rc.right-rc.left;
	if (height == -1)
		height	= rc.bottom-rc.top;

	HDC hDC = GetDC(0);
	HDC memDC = CreateCompatibleDC (hDC);
	HBITMAP hb = CreateCompatibleBitmap (hDC, width, height);
	HBITMAP OldBM = (HBITMAP)SelectObject(memDC, hb);
	BitBlt(memDC, 0, 0, width, height , hDC, left, top , SRCCOPY);

    FILE *file = NULL;
  	BITMAPINFO bmpInfo;
    BITMAPFILEHEADER bmpFileHeader;
    ZeroMemory(&bmpInfo, sizeof(BITMAPINFO));
    bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    GetDIBits(hDC, hb, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS);
   	if(bmpInfo.bmiHeader.biSizeImage <= 0)
     	bmpInfo.bmiHeader.biSizeImage = bmpInfo.bmiHeader.biWidth*labs(bmpInfo.bmiHeader.biHeight)*(bmpInfo.bmiHeader.biBitCount+7)/8;
   	char *cbuf = new char[bmpInfo.bmiHeader.biSizeImage];
   	LPVOID buf = cbuf;
	bmpInfo.bmiHeader.biCompression = BI_RGB;
	GetDIBits(hDC, hb, 0, bmpInfo.bmiHeader.biHeight, buf, &bmpInfo, DIB_RGB_COLORS);
	if((file = _wfopen(fileName.ToWString(),L"wb")) == NULL)
  		return false;
	bmpFileHeader.bfReserved1 = 0;
	bmpFileHeader.bfReserved2 = 0;
	bmpFileHeader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+bmpInfo.bmiHeader.biSizeImage;
	bmpFileHeader.bfType = 19778;
	bmpFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	fwrite(&bmpFileHeader, sizeof(BITMAPFILEHEADER), 1, file);
	fwrite(&bmpInfo.bmiHeader,sizeof(BITMAPINFOHEADER), 1, file);
	fwrite(buf,bmpInfo.bmiHeader.biSizeImage, 1, file);
	
	delete[] cbuf;
	fclose(file);

	SelectObject(hDC, OldBM);
	DeleteObject(hb);
	DeleteDC(memDC);
	ReleaseDC(0, hDC);
	
	return true;
}

class ScreenGrab {
private:	
	enum {GRAB_MODE_DESKTOP, GRAB_MODE_WINDOW, GRAB_MODE_RECT};
	static HDC lDCDest;
	static HGDIOBJ lDib;
	static void *lDibPtr;
	static HGDIOBJ lBmpOld;
	static BITMAPINFO tBmpInfo;
	static PAVIFILE lAVIPtrFile;
	static PAVISTREAM lAVIPtrStrm;
	static AVISTREAMINFO tAVIHdr;
	static AVIFILEINFO tAVIFile;
	long lAVICnt;
	double frameRate;
	long numFrames; 		// number of frames in video stream
	long firstFrame; 		// position of the first video frame
	String fileName;
	bool viewMouse;
	bool opened;
	int grabMode;
	int hwnd;
	int left;
	int top; 
	int width;
	int height;

	bool AVIOpen(bool create = true);
	bool AVIWrite();
	void AVIClose();
	HICON GetCursorHandle();
	bool DIBCreate(HWND lHandleSource, long lWidth, long lHeight, long lPosX, long lPosY);
	void DIBClean();
	bool ScreenshotMemory();

public:
	ScreenGrab(String fileName, int secsFrame = 1, bool viewMouse = true);
	~ScreenGrab();
	
	bool IniGrabDesktop();
	bool IniGrabWindow(long handle);
	bool IniGrabDesktopRectangle(int left, int top, int width, int height);
	bool Grab(unsigned duration);
	bool GrabSnapshot();
	void Close();
};

bool Record_Desktop(String fileName, int duration, int secsFrame, bool viewMouse)
{
	ScreenGrab grab(fileName, secsFrame, viewMouse);
	if (!grab.IniGrabDesktop())
		return false;
	if (!grab.Grab(duration))
		return false;
	grab.Close();
	return true;
}
bool Record_DesktopRectangle(String fileName, int duration, int left, int top, int width, int height, int secsFrame, bool viewMouse)
{
	ScreenGrab grab(fileName, secsFrame, viewMouse);
	if (!grab.IniGrabDesktopRectangle(left, top, width, height))
		return false;
	if (!grab.Grab(duration))
		return false;
	grab.Close();
	return true;
}
bool Record_Window(String fileName, int duration, long handle, int secsFrame, bool viewMouse)
{
	ScreenGrab grab(fileName, secsFrame, viewMouse);
	if (!grab.IniGrabWindow(handle))
		return false;
	if (!grab.Grab(duration))
		return false;
	grab.Close();
	return true;
}

bool ScreenGrab::AVIOpen(bool create)
{
    long lRet, mode, res;
    
    AVIFileInit();
    if (create)
        mode = OF_CREATE | OF_WRITE;
    else
        mode = OF_SHARE_DENY_WRITE;

    lRet = AVIFileOpen(&lAVIPtrFile, fileName, mode, 0);
    if (lRet == AVIERR_OK && create) {
      	tAVIHdr.fccType = streamtypeVIDEO;
        tAVIHdr.fccHandler = 0;
       	tAVIHdr.dwScale = 100;		
       	tAVIHdr.dwRate = (DWORD)(tAVIHdr.dwScale*frameRate);	
       	//tAVIHdr.dwQuality = -1;
       	tAVIHdr.dwSuggestedBufferSize = tBmpInfo.bmiHeader.biSizeImage;
        SetRect(&(tAVIHdr.rcFrame), 0, 0, tBmpInfo.bmiHeader.biWidth, tBmpInfo.bmiHeader.biHeight);
        lRet = AVIFileCreateStream(lAVIPtrFile, &lAVIPtrStrm, &tAVIHdr);
        if (lRet == AVIERR_OK) {
            lRet = AVIStreamSetFormat(lAVIPtrStrm, 0, &(tBmpInfo.bmiHeader), sizeof(tBmpInfo.bmiHeader));
            if (lRet == AVIERR_OK) 
                lAVICnt = 0;
        }
    } else {
        res = AVIFileGetStream(lAVIPtrFile, &lAVIPtrStrm, streamtypeVIDEO, 0);
        if (res != AVIERR_OK)
            return false;
        firstFrame = AVIStreamStart(lAVIPtrStrm);
        if (firstFrame != -1) 
			return false;
        numFrames = AVIStreamLength(lAVIPtrStrm); 
        if (numFrames == -1)
        	return false;
        res = AVIFileInfo(lAVIPtrFile, &tAVIFile, sizeof(tAVIFile));
        if (res != AVIERR_OK)
        	return false;
        res = AVIStreamInfo(lAVIPtrStrm, &tAVIHdr, sizeof(tAVIHdr));
        if (res != AVIERR_OK) 
			return false;
    }
    return true;
}

bool ScreenGrab::AVIWrite()
{
    HRESULT  lRet;
    
    lRet = AVIStreamWrite(lAVIPtrStrm, lAVICnt, 1, lDibPtr, tBmpInfo.bmiHeader.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);
    if (lRet == AVIERR_OK) {
        lAVICnt++;
        return true;
    } else
        return false;
}

void ScreenGrab::AVIClose()
{
    if (lAVIPtrStrm != 0) 
    	AVIStreamClose(lAVIPtrStrm);
    if (lAVIPtrFile != 0) 
		AVIFileClose(lAVIPtrFile);
    AVIFileExit();
}

HICON ScreenGrab::GetCursorHandle()
{
    HWND lHandle;
    POINT lpPos;
    long lThreadID;
    long lCurrentThreadID;
 
  	GetCursorPos(&lpPos);
    lHandle = WindowFromPoint(lpPos);
    lThreadID = GetWindowThreadProcessId(lHandle, 0);
    lCurrentThreadID = GetWindowThreadProcessId((HWND)GetWindowIdFromProcessId(GetProcessId()), 0); 
    HICON ret;
    if (lThreadID != lCurrentThreadID) {
        if (AttachThreadInput(lCurrentThreadID, lThreadID, true)) {
            ret = GetCursor();
            AttachThreadInput(lCurrentThreadID, lThreadID, false);
        }
    } else
        ret = GetCursor();
   
    return ret;
}

bool ScreenGrab::DIBCreate(HWND lHandleSource, long lWidth, long lHeight, long lPosX, long lPosY) 
{
    HDC lDCSource, lDCSourceDesktop;
    POINT lpCursorPos;
    
    lDCSource = GetWindowDC(lHandleSource);
    lDCSourceDesktop = GetWindowDC(0);
    
    bool ret = false;
    if (lDCSource != 0 && lDCSourceDesktop != 0) {
        lDCDest = CreateCompatibleDC(lDCSource);
        if (lDCDest != 0) {
            tBmpInfo.bmiHeader.biSize = sizeof(tBmpInfo.bmiHeader);
          	tBmpInfo.bmiHeader.biWidth = lWidth;
          	tBmpInfo.bmiHeader.biHeight = lHeight;
			tBmpInfo.bmiHeader.biPlanes = 1;
			tBmpInfo.bmiHeader.biBitCount = 24;
			tBmpInfo.bmiHeader.biCompression = 0;
			tBmpInfo.bmiHeader.biSizeImage = ((tBmpInfo.bmiHeader.biWidth * 3 + 3) & 0xFFFFFFFC) * tBmpInfo.bmiHeader.biHeight;
            lDib = CreateDIBSection(lDCDest, &tBmpInfo, 0, &lDibPtr, 0, 0);
            if (lDib != 0) {
                lBmpOld = SelectObject(lDCDest, lDib);
                BitBlt(lDCDest, 0, 0, lWidth, lHeight, lDCSourceDesktop, lPosX, lPosY, 0xCC0020);
                if (viewMouse) {
                    GetCursorPos(&lpCursorPos);
                    DrawIcon(lDCDest, lpCursorPos.x - lPosX, lpCursorPos.y - lPosY, GetCursorHandle());
                }
                ret = true;
            }
        }
    }
    ReleaseDC(lHandleSource, lDCSource);
    ReleaseDC(0, lDCSourceDesktop);
    return ret;
}

void ScreenGrab::DIBClean()
{
    SelectObject(lDCDest, lBmpOld);
    DeleteDC(lDCDest);
    DeleteObject(lDib);
}

bool ScreenGrab::ScreenshotMemory() 
{
    HWND lHandle;
    RECT lpRect;
    long lWidth, lHeight;
    long lPosX, lPosY;
    
	switch (grabMode) {
	case GRAB_MODE_DESKTOP:
    	lHandle = GetDesktopWindow();
    	GetWindowRect(lHandle, &lpRect);
    	lWidth = lpRect.right - lpRect.left;
    	lHeight = lpRect.bottom - lpRect.top;
    	lPosX = lpRect.left;
    	lPosY = lpRect.top;
    	break;
	case GRAB_MODE_WINDOW:
    	lHandle = (HWND)hwnd;
    	GetWindowRect(lHandle, &lpRect);
    	lWidth = lpRect.right - lpRect.left;
    	lHeight = lpRect.bottom - lpRect.top;
    	lPosX = lpRect.left;
    	lPosY = lpRect.top;
    	break;
	case GRAB_MODE_RECT:
    	lHandle = GetDesktopWindow();
    	GetWindowRect(lHandle, &lpRect);
    	lWidth = width;
    	lHeight = height;
    	lPosX = left;
    	lPosY = top;
    	break;
    default:
    	throw Exc(t_("Unknown grab mode"));
    	return false;
	}
    if (DIBCreate(lHandle, lWidth, lHeight, lPosX, lPosY))
        return true;
  	else
    	return false;
}

HDC ScreenGrab::lDCDest;
HGDIOBJ ScreenGrab::lDib;
void *ScreenGrab::lDibPtr;
HGDIOBJ ScreenGrab::lBmpOld;
PAVIFILE ScreenGrab::lAVIPtrFile;
PAVISTREAM ScreenGrab::lAVIPtrStrm;
BITMAPINFO ScreenGrab::tBmpInfo;
AVISTREAMINFO ScreenGrab::tAVIHdr;
AVIFILEINFO ScreenGrab::tAVIFile;

ScreenGrab::ScreenGrab(String _fileName, int secsFrame, bool _viewMouse)
{
	opened = false;
	fileName = _fileName;
	viewMouse = _viewMouse;
	frameRate = 1./secsFrame;
}

ScreenGrab::~ScreenGrab()
{
	Close();
}

void ScreenGrab::Close()
{
	if (!opened)
		return;
    AVIClose();
    DIBClean();
    opened = false;
}

bool ScreenGrab::IniGrabDesktop()
{
	opened = true;
	grabMode = GRAB_MODE_DESKTOP;
	if (!ScreenshotMemory())
    	return false;
  	if (!AVIOpen())
  		return false;
	return true;
}

bool ScreenGrab::IniGrabWindow(long handle)
{
	opened = true;
	grabMode = GRAB_MODE_WINDOW;
	hwnd = handle;
	if (!ScreenshotMemory())
    	return false;
  	if (!AVIOpen())
  		return false;
	return true;
}

bool ScreenGrab::IniGrabDesktopRectangle(int _left, int _top, int _width, int _height)
{
	opened = true;	
	grabMode = GRAB_MODE_RECT;
	left = _left;
	top = _top;
	width = _width;
	height = _height;
	if (!ScreenshotMemory())
    	return false;
  	if (!AVIOpen())
  		return false; 	
	return true;	
}
	
bool ScreenGrab::Grab(unsigned duration)
{
	if (!opened)
		return false;
    TimeStop timer;
    timer.Reset();
    while (timer.Elapsed() < duration*1000) {
        if (!ScreenshotMemory())
        	return false;
        if (!AVIWrite())
            return false;
        while (timer.Elapsed() < (lAVICnt*1000.)/frameRate)
	        Sleep(10);//DoEvents();
    }
    return true;
}

bool ScreenGrab::GrabSnapshot()
{
	if (!opened)
		return false;
  	if (!ScreenshotMemory())
       	return false;
   	if (!AVIWrite())
      	return false;
    return true;
}

#endif

#ifdef PLATFORM_POSIX

bool Window_GetRect(long windowId, long &left, long &top, long &right, long &bottom)
{
	SetSysInfoX11ErrorHandler();
	_XDisplay *dpy = XOpenDisplay (NULL);
	if (!dpy) {
		SetX11ErrorHandler();
		return false;
	}
	bool ret = false;
	Window rt;
	int x, y, rx, ry; 
	unsigned int width, height, bw, depth_; 
	if (XGetGeometry(dpy, windowId, &rt, &x, &y, &width, &height, &bw, &depth_)) { 
		left = x;
		top = y;
		right = x + width;
		bottom = y + height;
		ret = true;
	}
//	Window child; 		
//		if (XTranslateCoordinates (dpy, windowId, rt, 0, 0, &rx, &ry, &child))  
//			printf ("%d %d", rx - bw, ry - bw); 
	
	XCloseDisplay (dpy);
	SetX11ErrorHandler();
	return ret; 
}

bool Window_SetRect(long windowId, long left, long top, long right, long bottom)
{
	SetSysInfoX11ErrorHandler();
	_XDisplay *dpy = XOpenDisplay (NULL);
	if (!dpy) {
		SetX11ErrorHandler();
		return false;
	}
	bool ret = false;

	if (XMoveWindow(dpy, windowId, left, top)) {
		if (!XResizeWindow(dpy, windowId, right-left, bottom-top))
			ret = false;
		else
			ret = true;
	}
	XCloseDisplay (dpy);
	SetX11ErrorHandler();
	return ret; 
}

bool Mouse_GetPos(long &x, long &y)
{
	SetSysInfoX11ErrorHandler();
	_XDisplay *dpy = XOpenDisplay (NULL);
	if (!dpy) {
		SetX11ErrorHandler();
		return false;
	}   
	bool ret = false;
	Window root, child;
	Window r = DefaultRootWindow(dpy);
	int retx, rety;
	int wx, wy;
	unsigned int keys_buttons;
   	if (XQueryPointer(dpy, r, &root, &child, &retx, &rety, &wx, &wy, &keys_buttons)) {
   		x = wx;
   		y = wy;
   		ret = true;
	} else 
		x = y = -1;
		
	XCloseDisplay (dpy);
	SetX11ErrorHandler();
	
	return ret;
}

bool Mouse_SetPos(long x, long y, long windowId)
{
	SetSysInfoX11ErrorHandler();
	_XDisplay *dpy = XOpenDisplay (NULL);
	if (!dpy) {
		SetX11ErrorHandler();
		return false;
	}   
    long left, top, right, bottom;
    Window r = DefaultRootWindow(dpy);
    if (windowId != 0) {
        Window_GetRect(windowId, left, top, right, bottom);
        x = x + left;
        y = y + top;
    }     
	XWarpPointer(dpy, None, r, 0, 0, 0, 0, x, y);
	XCloseDisplay (dpy);
	SetX11ErrorHandler();
	
	return true;
}

// libxtst-dev
void Mouse_FakeClick(int button, int press) {
	_XDisplay *dpy = XOpenDisplay(NULL);
	XTestFakeButtonEvent(dpy, button, press, CurrentTime);
	XFlush(dpy);
	XCloseDisplay(dpy);
}

void Mouse_LeftDown() {
	Mouse_FakeClick(1, True);
}
void Mouse_LeftUp() {
	Mouse_FakeClick(1, False);
}
void Mouse_MiddleDown() {
	Mouse_FakeClick(2, True);
}
void Mouse_MiddleUp() {
	Mouse_FakeClick(2, False);
}
void Mouse_RightDown() {
	Mouse_FakeClick(3, True);
}
void Mouse_RightUp() {
	Mouse_FakeClick(3, False);
}

void PressKeyVK(int key, _XDisplay *dpy = NULL) {
	bool local = false;
	if (!dpy) {
		if (!(dpy = XOpenDisplay(NULL)))
			return;
		local = true;
	}
	XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, key), True, CurrentTime);
	XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, key), False, CurrentTime);
	if (local) {
		XFlush(dpy);
		XCloseDisplay(dpy);
	}
}

void PressKey(wchar key, _XDisplay *dpy = NULL) {
	bool local = false;
	if (!dpy) {
		if (!(dpy = XOpenDisplay(NULL)))
			return;
		local = true;
	}
	wchar k = key;
	if (key > 0x00ff)
    	key = key | 0x01000000;
 	
 	bool shift = false;
	KeyCode code = XKeysymToKeycode(dpy, key);
	if (code != 0) { 
		if (XKeycodeToKeysym(dpy, code, 0) != key) {
			if (XKeycodeToKeysym(dpy, code, 1) == key) 
				shift = true;
			else
				code = 0;
		}
	} else {  
		int firstKeycode, maxKeycode;
		int keysymsPerKeycode;
		
		XDisplayKeycodes(dpy, &firstKeycode, &maxKeycode);
		KeySym *keysyms = XGetKeyboardMapping(dpy, firstKeycode, maxKeycode-firstKeycode+1, &keysymsPerKeycode);
      	int indx = (maxKeycode-firstKeycode-1)*keysymsPerKeycode;
		keysyms[indx] = key;
      	XChangeKeyboardMapping(dpy, firstKeycode, keysymsPerKeycode, keysyms, maxKeycode-firstKeycode);
      	XSync(dpy, False);
      	code = maxKeycode-1;
      	if (XKeycodeToKeysym(dpy, code, 0) != key) {
			if (XKeycodeToKeysym(dpy, code, 1) == key) 
				shift = true;
		}
    }
	if (code != 0) {
		if (shift)
			XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_Shift_L), True, CurrentTime);	
		XTestFakeKeyEvent(dpy, code, True,  CurrentTime);
		XTestFakeKeyEvent(dpy, code, False, CurrentTime);
	 	if (shift)
			XTestFakeKeyEvent(dpy, XKeysymToKeycode(dpy, XK_Shift_L), False, CurrentTime);	
	} 	
	if (local) {
 		XFlush(dpy);
 		XCloseDisplay(dpy);
	}
}

bool GetKeyLockStatus0(bool &caps, bool &num, bool &scroll, _XDisplay *dpy) {
	int x, y, xx, yy;
	Window dm1, dm2;
	unsigned int sKbdState;
	
	if(!XQueryPointer(dpy, DefaultRootWindow(dpy), &dm1, &dm2, &x, &y, &xx, &yy, &sKbdState))
		return false;

	caps   = sKbdState & LockMask;
	num    = sKbdState & Mod2Mask;
	scroll = sKbdState & Mod5Mask;
	
	return true;
}

bool GetKeyLockStatus(bool &caps, bool &num, bool &scroll) {
	_XDisplay *dpy;
	if (!(dpy = XOpenDisplay(NULL)))
		return false;

	if (!GetKeyLockStatus0(caps, num, scroll, dpy)) {
		XCloseDisplay(dpy);
		return false;
	}
	XFlush(dpy);
	XCloseDisplay(dpy);
	return true;
}

bool SetKeyLockStatus(bool caps, bool num, bool scroll) {
	_XDisplay *dpy;
	if (!(dpy = XOpenDisplay(NULL)))
		return false;
			
	bool oldcaps, oldnum, oldscroll; 
	if (!GetKeyLockStatus0(oldcaps, oldnum, oldscroll, dpy)) {
		XCloseDisplay(dpy);
		return false;
	}
	if (caps != oldcaps) 
		PressKeyVK(XK_Caps_Lock, dpy);
	if (num != oldnum) 
		PressKeyVK(XK_Num_Lock, dpy);
	if (scroll != oldscroll) 
		PressKeyVK(XK_Scroll_Lock, dpy);

	XFlush(dpy);
	XCloseDisplay(dpy);
	return true;
}

bool Window_SaveCapture(long windowId, String fileName, int left, int top, int width, int height)
{
	if (GetFileExt(fileName) != ".xwd")
		fileName += ".xwd";
	
	String command;
	if (windowId == 0)
		command = "xwd -root -silent -out \"" + fileName + "\"";
	else
		command = "xwd -id " + FormatLong(windowId) + " -silent -out \"" + fileName + "\"";
	
	String strret;
	return Sys(command, strret) >= 0;
}

KeyCodes keyCodes[] = {
	"NUMPAD7", 	XK_KP_7,	 	"BACK", 	XK_BackSpace, 
	"NUMPAD8", 	XK_KP_8,		"TAB", 		XK_Tab,
	"NUMPAD9", 	XK_KP_9, 		"RETURN", 	XK_Return,
	"MULTIPLY", XK_KP_Multiply,	"SHIFT",	XK_Shift_Lock,
	"ADD",		XK_KP_Add, 	 	"CONTROL",	XK_Control_L,
	"SEPARATOR", XK_KP_Separator,"MENU", 	XK_Super_L,
	"SUBTRACT", XK_KP_Subtract,	"PAUSE", 	XK_Pause,
	"DECIMAL",	XK_KP_Decimal,	/*"CAPITAL", 	VK_CAPITAL,*/
	"DIVIDE",	XK_KP_Divide,	"ESCAPE",	XK_Escape,
	"F1", 		XK_F1, 			"SPACE", 	XK_KP_Space,
	"F2", 		XK_F2,	 		"END",		XK_End,
	"F3",		XK_F3, 			"HOME",		XK_Home,
	"F4",		XK_F4, 			"LEFT", 	XK_Left,
	"F5",		XK_F5,		 	"UP", 		XK_Up,
	"F6",		XK_F6,		 	"RIGHT",	XK_Right,
	"F7",		XK_F7,		 	"DOWN",		XK_Down,
	"F8",		XK_F8,		 	"PRINT",	XK_Sys_Req,
	"F9",		XK_F9,		 	/*"SNAPSHOT",	VK_SNAPSHOT,*/
	"F10",		XK_F10,		 	"INSERT",	XK_Insert,
	"F11",		XK_F11,		 	"DELETE",	XK_Delete,
	"F12",		XK_F12,		 	"LWIN",		XK_Meta_L,
	"NUMLOCK",	XK_Num_Lock, 	"RWIN",		XK_Meta_R,
	"SCROLL",	XK_Scroll_Lock,	"NUMPAD0",	XK_KP_0,
	"LSHIFT",	XK_Shift_L,	 	"NUMPAD1", 	XK_KP_1,
	"RSHIFT",	XK_Shift_R,	 	"NUMPAD2",	XK_KP_2,
	"LCONTROL",	XK_Control_L, 	"NUMPAD3",	XK_KP_3,
	"RCONTROL",	XK_Control_R, 	"NUMPAD4",	XK_KP_4,
	"LMENU",	XK_Super_L,		"NUMPAD5",	XK_KP_5,
	"RMENU",	XK_Super_R, 	"NUMPAD6",	XK_KP_6,
	"PGUP", 	XK_Page_Up, 	"PGDOWN", 	XK_Page_Down,
	"CAPSLOCK", XK_Caps_Lock, 	"BACKSPACE",XK_BackSpace,
	""
};

#endif

int GetKeyCode(String key) {
	for (int i = 0; keyCodes[i].code != 0; ++i)
		if (keyCodes[i].key == key)
			return keyCodes[i].code;
	return 0;
}

void Keyb_SendKeys(String text, long finalDelay, long delayBetweenKeys)
{
	Array <wchar> virt;
	bool inKey = false;
	String key = "";
	WString wtext(text);
	for (int i = 0; i < wtext.GetCount(); ++i) {
		bool vk = false;
		Sleep(delayBetweenKeys);
		wchar c = wtext[i];
		if (c == '{')
			inKey = true;
		else if (c == '}') {
			if (key == "{") 
				c = '{';
			else {
				c = GetKeyCode(key);
				vk = true;
			}
			inKey = false;
			key = "";
		} else if (inKey == 1)
			key.Cat(c);
		else if (c == '\n') {
			c = GetKeyCode("RETURN");
			vk = true;
		}
 		if (inKey == false) {
			if (!vk) 
 				PressKey(c);
#if defined(PLATFORM_WIN32)
			else {
				PressKeyVK(c, true);
				virt.Add(c);
			}
#endif
		}
	}
#if defined(PLATFORM_WIN32)
	for (int i = 0; i < virt.GetCount(); ++i)
		PressKeyVK(virt[i], false, true);
#endif
	Sleep(finalDelay);
}

bool Snap_Desktop(String fileName)
{
	return Window_SaveCapture(0, fileName);
}
bool Snap_DesktopRectangle(String fileName, int left, int top, int width, int height)
{
	return Window_SaveCapture(0, fileName, left, top, width, height);
}
bool Snap_Window(String fileName, long handle)
{
	return Window_SaveCapture(handle, fileName);
}

#ifdef PLATFORM_POSIX

void SetDesktopWallPaper(const char *path)
{
	String desktopManager = GetDesktopManagerNew();
	
	if (desktopManager == "gnome") {
		Sys("gconftool-2 -t str -s /desktop/gnome/background/picture_filename \"" + String(path) + "\"");
		String mode;
		if (*path == '\0')
			mode = "none";		// Values "none", "wallpaper", "centered", "scaled", "stretched"
		else
			mode = "stretched";
		Sys("gconftool-2 -t str -s /desktop/gnome/background/picture_options \"" + mode + "\"");	
	} else if (desktopManager == "kde") {
			// 1: disabled, only background color
			// 2: tiled with first image in top left corner
			// 3: tiled with first image centered
			// 4: centered stretched with proportions kept until one side hits screen, space filled by background color
			// 5: same as 4, though wallpaper aligned to top left and space after stretching filled by tiling
			// 6: stretched to fit screen 
		int mode;
		if (*path == '\0')
			mode = 1;
		else
			mode = 6;
		Sys("dcop kdesktop KBackgroundIface setWallpaper \"" + String(path) + "\" " + AsString(mode));
	} else
		throw Exc(t_("Not possible to change Desktop bitmap"));
}

#endif

#if defined(PLATFORM_WIN32)

void SetDesktopWallPaper(const char *path)
{
    if (0 == SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, (LPVOID)path, SPIF_UPDATEINIFILE || SPIF_SENDWININICHANGE))
        throw Exc(Format(t_("Error %s changing Desktop bitmap"), AsString(GetLastError())));
}
#endif
