All Downloads are FREE. Search and download functionalities are using the official Maven repository.

source.requirements.NativeTerminal.cpp Maven / Gradle / Ivy

#include 
#include 
#ifdef _WIN32
	#include 
#endif

#include "requirements/Util.h"
#include "requirements/NativeTerminal.h"

jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
	return JNI_VERSION_1_6;
}

#ifdef _WIN32
	#ifndef VC_EXTRALEAN
	#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
	#endif

	// Modify the following defines if you have to target a platform prior to the ones specified below.
	// Refer to MSDN for the latest info on corresponding values for different platforms.
	#ifndef WINVER
	#define WINVER 0x0A00
	#endif

	#ifndef _WIN32_WINNT
	#define _WIN32_WINNT 0x0A00
	#endif

	#include 
	#include 
	#include 

	static struct State
	{
		/**
		 * True after connected() is called.
		 */
		bool connected;
		/**
		 * True if stdout is connected to the terminal.
		 */
		bool connectedToStdout;
		/**
		 * The stdout handle.
		 */
		HANDLE stdoutHandle;
		/**
		* The stdout mode when open() was invoked (prior to any modification).
		*/
		DWORD stdoutMode;
		/**
		* The current mode of stdout.
		*/
		DWORD currentMode;
	} state;
#endif // _WIN32

/**
 * @param env the JVM environment
 * @param name the name of the terminal encoding
 * @return the TerminalEncoding enum value corresponding to the name
 */
jobject terminalEncoding(JNIEnv* env, const char* name)
{
	const char* enumName = "org/bitbucket/cowwoc/requirements/core/terminal/TerminalEncoding";
	jclass enumClass = env->FindClass(enumName);
	if (enumClass == 0)
	{
		assert(env->ExceptionCheck());
		return 0;
	}

	std::string enumFieldType("L");
	enumFieldType += enumName;
	enumFieldType += ";";
	jfieldID enumField = env->GetStaticFieldID(enumClass, name, enumFieldType.c_str());
	if (enumField == 0)
	{
		assert(env->ExceptionCheck());
		return 0;
	}
	return env->GetStaticObjectField(enumClass, enumField);
}

/**
* @param env the JNI environment
* @param o an object
* @return the String representation of the object
*/
std::string toString(JNIEnv* env, jobject o)
{
	jclass object = env->FindClass("java/lang/Object");
	if (object == 0)
	{
		assert(env->ExceptionCheck());
		return 0;
	}
	jmethodID toString = env->GetMethodID(object, "toString", "()Ljava/lang/String;");
	jstring result = (jstring) env->CallObjectMethod(o, toString);
	const char* charArray = env->GetStringUTFChars(result, 0);
	return std::string(charArray);
}

#ifdef _WIN32
	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.connect()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	void JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_connect
	(JNIEnv* env, jobject jthis)
	{
		state.connected = true;

		Exceptions exceptions(env);
		// See http://stackoverflow.com/a/3650507/14731
		CONSOLE_SCREEN_BUFFER_INFO sbi;
		HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
		BOOL rc = GetConsoleScreenBufferInfo(stdoutHandle, &sbi);
		DWORD lastError = GetLastError();
		state.connectedToStdout = rc && lastError != ERROR_INVALID_HANDLE;
		if (state.connectedToStdout)
		{
			state.stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
			if (state.stdoutHandle == INVALID_HANDLE_VALUE)
			{
				exceptions.throwIOException("Failed to get stdout handle");
				return;
			}

			if (!GetConsoleMode(state.stdoutHandle, &state.stdoutMode))
				exceptions.throwIOException("Failed to get stdout mode", GetLastError());
			state.currentMode = state.stdoutMode;
		}
	}

	typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);

	/**
	 * @param majorVersion a major version number
	 * @param minorVersion a minor version number
	 * @param buildVersion a build version number
	 * @return true if the Windows version is greater than or equal to the specified number
	 */
	bool isWindowsVersionOrGreater(WORD majorVersion, WORD minorVersion, WORD buildVersion)
	{
		// See http://stackoverflow.com/a/36545162/14731 and https://github.com/DarthTon/Blackbone/blob/master/contrib/VersionHelpers.h#L78
		RTL_OSVERSIONINFOW verInfo = { 0 };
		verInfo.dwOSVersionInfoSize = sizeof(verInfo);

		static auto RtlGetVersion = (RtlGetVersionPtr) GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetVersion");

		if (RtlGetVersion != 0 && RtlGetVersion(&verInfo) == 0)
		{
			if (verInfo.dwMajorVersion > majorVersion)
				return true;
			else if (verInfo.dwMajorVersion < majorVersion)
				return false;

			if (verInfo.dwMinorVersion > minorVersion)
				return true;
			else if (verInfo.dwMinorVersion < minorVersion)
				return false;

			if (verInfo.dwBuildNumber >= buildVersion)
				return true;
		}
		return false;
	}

	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.isConnectedToStdout()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	jboolean JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_isConnectedToStdout
	(JNIEnv* env, jobject jthis)
	{
		return state.connectedToStdout;
	}

	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.setEncoding()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	void JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_setEncoding
	(JNIEnv* env, jobject jthis, jobject encoding)
	{
		Exceptions exceptions(env);
		jobject NONE = terminalEncoding(env, "NONE");
		DWORD newStdoutMode;
		if (env->IsSameObject(encoding, NONE))
			newStdoutMode = state.stdoutMode & !ENABLE_VIRTUAL_TERMINAL_PROCESSING;
		else
		{
			std::deque supportedEncodings;
			// build 10586 added 16-bit color support: http://www.nivot.org/blog/post/2016/02/04/Windows-10-TH2-%28v1511%29-Console-Host-Enhancements
			assert(IsWindowsVersionOrGreater(10, 0, 10586));
			supportedEncodings.push_back("XTERM_8COLOR");
			supportedEncodings.push_back("XTERM_16COLOR");

			if (IsWindowsVersionOrGreater(10, 0, 14931))
			{
				// build 14931 added 24-bit color support: https://blogs.msdn.microsoft.com/commandline/2016/09/22/24-bit-color-in-the-windows-console/
				supportedEncodings.push_back("RGB_888COLOR");
			}

			bool matchFound;
			for (std::deque::iterator i = supportedEncodings.begin(); i != supportedEncodings.end(); ++i)
			{
				jobject expectedEnum = terminalEncoding(env, *i);
				if (env->IsSameObject(encoding, expectedEnum))
				{
					matchFound = true;
					break;
				}
			}
			if (!matchFound)
			{
				supportedEncodings.push_front("NONE");
				std::string message("Expected encoding to be one of [");
				for (std::deque::iterator i = supportedEncodings.begin(); i != supportedEncodings.end(); ++i)
				{
					message += *i;
					if (i != supportedEncodings.end() - 1)
						message += ", ";
				}
				message += "].\n";
				message += "Actual: ";
				message += toString(env, encoding);
				exceptions.throwIOException(message.c_str());
				return;
			}
			newStdoutMode = state.stdoutMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING;
		}
		if (state.currentMode != newStdoutMode)
		{
			if (!SetConsoleMode(state.stdoutHandle, newStdoutMode))
			{
				exceptions.throwIOException("Failed to set stdout mode", GetLastError());
				return;
			}
			state.currentMode = newStdoutMode;
		}
	}

	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.disconnect()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	void JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_disconnect
	(JNIEnv* env, jobject jthis)
	{
		if (!state.connected)
			return;
		if (state.currentMode != state.stdoutMode && !SetConsoleMode(state.stdoutHandle, state.stdoutMode))
		{
			DWORD lastError = GetLastError();
			Exceptions exceptions(env);
			exceptions.throwIOException("Failed to set stdout mode", lastError);
		}
		state.connected = false;
	}
#elif defined (__linux__) || defined (__APPLE__)
	#include 

	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.connect()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	void JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_connect
	(JNIEnv* env, jobject jthis)
	{
	}

	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.isConnectedToStdout()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	jboolean JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_isConnectedToStdout
	(JNIEnv* env, jobject jthis)
	{
		return isatty(STDOUT_FILENO);
	}

	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.setEncoding()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	void JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_setEncoding
	(JNIEnv* env, jobject jthis, jobject encoding)
	{
		Exceptions exceptions(env);
		jobject NONE = terminalEncoding(env, "NONE");
		if (env->IsSameObject(encoding, NONE))
			return;
		std::string message("Unexpected encoding: ");
		message += toString(env, encoding);
		exceptions.throwIOException(message.c_str());
	}

	/**
	 * org.bitbucket.cowwoc.requirements.internal.core.terminal.NativeTerminal.disconnect()
	 *
	 * @see https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx#Output_Sequences
	 */
	void JNICALL Java_org_bitbucket_cowwoc_requirements_internal_core_terminal_NativeTerminal_disconnect
	(JNIEnv* env, jobject jthis)
	{
	}
#endif // _WIN32




© 2015 - 2025 Weber Informatics LLC | Privacy Policy