Implemented SystemInfo for windows.

This commit is contained in:
Adam Murdoch
2012-09-09 17:07:38 +10:00
parent dd255be667
commit de4e340e2c
5 changed files with 784 additions and 721 deletions

View File

@@ -1,166 +1,166 @@
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'groovy' apply plugin: 'groovy'
apply plugin: 'cpp' apply plugin: 'cpp'
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'application' apply plugin: 'application'
apply plugin: 'maven' apply plugin: 'maven'
repositories { repositories {
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
groovy 'org.codehaus.groovy:groovy:1.8.7' groovy 'org.codehaus.groovy:groovy:1.8.7'
testCompile 'org.spockframework:spock-core:0.6-groovy-1.8' testCompile 'org.spockframework:spock-core:0.6-groovy-1.8'
} }
group = 'net.rubygrapefruit' group = 'net.rubygrapefruit'
version = '0.1' version = '0.1'
mainClassName = 'net.rubygrapefruit.platform.Main' mainClassName = 'net.rubygrapefruit.platform.Main'
def nativeHeadersDir = file("$buildDir/nativeHeaders") def nativeHeadersDir = file("$buildDir/nativeHeaders")
sourceCompatibility = 1.5 sourceCompatibility = 1.5
targetCompatibility = 1.5 targetCompatibility = 1.5
configurations.compile.extendsFrom = [] configurations.compile.extendsFrom = []
cpp { cpp {
sourceSets { sourceSets {
main main
} }
} }
libraries { libraries {
if (org.gradle.internal.os.OperatingSystem.current().macOsX) { if (org.gradle.internal.os.OperatingSystem.current().macOsX) {
universal.spec { universal.spec {
baseName = 'native-platform-osx-universal' baseName = 'native-platform-osx-universal'
includes(['/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/']) includes(['/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/'])
args("-lcurses", "-arch", "x86_64", "-arch", "i386", "-o", outputFile) args("-lcurses", "-arch", "x86_64", "-arch", "i386", "-o", outputFile)
} }
} else if (org.gradle.internal.os.OperatingSystem.current().windows) { } else if (org.gradle.internal.os.OperatingSystem.current().windows) {
main.spec { i386.spec {
baseName = 'native-platform-win32' baseName = 'native-platform-windows-i386'
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"])
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"])
args("/DWIN32") args("/DWIN32")
} }
} else if (org.gradle.internal.os.OperatingSystem.current().linux) { } else if (org.gradle.internal.os.OperatingSystem.current().linux) {
all { all {
spec { spec {
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"])
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"])
args("-lcurses") args("-lcurses")
} }
} }
if (System.getProperty('os.arch') == 'i386' || project.hasProperty('multiarch')) { if (System.getProperty('os.arch') == 'i386' || project.hasProperty('multiarch')) {
i386.spec { i386.spec {
baseName = 'native-platform-linux-i386' baseName = 'native-platform-linux-i386'
args("-m32") args("-m32")
} }
} }
if (System.getProperty('os.arch') == 'amd64' || project.hasProperty('multiarch')) { if (System.getProperty('os.arch') == 'amd64' || project.hasProperty('multiarch')) {
amd64.spec { amd64.spec {
baseName = 'native-platform-linux-amd64' baseName = 'native-platform-linux-amd64'
args("-m64") args("-m64")
} }
} }
} else { } else {
baseName = "native-platform-solaris" baseName = "native-platform-solaris"
main.spec { main.spec {
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"])
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/solaris"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/solaris"])
args("-DSOLARIS", "-lcurses") args("-DSOLARIS", "-lcurses")
} }
} }
all { all {
spec { spec {
includes([nativeHeadersDir]) includes([nativeHeadersDir])
} }
sourceSets << cpp.sourceSets.main sourceSets << cpp.sourceSets.main
} }
} }
task nativeHeaders { task nativeHeaders {
def outputFile = file("$nativeHeadersDir/native.h") def outputFile = file("$nativeHeadersDir/native.h")
inputs.files sourceSets.main.output inputs.files sourceSets.main.output
outputs.file outputFile outputs.file outputFile
doLast { doLast {
outputFile.parentFile.mkdirs() outputFile.parentFile.mkdirs()
exec { exec {
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah') executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
args '-o', outputFile args '-o', outputFile
args '-classpath', sourceSets.main.output.classesDir args '-classpath', sourceSets.main.output.classesDir
args 'net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions' args 'net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixFileFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixFileFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixProcessFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixProcessFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions'
args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions' args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions'
args 'net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions' args 'net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions'
} }
} }
} }
def deployer = null def deployer = null
uploadArchives { uploadArchives {
repositories { repositories {
deployer = mavenDeployer { deployer = mavenDeployer {
repository(url: uri("$buildDir/repo")) repository(url: uri("$buildDir/repo"))
} }
} }
} }
libraries.all { lib -> libraries.all { lib ->
def nativeJar = task("nativeJar${lib.name.capitalize()}", type: Jar) { def nativeJar = task("nativeJar${lib.name.capitalize()}", type: Jar) {
from lib.spec.task from lib.spec.task
baseName = lib.spec.baseName baseName = lib.spec.baseName
} }
startScripts.classpath += nativeJar.outputs.files startScripts.classpath += nativeJar.outputs.files
applicationDistribution.from(nativeJar) { applicationDistribution.from(nativeJar) {
into 'lib' into 'lib'
} }
lib.spec.task.dependsOn nativeHeaders lib.spec.task.dependsOn nativeHeaders
test.dependsOn lib.spec.task test.dependsOn lib.spec.task
artifacts { artifacts {
archives nativeJar archives nativeJar
} }
def jniPom = deployer.addFilter(lib.name) { artifact, file -> def jniPom = deployer.addFilter(lib.name) { artifact, file ->
return file == nativeJar.archivePath return file == nativeJar.archivePath
} }
jniPom.groupId = project.group jniPom.groupId = project.group
jniPom.artifactId = lib.spec.baseName jniPom.artifactId = lib.spec.baseName
jniPom.version = project.version jniPom.version = project.version
jniPom.scopeMappings.mappings.clear() jniPom.scopeMappings.mappings.clear()
} }
javadoc { javadoc {
exclude '**/internal/**' exclude '**/internal/**'
} }
applicationDistribution.from(javadoc) { applicationDistribution.from(javadoc) {
into 'javadoc' into 'javadoc'
} }
artifacts { artifacts {
archives distZip archives distZip
} }
def mainPom = deployer.addFilter('main') { artifact, file -> def mainPom = deployer.addFilter('main') { artifact, file ->
return file == distZip.archivePath || file == jar.archivePath return file == distZip.archivePath || file == jar.archivePath
} }
mainPom.groupId = project.group mainPom.groupId = project.group
mainPom.artifactId = jar.baseName mainPom.artifactId = jar.baseName
mainPom.version = project.version mainPom.version = project.version
mainPom.scopeMappings.mappings.clear() mainPom.scopeMappings.mappings.clear()
mainPom.withXml { provider -> mainPom.withXml { provider ->
def node = provider.asNode() def node = provider.asNode()
def deps = node.appendNode('dependencies') def deps = node.appendNode('dependencies')
def dep = deps.appendNode('dependency') def dep = deps.appendNode('dependency')
dep.appendNode('groupId', project.group) dep.appendNode('groupId', project.group)
dep.appendNode('artifactId', 'native-platform-osx-universal') dep.appendNode('artifactId', 'native-platform-osx-universal')
dep.appendNode('version', project.version) dep.appendNode('version', project.version)
} }
task wrapper(type: Wrapper) { task wrapper(type: Wrapper) {
gradleVersion = "1.3-20120907220018+0000" gradleVersion = "1.3-20120907220018+0000"
} }

296
readme.md
View File

@@ -1,149 +1,147 @@
# Native-platform: Java bindings for various native APIs # Native-platform: Java bindings for various native APIs
A collection of cross-platform Java APIs for various native APIs. Supports OS X, Linux, Solaris and Windows. A collection of cross-platform Java APIs for various native APIs. Supports OS X, Linux, Solaris and Windows.
These APIs support Java 5 and later. Some of these APIs overlap with APIs available in later Java versions. These APIs support Java 5 and later. Some of these APIs overlap with APIs available in later Java versions.
## Available bindings ## Available bindings
### Generic ### Generic
* Get PID of current process. * Get PID of current process.
* Get kernel name and version. * Get kernel name and version.
* Get machine architecture. * Get machine architecture.
### Terminal and console ### Terminal and console
These bindings work for both the UNIX terminal and the Windows console: These bindings work for both the UNIX terminal and the Windows console:
* Determine if stdout/stderr are attached to a terminal. * Determine if stdout/stderr are attached to a terminal.
* Query the terminal size. * Query the terminal size.
* Switch between bold and normal mode on the terminal. * Switch between bold and normal mode on the terminal.
* Change foreground color on the terminal. * Change foreground color on the terminal.
* Move terminal cursor up, down, left, right, start of line. * Move terminal cursor up, down, left, right, start of line.
* Clear to end of line. * Clear to end of line.
### File systems ### File systems
* Get and set UNIX file mode. * Get and set UNIX file mode.
* Create and read symbolic links. * Create and read symbolic links.
* List the available file systems on the machine * List the available file systems on the machine
* Query file system mount point. * Query file system mount point.
* Query file system type. * Query file system type.
* Query file system device name. * Query file system device name.
* Query whether a file system is local or remote. * Query whether a file system is local or remote.
## Supported platforms ## Supported platforms
Currently ported to OS X, Linux, Solaris and Windows. Tested on: Currently ported to OS X, Linux, Solaris and Windows. Tested on:
* OS X 10.7.4, 10.8 (x86_64), 10.6.7 (i386) * OS X 10.7.4, 10.8 (x86_64), 10.6.7 (i386)
* Ubunutu 12.04 (amd64), 8.04.4 (i386, amd64) * Ubunutu 12.04 (amd64), 8.04.4 (i386, amd64)
* Solaris 11 (x86) * Solaris 11 (x86)
* Windows 7 (amd64) * Windows 7 (amd64)
## Using ## Using
Include `native-platform.jar` and `native-platform-jni.jar` in your classpath. Include `native-platform.jar` and `native-platform-jni.jar` in your classpath.
import net.rubygrapefruit.platform.Native; import net.rubygrapefruit.platform.Native;
import net.rubygrapefruit.platform.Terminals; import net.rubygrapefruit.platform.Terminals;
import net.rubygrapefruit.platform.Terminal; import net.rubygrapefruit.platform.Terminal;
import static net.rubygrapefruit.platform.Terminals.Output.*; import static net.rubygrapefruit.platform.Terminals.Output.*;
Terminals terminals = Native.get(Terminals.class); Terminals terminals = Native.get(Terminals.class);
// check if terminal // check if terminal
terminals.isTerminal(Stdout); terminals.isTerminal(Stdout);
// use terminal // use terminal
Terminal stdout = terminals.getTerminal(Stdout); Terminal stdout = terminals.getTerminal(Stdout);
stdout.bold(); stdout.bold();
System.out.println("bold text"); System.out.println("bold text");
## Building ## Building
You will need to use the Gradle wrapper. Just run `gradlew` in the root directory. You will need to use the Gradle wrapper. Just run `gradlew` in the root directory.
### Ubuntu ### Ubuntu
The g++ compiler is required to build the native library. You will need the `g++` package for this. Usually this is already installed. The g++ compiler is required to build the native library. You will need the `g++` package for this. Usually this is already installed.
You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too. You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too.
#### 64-bit machines with multi-arch support #### 64-bit machines with multi-arch support
Where multi-arch support is available (e.g. recent Ubuntu releases), you can build the i386 and amd64 versions of the library on the Where multi-arch support is available (e.g. recent Ubuntu releases), you can build the i386 and amd64 versions of the library on the
same machine. same machine.
You need to install the `gcc-multilib` and `g++-multilib` packages to pick up i386 support. You need to install the `gcc-multilib` and `g++-multilib` packages to pick up i386 support.
You need to install the `lib32ncurses5-dev` package to pick up the ncurses i386 version. You need to install the `lib32ncurses5-dev` package to pick up the ncurses i386 version.
To build, include `-Pmultiarch` on the command-line. To build, include `-Pmultiarch` on the command-line.
### Windows ### Windows
You need to install Visual studio, and build from a Visual studio command prompt. You need to install Visual studio, and build from a Visual studio command prompt.
### OS X ### OS X
The g++ compiler is required to build the native library. You will need to install the XCode tools for this. The g++ compiler is required to build the native library. You will need to install the XCode tools for this.
### Solaris ### Solaris
For Solaris 11, you need to install the `development/gcc-45` and `system/header` packages. For Solaris 11, you need to install the `development/gcc-45` and `system/header` packages.
## Running ## Running
Run `gradle install` to install into `build/install/native-platform`. Or `gradle distZip` to create an application distribtion Run `gradle install` to install into `build/install/native-platform`. Or `gradle distZip` to create an application distribtion
in `build/distributions/native-platform.zip`. in `build/distributions/native-platform.zip`.
You can run `$INSTALL_DIR/bin/native-platform` to run the test application. You can run `$INSTALL_DIR/bin/native-platform` to run the test application.
## Testing ## Testing
* Test on IBM JVM. * Test on IBM JVM.
* Test on Java 5, 6, 7. * Test on Java 5, 6, 7.
* Test on Windows 7, Windows XP * Test on Windows 7, Windows XP
## TODO ## TODO
### Fixes ### Fixes
* Windows: build 32 bit and 64 bit libraries. * Windows: build 32 bit and 64 bit libraries.
* Windows: fail for unsupported architecture. * Windows: fail for unsupported architecture.
* Linux: detect remote filesystems. * Linux: detect remote filesystems.
* Solaris: fix unicode file name handling. * Solaris: fix unicode file name handling.
* Solaris: fail for unsupported architecture. * Solaris: fail for unsupported architecture.
* Solaris: build 32 bit and 64 bit libraries. * Solaris: build 32 bit and 64 bit libraries.
* Freebsd: finish port. * Freebsd: finish port.
* Freebsd: fail for unsupported architecture. * Freebsd: fail for unsupported architecture.
* Freebsd: build 32 bit and 64 bit libraries. * Freebsd: build 32 bit and 64 bit libraries.
### Improvements ### Improvements
* Cache class, method and field lookups (in particular for String conversions). * Cache class, method and field lookups (in particular for String conversions).
* Determine C charset once at startup * Determine C charset once at startup
* Change readLink() implementation so that it does not need to NULL terminate the encoded content * Change readLink() implementation so that it does not need to NULL terminate the encoded content
* Implement java_to_char_str() * Don't use NewStringUTF() anywhere
* Use iconv() to convert from C char string to UTF-16 when converting from C char string to Java String. * Use iconv() to convert from C char string to UTF-16 when converting from C char string to Java String.
* Support for cygwin terminal * Support for cygwin terminal
* Use TERM=xtermc instead of TERM=xterm on Solaris. * Use TERM=xtermc instead of TERM=xterm on Solaris.
* Add diagnostics for terminal. * Add diagnostics for terminal.
* Split out separate native library for terminal handling. * Split out separate native library for terminal handling.
* Version each native interface separately. * Version each native interface separately.
* String names for errno values. * String names for errno values.
* Split into multiple projects. * Split into multiple projects.
* Convert to c. * Convert to c.
* Make native library extraction multi-process safe. * Make native library extraction multi-process safe.
* Initial release. * Initial release.
* Use fully decomposed form for unicode file names on hfs+ filesystems. * Use fully decomposed form for unicode file names on hfs+ filesystems.
* Handle string encoding for file system details.
* Handle string encoding for system info. ### Ideas
### Ideas * Expose platform-specific HTTP proxy configuration. Query registry on windows to determine IE settings.
* Expose platform-specific HTTP proxy configuration. Query registry on windows to determine IE settings.

View File

@@ -1,240 +1,266 @@
#ifdef WIN32 #ifdef WIN32
#include "native.h" #include "native.h"
#include "generic.h" #include "generic.h"
#include <windows.h> #include <windows.h>
/* /*
* Marks the given result as failed, using the current value of GetLastError() * Marks the given result as failed, using the current value of GetLastError()
*/ */
void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) { void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
mark_failed_with_code(env, message, GetLastError(), result); mark_failed_with_code(env, message, GetLastError(), result);
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getPlatform(JNIEnv *env, jclass target) { Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) {
} jclass infoClass = env->GetObjectClass(info);
/* OSVERSIONINFOEX versionInfo;
* Process functions versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
*/ if (GetVersionEx((OSVERSIONINFO*)&versionInfo) == 0) {
mark_failed_with_errno(env, "could not get version info", result);
JNIEXPORT jint JNICALL return;
Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) { }
return GetCurrentProcessId();
} SYSTEM_INFO systemInfo;
GetNativeSystemInfo(&systemInfo);
/* jstring arch = NULL;
* Console functions if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
*/ arch = env->NewStringUTF("amd64");
} else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) {
HANDLE getHandle(JNIEnv *env, int output, jobject result) { arch = env->NewStringUTF("x86");
HANDLE handle = output == 1 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE); } else if (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) {
if (handle == INVALID_HANDLE_VALUE) { arch = env->NewStringUTF("ia64");
mark_failed_with_errno(env, "could not get console handle", result); } else {
return NULL; arch = env->NewStringUTF("unknown");
} }
return handle;
} jmethodID method = env->GetMethodID(infoClass, "windows", "(IIIZLjava/lang/String;)V");
env->CallVoidMethod(info, method, versionInfo.dwMajorVersion, versionInfo.dwMinorVersion,
JNIEXPORT jboolean JNICALL versionInfo.dwBuildNumber, versionInfo.wProductType == VER_NT_WORKSTATION,
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_isConsole(JNIEnv *env, jclass target, jint output, jobject result) { arch);
CONSOLE_SCREEN_BUFFER_INFO console_info; }
HANDLE handle = getHandle(env, output, result);
if (handle == NULL) { /*
return JNI_FALSE; * Process functions
} */
if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
if (GetLastError() == ERROR_INVALID_HANDLE) { JNIEXPORT jint JNICALL
return JNI_FALSE; Java_net_rubygrapefruit_platform_internal_jni_PosixProcessFunctions_getPid(JNIEnv *env, jclass target) {
} return GetCurrentProcessId();
mark_failed_with_errno(env, "could not get console buffer", result); }
return JNI_FALSE;
} /*
return JNI_TRUE; * Console functions
} */
JNIEXPORT void JNICALL HANDLE getHandle(JNIEnv *env, int output, jobject result) {
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) { HANDLE handle = output == 1 ? GetStdHandle(STD_OUTPUT_HANDLE) : GetStdHandle(STD_ERROR_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO console_info; if (handle == INVALID_HANDLE_VALUE) {
HANDLE handle = getHandle(env, output, result); mark_failed_with_errno(env, "could not get console handle", result);
if (handle == NULL) { return NULL;
mark_failed_with_message(env, "not a console", result); }
return; return handle;
} }
if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result); JNIEXPORT jboolean JNICALL
return; 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);
jclass dimensionClass = env->GetObjectClass(dimension); if (handle == NULL) {
jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I"); return JNI_FALSE;
env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1); }
jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I"); if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1); if (GetLastError() == ERROR_INVALID_HANDLE) {
} return JNI_FALSE;
}
HANDLE current_console = NULL; mark_failed_with_errno(env, "could not get console buffer", result);
WORD original_attributes = 0; return JNI_FALSE;
WORD current_attributes = 0; }
return JNI_TRUE;
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; JNIEXPORT void JNICALL
HANDLE handle = getHandle(env, output, result); Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_getConsoleSize(JNIEnv *env, jclass target, jint output, jobject dimension, jobject result) {
if (handle == NULL) { CONSOLE_SCREEN_BUFFER_INFO console_info;
mark_failed_with_message(env, "not a terminal", result); HANDLE handle = getHandle(env, output, result);
return; if (handle == NULL) {
} mark_failed_with_message(env, "not a console", result);
if (!GetConsoleScreenBufferInfo(handle, &console_info)) { return;
if (GetLastError() == ERROR_INVALID_HANDLE) { }
mark_failed_with_message(env, "not a console", result); if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
} else { mark_failed_with_errno(env, "could not get console buffer", result);
mark_failed_with_errno(env, "could not get console buffer", result); return;
} }
return;
} jclass dimensionClass = env->GetObjectClass(dimension);
current_console = handle; jfieldID widthField = env->GetFieldID(dimensionClass, "cols", "I");
original_attributes = console_info.wAttributes; env->SetIntField(dimension, widthField, console_info.srWindow.Right - console_info.srWindow.Left + 1);
current_attributes = original_attributes; jfieldID heightField = env->GetFieldID(dimensionClass, "rows", "I");
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(env, target, result); env->SetIntField(dimension, heightField, console_info.srWindow.Bottom - console_info.srWindow.Top + 1);
} }
JNIEXPORT void JNICALL HANDLE current_console = NULL;
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) { WORD original_attributes = 0;
current_attributes |= FOREGROUND_INTENSITY; WORD current_attributes = 0;
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_initConsole(JNIEnv *env, jclass target, jint output, jobject result) {
} CONSOLE_SCREEN_BUFFER_INFO console_info;
HANDLE handle = getHandle(env, output, result);
JNIEXPORT void JNICALL if (handle == NULL) {
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(JNIEnv *env, jclass target, jobject result) { mark_failed_with_message(env, "not a terminal", result);
current_attributes &= ~FOREGROUND_INTENSITY; return;
SetConsoleTextAttribute(current_console, current_attributes); }
if (!SetConsoleTextAttribute(current_console, current_attributes)) { if (!GetConsoleScreenBufferInfo(handle, &console_info)) {
mark_failed_with_errno(env, "could not set text attributes", result); 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);
JNIEXPORT void JNICALL }
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) { return;
SetConsoleTextAttribute(current_console, original_attributes); }
if (!SetConsoleTextAttribute(current_console, current_attributes)) { current_console = handle;
mark_failed_with_errno(env, "could not set text attributes", result); original_attributes = console_info.wAttributes;
} current_attributes = original_attributes;
} Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(env, target, result);
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_foreground(JNIEnv *env, jclass target, jint color, jobject result) { JNIEXPORT void JNICALL
current_attributes &= ~ (FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN); Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_bold(JNIEnv *env, jclass target, jobject result) {
switch (color) { current_attributes |= FOREGROUND_INTENSITY;
case 0: if (!SetConsoleTextAttribute(current_console, current_attributes)) {
break; mark_failed_with_errno(env, "could not set text attributes", result);
case 1: }
current_attributes |= FOREGROUND_RED; }
break;
case 2: JNIEXPORT void JNICALL
current_attributes |= FOREGROUND_GREEN; Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_normal(JNIEnv *env, jclass target, jobject result) {
break; current_attributes &= ~FOREGROUND_INTENSITY;
case 3: SetConsoleTextAttribute(current_console, current_attributes);
current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN; if (!SetConsoleTextAttribute(current_console, current_attributes)) {
break; mark_failed_with_errno(env, "could not set text attributes", result);
case 4: }
current_attributes |= FOREGROUND_BLUE; }
break;
case 5: JNIEXPORT void JNICALL
current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE; Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_reset(JNIEnv *env, jclass target, jobject result) {
break; SetConsoleTextAttribute(current_console, original_attributes);
case 6: if (!SetConsoleTextAttribute(current_console, current_attributes)) {
current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE; mark_failed_with_errno(env, "could not set text attributes", result);
break; }
default: }
current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
break; 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);
SetConsoleTextAttribute(current_console, current_attributes); switch (color) {
if (!SetConsoleTextAttribute(current_console, current_attributes)) { case 0:
mark_failed_with_errno(env, "could not set text attributes", result); break;
} case 1:
} current_attributes |= FOREGROUND_RED;
break;
JNIEXPORT void JNICALL case 2:
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) { current_attributes |= FOREGROUND_GREEN;
CONSOLE_SCREEN_BUFFER_INFO console_info; break;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { case 3:
mark_failed_with_errno(env, "could not get console buffer", result); current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN;
return; break;
} case 4:
console_info.dwCursorPosition.X -= count; current_attributes |= FOREGROUND_BLUE;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { break;
mark_failed_with_errno(env, "could not set cursor position", result); case 5:
} current_attributes |= FOREGROUND_RED|FOREGROUND_BLUE;
} break;
case 6:
JNIEXPORT void JNICALL current_attributes |= FOREGROUND_GREEN|FOREGROUND_BLUE;
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) { break;
CONSOLE_SCREEN_BUFFER_INFO console_info; default:
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { current_attributes |= FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE;
mark_failed_with_errno(env, "could not get console buffer", result); break;
return; }
}
console_info.dwCursorPosition.X += count; SetConsoleTextAttribute(current_console, current_attributes);
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { if (!SetConsoleTextAttribute(current_console, current_attributes)) {
mark_failed_with_errno(env, "could not set cursor position", result); mark_failed_with_errno(env, "could not set text attributes", result);
} }
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_left(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info; CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result); mark_failed_with_errno(env, "could not get console buffer", result);
return; return;
} }
console_info.dwCursorPosition.Y -= count; console_info.dwCursorPosition.X -= count;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result); mark_failed_with_errno(env, "could not set cursor position", result);
} }
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_right(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info; CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result); mark_failed_with_errno(env, "could not get console buffer", result);
return; return;
} }
console_info.dwCursorPosition.Y += count; console_info.dwCursorPosition.X += count;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result); mark_failed_with_errno(env, "could not set cursor position", result);
} }
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_startLine(JNIEnv *env, jclass target, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_up(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info; CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result); mark_failed_with_errno(env, "could not get console buffer", result);
return; return;
} }
console_info.dwCursorPosition.X = 0; console_info.dwCursorPosition.Y -= count;
if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) { if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
mark_failed_with_errno(env, "could not set cursor position", result); mark_failed_with_errno(env, "could not set cursor position", result);
} }
} }
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_clearToEndOfLine(JNIEnv *env, jclass target, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_WindowsConsoleFunctions_down(JNIEnv *env, jclass target, jint count, jobject result) {
CONSOLE_SCREEN_BUFFER_INFO console_info; CONSOLE_SCREEN_BUFFER_INFO console_info;
if (!GetConsoleScreenBufferInfo(current_console, &console_info)) { if (!GetConsoleScreenBufferInfo(current_console, &console_info)) {
mark_failed_with_errno(env, "could not get console buffer", result); mark_failed_with_errno(env, "could not get console buffer", result);
return; return;
} }
for (int i = console_info.dwCursorPosition.X; i < console_info.dwSize.X; i++) { console_info.dwCursorPosition.Y += count;
WriteConsole(current_console, " ", 1, NULL, NULL); if (!SetConsoleCursorPosition(current_console, console_info.dwCursorPosition)) {
} mark_failed_with_errno(env, "could not set cursor position", result);
} }
}
#endif
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_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;
}
for (int i = console_info.dwCursorPosition.X; i < console_info.dwSize.X; i++) {
WriteConsole(current_console, " ", 1, NULL, NULL);
}
}
#endif

View File

@@ -1,21 +1,53 @@
package net.rubygrapefruit.platform.internal; package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.SystemInfo; import net.rubygrapefruit.platform.SystemInfo;
public class MutableSystemInfo implements SystemInfo { public class MutableSystemInfo implements SystemInfo {
public String osName; public String osName;
public String osVersion; public String osVersion;
public String machineArchitecture; public String machineArchitecture;
public String getKernelName() { public String getKernelName() {
return osName; return osName;
} }
public String getKernelVersion() { public String getKernelVersion() {
return osVersion; return osVersion;
} }
public String getMachineArchitecture() { public String getMachineArchitecture() {
return machineArchitecture; return machineArchitecture;
} }
}
void windows(int major, int minor, int build, boolean workstation, String arch) {
osName = toWindowsVersionName(major, minor, workstation);
osVersion = String.format("%s.%s (build %s)", major, minor, build);
machineArchitecture = arch;
}
private String toWindowsVersionName(int major, int minor, boolean workstation) {
switch (major) {
case 5:
switch (minor) {
case 0:
return "Windows 2000";
case 1:
return "Windows XP";
case 2:
return "Windows Server 2003";
}
break;
case 6:
switch (minor) {
case 0:
return workstation ? "Windows Vista" : "Windows Server 2008";
case 1:
return workstation ? "Windows 7" : "Windows Server 2008 R2";
case 2:
return workstation ? "Windows 8" : "Windows Server 2012";
}
break;
}
return "Windows (unknown version)";
}
}

View File

@@ -1,145 +1,152 @@
package net.rubygrapefruit.platform.internal; package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.*; import net.rubygrapefruit.platform.*;
import net.rubygrapefruit.platform.Process; import net.rubygrapefruit.platform.Process;
public abstract class Platform { public abstract class Platform {
private static Platform platform; private static Platform platform;
public static Platform current() { public static Platform current() {
synchronized (Platform.class) { synchronized (Platform.class) {
if (platform == null) { if (platform == null) {
String osName = getOperatingSystem().toLowerCase(); String osName = getOperatingSystem().toLowerCase();
String arch = getArchitecture(); String arch = getArchitecture();
if (osName.contains("windows")) { if (osName.contains("windows")) {
platform = new Windows(); platform = new Windows();
} else if (osName.contains("linux")) { } else if (osName.contains("linux")) {
platform = new Linux(); platform = new Linux();
} else if (osName.contains("os x") && (arch.equals("i386") || arch.equals("x86_64"))) { } else if (osName.contains("os x") && (arch.equals("i386") || arch.equals("x86_64"))) {
platform = new OsX(); platform = new OsX();
} else if (osName.contains("sunos")) { } else if (osName.contains("sunos")) {
platform = new Solaris(); platform = new Solaris();
} else { } else {
platform = new Unsupported(); platform = new Unsupported();
} }
} }
return platform; return platform;
} }
} }
public boolean isWindows() { public boolean isWindows() {
return false; return false;
} }
public <T extends NativeIntegration> T get(Class<T> type) { public <T extends NativeIntegration> T get(Class<T> type) {
throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported on this operating system (%s %s)", throw new NativeIntegrationUnavailableException(String.format("Native integration %s is not supported on this operating system (%s %s)",
type.getSimpleName(), getOperatingSystem(), getArchitecture())); type.getSimpleName(), getOperatingSystem(), getArchitecture()));
} }
public abstract String getLibraryName() throws NativeIntegrationUnavailableException; public abstract String getLibraryName() throws NativeIntegrationUnavailableException;
private static class Windows extends Platform { private static class Windows extends Platform {
@Override @Override
public boolean isWindows() { public boolean isWindows() {
return true; return true;
} }
@Override @Override
public String getLibraryName() { public String getLibraryName() {
return "native-platform-win32.dll"; if (getArchitecture().equals("x86")) {
} return "native-platform-windows-i386.dll";
}
@Override throw new NativeIntegrationUnavailableException(String.format(
public <T extends NativeIntegration> T get(Class<T> type) { "Native integration is not available for this architecture (%s) on Windows.", getArchitecture()));
if (type.equals(net.rubygrapefruit.platform.Process.class)) { }
return type.cast(new DefaultProcess());
} @Override
if (type.equals(Terminals.class)) { public <T extends NativeIntegration> T get(Class<T> type) {
return type.cast(new WindowsTerminals()); if (type.equals(net.rubygrapefruit.platform.Process.class)) {
} return type.cast(new DefaultProcess());
return super.get(type); }
} if (type.equals(Terminals.class)) {
} return type.cast(new WindowsTerminals());
}
private static abstract class Posix extends Platform { if (type.equals(SystemInfo.class)) {
@Override return type.cast(new DefaultSystemInfo());
public <T extends NativeIntegration> T get(Class<T> type) { }
if (type.equals(PosixFile.class)) { return super.get(type);
return type.cast(new DefaultPosixFile()); }
} }
if (type.equals(Process.class)) {
return type.cast(new DefaultProcess()); private static abstract class Posix extends Platform {
} @Override
if (type.equals(Terminals.class)) { public <T extends NativeIntegration> T get(Class<T> type) {
return type.cast(new TerminfoTerminals()); if (type.equals(PosixFile.class)) {
} return type.cast(new DefaultPosixFile());
if (type.equals(SystemInfo.class)) { }
return type.cast(new DefaultSystemInfo()); if (type.equals(Process.class)) {
} return type.cast(new DefaultProcess());
return super.get(type); }
} if (type.equals(Terminals.class)) {
} return type.cast(new TerminfoTerminals());
}
private abstract static class Unix extends Posix { if (type.equals(SystemInfo.class)) {
} return type.cast(new DefaultSystemInfo());
}
private static class Linux extends Unix { return super.get(type);
@Override }
public <T extends NativeIntegration> T get(Class<T> type) { }
if (type.equals(FileSystems.class)) {
return type.cast(new PosixFileSystems()); private abstract static class Unix extends Posix {
} }
return super.get(type);
} private static class Linux extends Unix {
@Override
@Override public <T extends NativeIntegration> T get(Class<T> type) {
public String getLibraryName() { if (type.equals(FileSystems.class)) {
if (getArchitecture().equals("amd64")) { return type.cast(new PosixFileSystems());
return "libnative-platform-linux-amd64.so"; }
} return super.get(type);
if (getArchitecture().equals("i386") || getArchitecture().equals("x86")) { }
return "libnative-platform-linux-i386.so";
} @Override
throw new NativeIntegrationUnavailableException(String.format( public String getLibraryName() {
"Native integration is not available for this architecture (%s) on Linux.", getArchitecture())); if (getArchitecture().equals("amd64")) {
} return "libnative-platform-linux-amd64.so";
} }
if (getArchitecture().equals("i386") || getArchitecture().equals("x86")) {
private static String getArchitecture() { return "libnative-platform-linux-i386.so";
return System.getProperty("os.arch"); }
} throw new NativeIntegrationUnavailableException(String.format(
"Native integration is not available for this architecture (%s) on Linux.", getArchitecture()));
private static class Solaris extends Unix { }
@Override }
public String getLibraryName() {
return "libnative-platform-solaris.so"; private static String getArchitecture() {
} return System.getProperty("os.arch");
} }
private static class OsX extends Posix { private static class Solaris extends Unix {
@Override @Override
public <T extends NativeIntegration> T get(Class<T> type) { public String getLibraryName() {
if (type.equals(FileSystems.class)) { return "libnative-platform-solaris.so";
return type.cast(new PosixFileSystems()); }
} }
return super.get(type);
} private static class OsX extends Posix {
@Override
@Override public <T extends NativeIntegration> T get(Class<T> type) {
public String getLibraryName() { if (type.equals(FileSystems.class)) {
return "libnative-platform-osx-universal.dylib"; return type.cast(new PosixFileSystems());
} }
} return super.get(type);
}
private static class Unsupported extends Platform {
public String getLibraryName() { @Override
throw new NativeIntegrationUnavailableException(String.format( public String getLibraryName() {
"Native integration is not available for this operating system (%s %s)", getOperatingSystem(), return "libnative-platform-osx-universal.dylib";
getArchitecture())); }
} }
}
private static class Unsupported extends Platform {
private static String getOperatingSystem() { public String getLibraryName() {
return System.getProperty("os.name"); throw new NativeIntegrationUnavailableException(String.format(
} "Native integration is not available for this operating system (%s %s)", getOperatingSystem(),
} getArchitecture()));
}
}
private static String getOperatingSystem() {
return System.getProperty("os.name");
}
}