JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) { jclass infoClass = env->GetObjectClass(info); OSVERSIONINFOEX versionInfo; versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if (GetVersionEx((OSVERSIONINFO*)&versionInfo) == 0) { mark_failed_with_errno(env, "could not get version info", result); return; } SYSTEM_INFO systemInfo; GetNativeSystemInfo(&systemInfo); jstring arch = NULL; if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { arch = env->NewStringUTF("amd64"); } else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { arch = env->NewStringUTF("x86"); } else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { arch = env->NewStringUTF("ia64"); } else { arch = env->NewStringUTF("unknown"); } jmethodID method = env->GetMethodID(infoClass, "windows", "(IIIZLjava/lang/String;)V"); env->CallVoidMethod(info, method, versionInfo.dwMajorVersion, versionInfo.dwMinorVersion, versionInfo.dwBuildNumber, versionInfo.wProductType == VER_NT_WORKSTATION, arch); }
/* * File system functions */ JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) { int fs_count = getfsstat(NULL, 0, MNT_NOWAIT); if (fs_count < 0) { mark_failed_with_errno(env, "could not stat file systems", result); return; } size_t len = fs_count * sizeof(struct statfs); struct statfs* buf = (struct statfs*)malloc(len); if (getfsstat(buf, len, MNT_NOWAIT) < 0 ) { mark_failed_with_errno(env, "could not stat file systems", result); free(buf); return; } jclass info_class = env->GetObjectClass(info); jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZZZ)V"); for (int i = 0; i < fs_count; i++) { jboolean caseSensitive = JNI_TRUE; jboolean casePreserving = JNI_TRUE; jstring mount_point = char_to_java(env, buf[i].f_mntonname, result); jstring file_system_type = char_to_java(env, buf[i].f_fstypename, result); jstring device_name = char_to_java(env, buf[i].f_mntfromname, result); jboolean remote = (buf[i].f_flags & MNT_LOCAL) == 0; env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, remote, caseSensitive, casePreserving); } free(buf); }
JNIEXPORT jstring JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getEnvironmentVariable(JNIEnv *env, jclass target, jstring var, jobject result) { wchar_t* varStr = java_to_wchar(env, var, result); DWORD len = GetEnvironmentVariableW(varStr, NULL, 0); if (len == 0) { if (GetLastError() != ERROR_ENVVAR_NOT_FOUND) { mark_failed_with_errno(env, "could not determine length of environment variable", result); } free(varStr); return NULL; } wchar_t* valueStr = (wchar_t*)malloc(sizeof(wchar_t) * len); DWORD copied = GetEnvironmentVariableW(varStr, valueStr, len); if (copied == 0) { if (len > 1) { // If the value is empty, then copied will be 0 mark_failed_with_errno(env, "could not get environment variable", result); } free(varStr); free(valueStr); return NULL; } free(varStr); jstring value = wchar_to_java(env, valueStr, copied, result); free(valueStr); return value; }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) { current_attributes = original_attributes; if (!SetConsoleTextAttribute(current_console, current_attributes)) { mark_failed_with_errno(env, "could not set text attributes", result); } if (!SetConsoleCursorInfo(current_console, &original_cursor)) { mark_failed_with_errno(env, "could not set console cursor", result); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_clearToEndOfLine(JNIEnv *env, jclass target, jobject result) { CONSOLE_SCREEN_BUFFER_INFO console_info; if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { mark_failed_with_errno(env, "could not get console buffer", result); return; } DWORD count; if (!FillConsoleOutputCharacterW(current_console, L' ', console_info.dwSize.X - console_info.dwCursorPosition.X, console_info.dwCursorPosition, &count)) { mark_failed_with_errno(env, "could not clear console", result); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_startLine(JNIEnv *env, jclass target, jobject result) { CONSOLE_SCREEN_BUFFER_INFO console_info; if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { mark_failed_with_errno(env, "could not get console buffer", result); return; } console_info.dwCursorPosition.X = 0; if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { mark_failed_with_errno(env, "could not set cursor position", result); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_defaultForeground(JNIEnv *env, jclass target, jobject result) { current_attributes = (current_attributes & ~ALL_COLORS) | (original_attributes & ALL_COLORS); if (!SetConsoleTextAttribute(current_console, current_attributes)) { mark_failed_with_errno(env, "could not set text attributes", result); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_stat(JNIEnv *env, jclass target, jstring path, jobject dest, jobject result) { jclass destClass = env->GetObjectClass(dest); jmethodID mid = env->GetMethodID(destClass, "details", "(IJJ)V"); if (mid == NULL) { mark_failed_with_message(env, "could not find method", result); return; } WIN32_FILE_ATTRIBUTE_DATA attr; wchar_t* pathStr = java_to_wchar(env, path, result); BOOL ok = GetFileAttributesExW(pathStr, GetFileExInfoStandard, &attr); free(pathStr); if (!ok) { DWORD error = GetLastError(); if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND || error == ERROR_NOT_READY) { // Treat device with no media as missing env->CallVoidMethod(dest, mid, (jint)FILE_TYPE_MISSING, (jlong)0, (jlong)0); return; } mark_failed_with_errno(env, "could not file attributes", result); return; } jlong lastModified = lastModifiedNanos(&attr.ftLastWriteTime); if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { env->CallVoidMethod(dest, mid, (jint)FILE_TYPE_DIRECTORY, (jlong)0, lastModified); } else { jlong size = ((jlong)attr.nFileSizeHigh << 32) | attr.nFileSizeLow; env->CallVoidMethod(dest, mid, (jint)FILE_TYPE_FILE, size, lastModified); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) { current_attributes |= FOREGROUND_INTENSITY; if (!SetConsoleTextAttribute(current_console, current_attributes)) { mark_failed_with_errno(env, "could not set text attributes", result); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) { current_attributes &= ~ (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); switch (color) { case 0: break; case 1: current_attributes |= FOREGROUND_RED; break; case 2: current_attributes |= FOREGROUND_GREEN; break; case 3: current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN; break; case 4: current_attributes |= FOREGROUND_BLUE; break; case 5: current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE; break; case 6: current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE; break; default: current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; break; } SetConsoleTextAttribute(current_console, current_attributes); if (!SetConsoleTextAttribute(current_console, current_attributes)) { mark_failed_with_errno(env, "could not set text attributes", result); } }
void uninheritStream(JNIEnv *env, DWORD stdInputHandle, jobject result) { HANDLE streamHandle = GetStdHandle(stdInputHandle); if (streamHandle == NULL) { // We're not attached to a stdio (eg Desktop application). Ignore. return; } if (streamHandle == INVALID_HANDLE_VALUE) { mark_failed_with_errno(env, "could not get std handle", result); return; } boolean ok = SetHandleInformation(streamHandle, HANDLE_FLAG_INHERIT, 0); if (!ok) { if (GetLastError() != ERROR_INVALID_PARAMETER && GetLastError() != ERROR_INVALID_HANDLE) { mark_failed_with_errno(env, "could not change std handle", result); } } }
JNIEXPORT jboolean JNICALL Java_net_rubygrapefruit_platform_internal_jni_FileEventFunctions_waitForNextEvent(JNIEnv *env, jclass target, jobject handle, jobject result) { watch_details_t* details = (watch_details_t*)env->GetDirectBufferAddress(handle); if (WaitForSingleObject(details->watch_handle, INFINITE) == WAIT_FAILED) { mark_failed_with_errno(env, "could not wait for change notification", result); return JNI_FALSE; } if (!FindNextChangeNotification(details->watch_handle)) { if (GetLastError() == ERROR_INVALID_HANDLE) { // Assumed closed return JNI_FALSE; } mark_failed_with_errno(env, "could not schedule next change notification", result); return JNI_FALSE; } return JNI_TRUE; }
HANDLE getHandle(JNIEnv *env, int output, jobject result) { HANDLE handle = output == 1 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); if (handle == INVALID_HANDLE_VALUE) { mark_failed_with_errno(env, "could not get console handle", result); return NULL; } return handle; }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_rawInputMode(JNIEnv *env, jclass target, jobject result) { init_input(env, result); DWORD mode = original_mode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT); if (!SetConsoleMode(console_buffer, mode)) { mark_failed_with_errno(env, "could not set console buffer mode", result); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_detach(JNIEnv *env, jclass target, jobject result) { if (FreeConsole() == 0) { // Ignore if the error is that the process is already detached from the console if (GetLastError() != ERROR_INVALID_PARAMETER) { mark_failed_with_errno(env, "could not FreeConsole()", result); } } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_showCursor(JNIEnv *env, jclass target, jobject result) { CONSOLE_CURSOR_INFO cursor; cursor = original_cursor; cursor.bVisible = true; if (!SetConsoleCursorInfo(current_console, &cursor)) { mark_failed_with_errno(env, "could not show cursor", result); } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_resetInputMode(JNIEnv *env, jclass target, jobject result) { if (console_buffer == NULL) { return; } if (!SetConsoleMode(console_buffer, original_mode)) { mark_failed_with_errno(env, "could not set console buffer mode", result); } }
void init_input(JNIEnv *env, jobject result) { if (console_buffer == NULL) { console_buffer = GetStdHandle(STD_INPUT_HANDLE); if (!GetConsoleMode(console_buffer, &original_mode)) { mark_failed_with_errno(env, "could not get console buffer mode", result); return; } } }
JNIEXPORT jstring JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getWorkingDirectory(JNIEnv *env, jclass target, jobject result) { DWORD size = GetCurrentDirectoryW(0, NULL); if (size == 0) { mark_failed_with_errno(env, "could not determine length of working directory path", result); return NULL; } size = size+1; // Needs to include null character wchar_t* path = (wchar_t*)malloc(sizeof(wchar_t) * size); DWORD copied = GetCurrentDirectoryW(size, path); if (copied == 0) { free(path); mark_failed_with_errno(env, "could get working directory path", result); return NULL; } jstring dirName = wchar_to_java(env, path, copied, result); free(path); return dirName; }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_setWorkingDirectory(JNIEnv *env, jclass target, jstring dir, jobject result) { wchar_t* dirPath = java_to_wchar(env, dir, result); if (dirPath == NULL) { return; } BOOL ok = SetCurrentDirectoryW(dirPath); free(dirPath); if (!ok) { mark_failed_with_errno(env, "could not set current directory", result); return; } }
JNIEXPORT jobject JNICALL Java_net_rubygrapefruit_platform_internal_jni_FileEventFunctions_createWatch(JNIEnv *env, jclass target, jstring path, jobject result) { wchar_t* pathStr = java_to_wchar(env, path, result); HANDLE h = FindFirstChangeNotificationW(pathStr, FALSE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE); free(pathStr); if (h == INVALID_HANDLE_VALUE) { mark_failed_with_errno(env, "could not open change notification", result); return NULL; } watch_details_t* details = (watch_details_t*)malloc(sizeof(watch_details_t)); details->watch_handle = h; return env->NewDirectByteBuffer(details, sizeof(watch_details_t)); }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_setEnvironmentVariable(JNIEnv *env, jclass target, jstring var, jstring value, jobject result) { wchar_t* varStr = java_to_wchar(env, var, result); wchar_t* valueStr = value == NULL ? NULL : java_to_wchar(env, value, result); BOOL ok = SetEnvironmentVariableW(varStr, valueStr); free(varStr); if (valueStr != NULL) { free(valueStr); } if (!ok && GetLastError() != ERROR_ENVVAR_NOT_FOUND) { mark_failed_with_errno(env, "could not set environment var", result); return; } }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsFileFunctions_readdir(JNIEnv *env, jclass target, jstring path, jobject contents, jobject result) { jclass contentsClass = env->GetObjectClass(contents); jmethodID mid = env->GetMethodID(contentsClass, "addFile", "(Ljava/lang/String;IJJ)V"); if (mid == NULL) { mark_failed_with_message(env, "could not find method", result); return; } WIN32_FIND_DATAW entry; wchar_t* pathStr = java_to_wchar(env, path, result); HANDLE dirHandle = FindFirstFileW(pathStr, &entry); free(pathStr); if (dirHandle == INVALID_HANDLE_VALUE) { mark_failed_with_errno(env, "could not open directory", result); return; } do { if (wcscmp(L".", entry.cFileName) == 0 || wcscmp(L"..", entry.cFileName) == 0) { continue; } jstring childName = wchar_to_java(env, entry.cFileName, wcslen(entry.cFileName), result); jint type = (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? FILE_TYPE_DIRECTORY : FILE_TYPE_FILE; jlong lastModified = lastModifiedNanos(&entry.ftLastWriteTime); jlong size = ((jlong)entry.nFileSizeHigh << 32) | entry.nFileSizeLow; env->CallVoidMethod(contents, mid, childName, type, size, lastModified); } while (FindNextFileW(dirHandle, &entry) != 0); DWORD error = GetLastError(); if (error != ERROR_NO_MORE_FILES ) { mark_failed_with_errno(env, "could not read next directory entry", result); } FindClose(dirHandle); }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_initConsole(JNIEnv *env, jclass target, jint output, jobject result) { CONSOLE_SCREEN_BUFFER_INFO console_info; HANDLE handle = getHandle(env, output, result); if (handle == NULL) { mark_failed_with_message(env, "not a terminal", result); return; } if (!GetConsoleScreenBufferInfo(handle, &console_info)) { if (GetLastError() == ERROR_INVALID_HANDLE) { mark_failed_with_message(env, "not a console", result); } else { mark_failed_with_errno(env, "could not get console buffer", result); } return; } if (!GetConsoleCursorInfo(handle, &original_cursor)) { mark_failed_with_errno(env, "could not get console cursor", result); return; } current_console = handle; original_attributes = console_info.wAttributes; current_attributes = original_attributes; }
JNIEXPORT jboolean JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_isConsole(JNIEnv *env, jclass target, jint output, jobject result) { CONSOLE_SCREEN_BUFFER_INFO console_info; HANDLE handle = getHandle(env, output, result); if (handle == NULL) { return JNI_FALSE; } if (!GetConsoleScreenBufferInfo(handle, &console_info)) { if (GetLastError() == ERROR_INVALID_HANDLE) { return JNI_FALSE; } mark_failed_with_errno(env, "could not get console buffer", result); return JNI_FALSE; } return JNI_TRUE; }
HANDLE getHandle(JNIEnv *env, int output, jobject result) { HANDLE handle = INVALID_HANDLE_VALUE; switch (output) { case STDIN_DESCRIPTOR: handle = GetStdHandle(STD_INPUT_HANDLE); break; case STDOUT_DESCRIPTOR: handle = GetStdHandle(STD_OUTPUT_HANDLE); break; case STDERR_DESCRIPTOR: handle = GetStdHandle(STD_ERROR_HANDLE); break; } if (handle == INVALID_HANDLE_VALUE) { mark_failed_with_errno(env, "could not get console handle", result); return NULL; } return handle; }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) { CONSOLE_SCREEN_BUFFER_INFO console_info; HANDLE handle = getHandle(env, output, result); if (handle == NULL) { mark_failed_with_message(env, "not a console", result); return; } if (!GetConsoleScreenBufferInfo(handle, &console_info)) { mark_failed_with_errno(env, "could not get console buffer", result); return; } jclass dimensionClass = env->GetObjectClass(dimension); jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I"); env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1); jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I"); env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1); }
/* * File system functions */ JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) { FILE *fp = setmntent(MOUNTED, "r"); if (fp == NULL) { mark_failed_with_errno(env, "could not open mount file", result); return; } char buf[1024]; struct mntent mount_info; jclass info_class = env->GetObjectClass(info); jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V"); while (getmntent_r(fp, &mount_info, buf, sizeof(buf)) != NULL) { jstring mount_point = char_to_java(env, mount_info.mnt_dir, result); jstring file_system_type = char_to_java(env, mount_info.mnt_type, result); jstring device_name = char_to_java(env, mount_info.mnt_fsname, result); env->CallVoidMethod(info, method, mount_point, file_system_type, device_name, JNI_FALSE); } endmntent(fp); }
/* * File system functions */ JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_PosixFileSystemFunctions_listFileSystems(JNIEnv *env, jclass target, jobject info, jobject result) { wchar_t* volumeName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1)); jclass info_class = env->GetObjectClass(info); jmethodID method = env->GetMethodID(info_class, "add", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V"); HANDLE handle = FindFirstVolumeW(volumeName, MAX_PATH+1); if (handle == INVALID_HANDLE_VALUE) { free(volumeName); mark_failed_with_errno(env, "could not find first volume", result); return; } wchar_t* deviceName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1)); wchar_t* pathNames = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1)); wchar_t* fsName = (wchar_t*)malloc(sizeof(wchar_t) * (MAX_PATH+1)); while(true) { // Chop off the trailing '\' size_t len = wcslen(volumeName); if (len < 5) { mark_failed_with_message(env, "volume name is too short", result); break; } volumeName[len-1] = L'\0'; if (QueryDosDeviceW(&volumeName[4], deviceName, MAX_PATH+1) == 0) { mark_failed_with_errno(env, "could not query dos device", result); break; } volumeName[len-1] = L'\\'; DWORD used; if (GetVolumePathNamesForVolumeNameW(volumeName, pathNames, MAX_PATH+1, &used) == 0) { // TODO - try again if the buffer is too small mark_failed_with_errno(env, "could not query volume paths", result); break; } wchar_t* cur = pathNames; if (cur[0] != L'\0') { if(GetVolumeInformationW(cur, NULL, 0, NULL, NULL, NULL, fsName, MAX_PATH+1) == 0) { if (GetLastError() != ERROR_NOT_READY) { mark_failed_with_errno(env, "could not query volume information", result); break; } wcscpy(fsName, L"unknown"); } for (;cur[0] != L'\0'; cur += wcslen(cur) + 1) { env->CallVoidMethod(info, method, env->NewString((jchar*)deviceName, wcslen(deviceName)), env->NewString((jchar*)fsName, wcslen(fsName)), env->NewString((jchar*)cur, wcslen(cur)), JNI_FALSE); } } if (FindNextVolumeW(handle, volumeName, MAX_PATH) == 0) { if (GetLastError() != ERROR_NO_MORE_FILES) { mark_failed_with_errno(env, "could find next volume", result); } break; } } free(volumeName); free(deviceName); free(pathNames); free(fsName); FindVolumeClose(handle); }
JNIEXPORT void JNICALL Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_readInput(JNIEnv *env, jclass target, jobject char_buffer, jobject result) { init_input(env, result); INPUT_RECORD events[1]; DWORD nread; while(TRUE) { if (!ReadConsoleInputW(console_buffer, events, 1, &nread)) { mark_failed_with_errno(env, "could not read from console", result); return; } if (events[0].EventType != KEY_EVENT) { continue; } KEY_EVENT_RECORD keyEvent = events[0].Event.KeyEvent; if (!keyEvent.bKeyDown) { if (keyEvent.wVirtualKeyCode == 0x43 && keyEvent.uChar.UnicodeChar == 3) { // key down event for ctrl-c doesn't seem to be delivered, but key up event does return; } continue; } if ((keyEvent.dwControlKeyState & (LEFT_ALT_PRESSED|LEFT_CTRL_PRESSED|RIGHT_ALT_PRESSED|RIGHT_CTRL_PRESSED|SHIFT_PRESSED)) == 0) { if (keyEvent.wVirtualKeyCode == VK_RETURN) { control_key(env, 0, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_UP) { control_key(env, 1, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_DOWN) { control_key(env, 2, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_LEFT) { control_key(env, 3, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_RIGHT) { control_key(env, 4, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_HOME) { control_key(env, 5, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_END) { control_key(env, 6, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_BACK) { control_key(env, 7, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_DELETE) { control_key(env, 8, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_PRIOR) { // page up control_key(env, 10, char_buffer, result); return; } else if (keyEvent.wVirtualKeyCode == VK_NEXT) { // page down control_key(env, 11, char_buffer, result); return; } } if (keyEvent.wVirtualKeyCode == 0x44 && keyEvent.uChar.UnicodeChar == 4) { // ctrl-d return; } if (keyEvent.uChar.UnicodeChar == 0) { // Some other control key continue; } if (keyEvent.uChar.UnicodeChar == '\t' && (keyEvent.dwControlKeyState & (SHIFT_PRESSED)) == 0) { // shift-tab control_key(env, 9, char_buffer, result); } else { character(env, (jchar)keyEvent.uChar.UnicodeChar, char_buffer, result); } return; } }