Java: Colorful Console using Java Native Interface (JNI)

Posted on May 19, 2009

1


Background

First time trying Java Native Interface although I know that it is there for years. The main objective is to make Java command line program to be able to put color attribute at Windows Console. After searching online, I found Real’s How To include the color attribute into Java. Basically, it makes a bridge between Java and Windows native dynamic libraries. It is similar to having C# codes call unmanaged libraries.

Introduction (optional to read)

Java is a programming language originally developed by Sun Microsystems and released in 1995 as a core component of Sun’s Java platform. The language derives much of its syntax from C and C++ but has a simpler object model and fewer low-level facilities. Java applications are typically compiled to bytecode which can run on any Java virtual machine (JVM) regardless of computer architecture. read more…

Software and Configuration Requirement

  • Microsoft Windows (XP SP2 – used in this experiment)
  • Java Development Kit (JDK6 – used in this experiment)
  • Java Environment Variable
  • Microsoft Visual Studio (2005 – used in this experiment)

Java Native Interface Step by Step

  1. Create a Java class as the foundation for the native C++ header file. Indicate the C++ function with native keyword. And here is the example:
    public class Console {
    
    public static final int FG_BLACK     = 0x0000;
    public static final int FG_BLUE      = 0x0001;
    public static final int FG_GREEN     = 0x0002;
    public static final int FG_RED       = 0x0004;
    public static final int FG_INTENSITY = 0x0008;
    public static final int FG_YELLOW    = FG_RED | FG_GREEN;
    public static final int FG_MAGENTA   = FG_RED | FG_BLUE;
    public static final int FG_CYAN      = FG_BLUE | FG_GREEN;
    public static final int FG_GREY      = FG_RED | FG_GREEN | FG_BLUE;
    public static final int FG_L_RED     = FG_RED | FG_INTENSITY;
    public static final int FG_L_GREEN   = FG_GREEN | FG_INTENSITY;
    public static final int FG_L_YELLOW  = FG_YELLOW | FG_INTENSITY;
    public static final int FG_L_BLUE    = FG_BLUE | FG_INTENSITY;
    public static final int FG_L_MAGENTA = FG_MAGENTA | FG_INTENSITY;
    public static final int FG_L_CYAN    = FG_CYAN | FG_INTENSITY;
    public static final int FG_WHITE     = FG_GREY | FG_INTENSITY;
    
    public static final int BG_BLACK     = 0x0000;
    public static final int BG_BLUE      = 0x0010;
    public static final int BG_GREEN     = 0x0020;
    public static final int BG_RED       = 0x0040;
    public static final int BG_INTENSITY = 0x0080;
    public static final int BG_YELLOW    = BG_RED | BG_GREEN;
    public static final int BG_MAGENTA   = BG_RED | BG_BLUE;
    public static final int BG_CYAN      = BG_BLUE | BG_GREEN;
    public static final int BG_GREY      = BG_RED | BG_GREEN | BG_BLUE;
    public static final int BG_L_RED     = BG_RED | BG_INTENSITY;
    public static final int BG_L_GREEN   = BG_GREEN | BG_INTENSITY;
    public static final int BG_L_YELLOW  = BG_YELLOW | BG_INTENSITY;
    public static final int BG_L_BLUE    = BG_BLUE | BG_INTENSITY;
    public static final int BG_L_MAGENTA = BG_MAGENTA | BG_INTENSITY;
    public static final int BG_L_CYAN    = BG_CYAN | BG_INTENSITY;
    public static final int BG_WHITE     = BG_GREY | BG_INTENSITY;
    
    /** identify console */
    public static String identify() {
    return System.getProperty("os.name").toLowerCase();
    }
    
    /** clear screen */
    public static native void cls();
    
    /** set cursor position */
    public static native void setCursorPos(int x, int y);
    
    /** save current colors */
    public static native void saveColors();
    
    /** restore saved colors */
    public static native void restoreColors();
    
    /** set color */
    public static native void setColors(int foreground, int background);
    
    static { System.loadLibrary("jConsoleControl"); }
    
    }
  2. Create the C++ header file using javac and javah command. For instance:
    javac Console.java <Return>
    javah Console <Return>

    When you define a package. For example:

    package org.xandertan.io;

    Then you should do the following javah command instead:

    javah org.xandertan.io.Console <Return>
  3. Create a new Win32 Console Application project in Visual Studio 2005. Choose DLL Application type in Application Settings.
  4. Include the C++ header file created earlier into the project. Implement all functions defined in the header file in the C++ source file. You can follow this example:
    #include "stdafx.h"
    #include <stdlib.h>
    #include "Console.h"
    
    int savedColors = -1;
    
    BOOL APIENTRY DllMain( HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved )
    { return TRUE; }
    
    JNIEXPORT void JNICALL Java_com_xiaotech_io_Console_cls
    (JNIEnv *env, jclass obj)
    { system ("cls"); }
    
    JNIEXPORT void JNICALL Java_com_xiaotech_io_Console_setCursorPos
    (JNIEnv *env, jclass obj, jint x, jint y)
    {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    COORD coordScreen;
    coordScreen.X = (short)x;
    coordScreen.Y = (short)y;
    SetConsoleCursorPosition(hConsole, coordScreen);
    }
    
    JNIEXPORT void JNICALL Java_com_xiaotech_io_Console_saveColors
    (JNIEnv *env, jclass obj)
    {
    CONSOLE_SCREEN_BUFFER_INFO ConsoleInfo;
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    GetConsoleScreenBufferInfo(hConsole, &ConsoleInfo);
    savedColors = ConsoleInfo.wAttributes;
    }
    
    JNIEXPORT void JNICALL Java_com_xiaotech_io_Console_restoreColors
    (JNIEnv *env, jclass obj)
    {
    if(savedColors == -1) return;
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, savedColors);
    }
    
    JNIEXPORT void JNICALL Java_com_xiaotech_io_Console_setColors
    (JNIEnv *env, jclass obj, jint foreground, jint background)
    {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, (WORD)(foreground + background));
    }
  5. Use the Release mode as Active Solution Configuration in Configuration Manager. Press this magic combination Ctrl+Shift+B to build the solution.
  6. Go to the release directory to pick up the freshly built dll file and then copy it into the Java workspace. When there is no package defined, you may put the dll in the same directory as the class file. But if there is a package defined, it has to be located in the root directory of the package.

And here is the simple snippet to use the Windows platform dependent dll file.

 class JavaConsole {

public static void main(String[] args) {

// clear the screen
Console.cls();

// set the cursor at line 10 column 20
Console.setCursorPos((short)20,(short)10);

System.out.print("Modified Real's HowTo");

// set the cursor at line 15 column 20
Console.setCursorPos((short)20,(short)15);

// keep the current colors
Console.saveColors();

// set the color as WHITE on BLUE
Console.setColors(Console.FG_WHITE,Console.BG_BLUE);
System.out.print("http://xandertan.spaces.live.com");
System.out.print("http://www.rgagnon.com");

// restore the orginal colors
Console.restoreColors();

// set the cursor at line 20 column 0
Console.setCursorPos((short)0,(short)20);

}

}
Advertisements
Tagged: ,
Posted in: Technology