/*
** Module   : Toolkit.c
** Abstract : ncurses library interface code
**
** Modifications Copyright (c) 2005-2009, Golden Code Development Corporation.
** The original LGPL license terms apply
**
** -#- -I- --Date-- --JPRM-- ----------------Description-----------------
** 001 NVS 20050915   @22725 First modification. Added two new functions
**                           suspend() and resume() to temporary suspend
**                           ncurses preserving the screen environment
**                           and restore it later.
** 002 NVS 20060118   @23962 drawBoxNative() function didn't have
**                           attribute parameter. Added.
** 003 NVS 20060118   @23970 blankBoxNative() function didn't have
**                           attribute parameter. Added.
** 004 NVS 20060124   @24047 Renamed addHorizontalLine and addString to
**                           addHorizontalLineNative and addStringNative
**                           respectively to allow addition of capture
**                           logic to Toolkit.java
** 005 GES 20060213   @24591 Added functions for pseudo-tty and process
**                           launching. Based on original work by EVL
**                           see his entry in @23680.
** 006 GES 20060217   @24651 Massive rework new features and for correct
**                           functioning of the redirected terminal
**                           output mode/terminal switching.                               
** 007 GES 20060222   @24652 Removed the redirected terminal support
**                           since ncurses cannot be made to properly
**                           handle all features needed.
** 008 GES 20060222   @24658 Changed some API names to match Java layer
**                           changes.
** 009 GES 20060322   @25183 Added terminal type control methods.
** 010 ECF 20060614   @27248 Changed keyboard timeout to 500 msec. This
**                           helps reduce flicker and wild cursor
**                           movements when repainting large screen areas
**                           because getch() causes NCURSES to sync() so
**                           reducing the number of times it is called
**                           causes sync() to be called less and thus
**                           there is less flashing.
** 011 GES 20060712   @28034 Process reaping fix by overriding SIGCHLD.
** 012 EVL 20060802   @28356 Adding implementation for the
**                           JNI setCursorStatus. The cursor is turned off
**                           when param is false and turned on when param
**                           is true. Returns previos state as boolean.
** 013 GES 20060817   @28657 Force the master and slave pty configurations
**                           to a true RAW mode.
** 014 NVS 20060821   @28702 curs_set failure should not be fatal.
** 015 GES 20060828   @28936 Disable sync during getch() to eliminate
**                           terminal corruption issues since ncurses is
**                           NOT thread safe. This change makes our 2
**                           thread approach (1 thread does getch() and
**                           all other ncurses processing is on another
**                           thread) safe enough to work reliably.
** 016 NVS 20070308   @32295 Reworked the process launch function to use
**                           fork() and pipe() calls instead of forkpty()
**                           and to leave unused stdio handles atatched to
**                           the parent's TTY.
** 017 NVS 20070309   @32354 Improved error handling in process launch
**                           method. Multiple negative error codes can be
**                           returned to report the precise reason.
** 018 NVS 20080212   @37026 Interactive processes are launched with some
**                           additional handling: the terminal gets
**                           cleared before the application, paused with
**                           the default "press space bar..." message
**                           when it's done, and stderr is redirected to
**                           stdout while it's running.
**                           The calling process is set up to ignore some
**                           job control related signals. This handling is
**                           inherited by child process as well.
** 019 GES 20080326   @37695 Modified the addStringNative method to take
**                           a byte[] instead of a string. This allows
**                           the peer code on the Java side to convert
**                           the UNICODE string into a properly encoded
**                           native byte array which can then be output
**                           here. This allows characters above the 7-bit
**                           ASCII range to be properly supported and is
**                           needed for I18N support.
** 020 CA  20081107   @40436 Removed all clipping code. Clipping is the 
**                           responsability of the charva Toolkit class.
** 021 NVS 20090209   @41275 Added new method addArrayNative() which does
**                           bulk output of the screenful of the data,
**                           where every character position is either a
**                           skip indicator (-1) or a fully prepared code
**                           with character, attribute and color included.
** 022 SIY 20090702   @43031 Read screen size once at startup. This prevents
**                           mismatch between Java and C parts if terminal 
**                           size changes at run time. Note that although this
**                           is desired behavior for our purposes, this might
**                           be inconvenient in general case. 
** 023 LMR 20101129          Moved code dealing with child process management
**                           (initializeFileDescriptor(), logic in init()
**                           to ignore signals,
**                           Java_charva_awt_Toolkit_pseudoTerminalLaunch(),
**                           Java_charva_awt_Toolkit_pseudoTerminalWait())
**                           to p2j/src/native/process.c.
*/

/* Toolkit.c
 * 
 * Copyright (C) 2001  R M Pitman
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * Modified Jul 14, 2003 by Tadpole Computer, Inc.
 * Modifications Copyright 2003 by Tadpole Computer, Inc.
 *
 * Modifications are hereby licensed to all parties at no charge under
 * the same terms as the original.
 *
 * Modifications include minor bug fixes, and moving the handling of
 * endwin() into an atexit(3) function call.
 */
#include "../include/charva_awt_Toolkit.h"
#if (defined _PDCURSES_)
    // Use the PDCurses version of the header file, which must be
    // in the current directory.
    #include "curses.h"
#elif (defined _USE_CURSES_)
    #include <curses.h>
    #include <term.h>
#else
    // Default is to use ncurses
    #include <ncurses.h>
#endif
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <termios.h>
#include <fcntl.h>

//************************************************************************
// LOCAL FUNCTION DECLARATIONS
static void my_move(int y_, int x_);
static void my_addch(int chr_);

//************************************************************************
// LOCAL VARIABLES
static int hascolors = 0;	// set when we initialize
static int colors_started = 0;	// set when we call start_color().

// pointer to the structure of the interactive terminal
static SCREEN* terminal = NULL;

// cache field IDs for performance
static jfieldID jfd    = NULL;

// terminal size at the moment of initialization of the library 
static int base_rows = 0;
static int base_cols = 0;

//************************************************************************
// FUNCTION DEFINITIONS

void throwException(JNIEnv* env, char* name, char* msg)
{
   jclass jcls = (*env)->FindClass(env, name);
   
   if (jcls != NULL)
   {
      (*env)->ThrowNew(env, jcls, msg);
   }
}

JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getAttribute
  (JNIEnv *env, jclass class, jint offset)
{
    switch (offset) {
	case 0:
	    return (jint) A_NORMAL;
	case 1:
	    return (jint) A_STANDOUT;
	case 2:
#if (defined _PDCURSES_)
	    // PDCurses doesn't handle UNDERLINE correctly; it
	    // just sets the color to blue.
	    return (jint) A_BOLD;
#else
	    return (jint) A_UNDERLINE;
#endif
	case 3:
	    return (jint) A_REVERSE;
	case 4:
	    return (jint) A_BLINK;
	case 5:
	    return (jint) A_DIM;
	case 6:
	    return (jint) A_BOLD;
	case 7:
	    return (jint) A_ALTCHARSET;
	case 8:
	    return (jint) A_INVIS;
    }
}

JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getACSchar
  (JNIEnv *env, jclass class, jint offset)
{
    switch (offset) {
	case 0:
	    return (jint) ACS_ULCORNER;
	case 1:
	    return (jint) ACS_LLCORNER;
	case 2:
	    return (jint) ACS_URCORNER;
	case 3:
	    return (jint) ACS_LRCORNER;
	case 4:
	    return (jint) ACS_LTEE;
	case 5:
	    return (jint) ACS_RTEE;
	case 6:
	    return (jint) ACS_BTEE;
	case 7:
	    return (jint) ACS_TTEE;
	case 8:
	    return (jint) ACS_HLINE;
	case 9:
	    return (jint) ACS_VLINE;
	case 10:
	    return (jint) ACS_PLUS;
	case 11:
	    return (jint) ACS_S1;
	case 12:
	    return (jint) ACS_S9;
	case 13:
	    return (jint) ACS_DIAMOND;
	case 14:
	    return (jint) ACS_CKBOARD;
	case 15:
	    return (jint) ACS_DEGREE;
	case 16:
	    return (jint) ACS_PLMINUS;
	case 17:
	    return (jint) ACS_BULLET;
    }
}

JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getColor
  (JNIEnv *env, jclass class, jint offset)
{
    switch (offset) {
	case 0:
	    return (jint) COLOR_BLACK;
	case 1:
	    return (jint) COLOR_RED;
	case 2:
	    return (jint) COLOR_GREEN;
	case 3:
	    return (jint) COLOR_YELLOW;
	case 4:
	    return (jint) COLOR_BLUE;
	case 5:
	    return (jint) COLOR_MAGENTA;
	case 6:
	    return (jint) COLOR_CYAN;
	case 7:
	    return (jint) COLOR_WHITE;
    }
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_init(JNIEnv *env, jobject jo)
{
   char *strcap;
   char *termname;
   int i;

   termname = getenv("TERM");
    
   if (termname == NULL || *termname == '\0')
   {
      termname = "vt100";
   }
   
   // use this instead of initscr because there is no way to query the
   // current terminal and when you use multiple terms, newterm overwrites
   // the current terminal... so you can never get it if you don't use
   // newterm directly
   terminal = newterm(termname, stdout, stdin);
    
   if (terminal == NULL)
   {
      char* spec   = "Interactive terminal of type %s cannot be initialized.";
      char* errmsg = malloc(strlen(spec) + strlen(termname) + 1); 
      sprintf(errmsg, spec, termname);
      throwException(env, "java/lang/IllegalStateException", errmsg);
      return;
   }
    
   keypad(stdscr, TRUE);      // enable keyboard mapping
   timeout(500);              // wait up to 500msec for input
   noecho();                  // don't echo input
   raw();
   nonl();
   auto_getch_refresh(FALSE); // disable sync during getch()
   def_prog_mode();           // save our state (after our mode changes)

   hascolors = has_colors();

#ifdef NCURSES_MOUSE_VERSION
   /* For some reason, if the mouse_interval is nonzero, the button-presses
    * are reported very erratically. So we disable click resolution by
    * setting mouse_interval to 0.
    */
   mousemask(BUTTON1_RELEASED | BUTTON1_PRESSED | 
	     BUTTON2_RELEASED | BUTTON2_PRESSED |
	     BUTTON3_RELEASED | BUTTON3_PRESSED, 
	     NULL);
   mouseinterval(0);
#endif

   getmaxyx(stdscr, base_rows, base_cols);

   atexit((void (*)(void))endwin);

}

// simple reset of terminal type
void restartTerminal(JNIEnv *env, char* termname)
{
   int   err  = 0;
   int   rc   = restartterm(termname, fileno(stdout), &err); 
   
   if (rc == ERR)
   {
      char* spec   = "Failure resetting terminal of type %s (error %i).";
      char* errmsg = malloc(strlen(spec) + strlen(termname) + 15); 
      sprintf(errmsg, spec, termname, err);
      throwException(env, "java/lang/IllegalStateException", errmsg);
   }
}

// set the NCURSES terminal type
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_setTermType(JNIEnv *env,
                                                           jclass  cls,
                                                           jstring name)
{
   char* termname = (char*) (*env)->GetStringUTFChars(env, name, NULL);
   
   if (termname == NULL)
   {
      // out of memory!
      exit(-1);
   }
   
   restartTerminal(env, strdup(termname));
   
   (*env)->ReleaseStringUTFChars(env, name, termname);
}

// returns the termname() or "" on any failure
char* safe_termname()
{
   char* term = termname();
   return (term == NULL) ? "" : term;
}

// return the NCURSES terminal type
JNIEXPORT jstring JNICALL Java_charva_awt_Toolkit_getTermType(JNIEnv *env,
                                                              jclass  cls)
{
   return (*env)->NewStringUTF(env, safe_termname());
}

// reset NCURSES state associated with the terminal type (without changing
// the type), this is effectively the same as setTermType(getTermType())
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_resetTermType(JNIEnv *env,
                                                             jclass  cls)
{
   restartTerminal(env, termname()); 
}

// Returns -1 if there was no character to read.
// Otherwise returns the character that was read.
JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_readKey
  (JNIEnv *env, jobject jo)
{
    jint c;	// defined as a long for Linux.

try_again:
    c = (jint) getch();

 // THIS HAS ALL CHANGED: getch() is now called from the event-dispatching thread!!! (November 2004)
    // There are two known scenarios in which getch() returns ERR.
    // 1. The user resizes his PuTTY window (in which case errno == 0).
    // 2. The user closes his Telnet/SSH session (errno == EINTR).
    //if (c == ERR && errno == 0) {
	//    // The window has been resized.
	//    Java_charva_awt_Toolkit_resetClipRectNative(env, jo);
	//    goto try_again;
    //}

    return c;
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_syncNative
  (JNIEnv *env, jobject jo)
{
    refresh();    
}

#ifndef _PDCURSES_
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_redrawWin
  (JNIEnv *env, jobject jo)
{
    redrawwin(stdscr);	
}
#endif

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_close
  (JNIEnv *env, jobject jo)
{
    endwin();
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_clear
  (JNIEnv *env, jobject jo)
{
    clear();
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_suspend
  (JNIEnv *env, jobject jo)
{
    def_prog_mode();            /* Save the tty modes             */
    endwin();                   /* End curses mode temporarily    */
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_resume
  (JNIEnv *env, jobject jo)
{
    reset_prog_mode();          /* Return to the previous tty mode*/
                                /* stored by def_prog_mode()      */
    doupdate();                 /* Do refresh() to restore the    */
                                /* Screen contents                */
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_resetMode
  (JNIEnv *env, jobject jo)
{
    reset_prog_mode();
}

JNIEXPORT jboolean JNICALL Java_charva_awt_Toolkit_setCursorNative
  (JNIEnv *env, jobject jo, jint x_, jint y_)
{
    int x, y;
    my_move(y_, x_);

    // Set current cursor position
    getyx(stdscr, y, x);
    
    return y == y_ && x == x_;
}

JNIEXPORT jboolean JNICALL Java_charva_awt_Toolkit_setCursorStatus
  (JNIEnv *env, jobject jo, jboolean on)
{
   int rc = ERR;
   jboolean ret;
   
   if( on )
      rc = curs_set( 1 );
   else
      rc = curs_set( 0 );
   
   if ( rc == 1 || rc == 2 )
      ret = true;
   else if ( rc == 0 )
      ret = false;
   else
   {
      char* spec;
      char* termname = getenv("TERM");
      if ( rc == ERR )
         return true;
      else
         spec = "Unexpected return from curs_set() for terminal of type %s (%d).";
      char* errmsg = malloc(strlen(spec) + strlen(termname) + 32); 
      sprintf(errmsg, spec, termname, rc);
      throwException(env, "java/lang/IllegalStateException", errmsg);
   }
   
   return ret;
}

/*
 * Draw a string of text.
 */
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addStringNative
  (JNIEnv *env, jobject jo, jbyteArray jstr, jint len, jint attrib, jint colorpair)
{
   int i;
   int attr = attrib;

   if (colors_started)
      attr |= COLOR_PAIR(colorpair);
   
   if (len <= 0)
      return;
   
   jbyte* chrs = (jbyte*) malloc(len * sizeof(jbyte));
   
   if (chrs == NULL)
   {
      throwException(env, "java/lang/OutOfMemoryError", "Cannot copy data.");
   }
   
   (*env)->GetByteArrayRegion(env, jstr, 0, len, chrs);
   
   for (i = 0; i < len; i++)
   {
      // remove the automatic sign extension (we want to treat this as
      // an unsigned 8-bit number)
      int extended = 0x000000FF & (int) chrs[i];
      my_addch(extended | attr);
   } 

   (*env)->DeleteLocalRef(env, jstr);
   free(chrs);
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addCharNative
  (JNIEnv *env, jobject jo, jint chr_, jint attrib_, jint colorpair_)
{
    int attr = attrib_;

    if (colors_started)
	attr |= COLOR_PAIR(colorpair_);

    my_addch(chr_ | attr);
}

/* This function outputs the screenful of data at once using an array of codes
   where every character position is either a skip indicator (-1) or a fully 
   prepared code with character, attribute and color included.
   The final step is the cursor positioning.
*/
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addArrayNative
  (JNIEnv *env, jobject jo, jintArray arr_, jint cx_, jint cy_)
{
   int i, j, chr;
   jboolean isCopy = JNI_FALSE;

   jint* arrp = (*env)->GetIntArrayElements(env, arr_, &isCopy);
   jint* arp  = arrp;

   if (arp == NULL)
   {
      return;
   }

   for (i = 0; i < base_rows; i++)
   {
      for (j = 0; j < base_cols; j++)
      {
         chr = *arp++;

         if (chr == -1)
         {
            continue;
         }

         my_move(i, j);
         my_addch(chr);
       }
   }

   my_move(cy_, cx_);
   refresh();

   (*env)->ReleaseIntArrayElements(env, arr_, arrp, JNI_ABORT);
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_drawBoxNative
  (JNIEnv *env, jobject jo, jint left_, jint top_, jint right_, jint bottom_,
   jint attrib, jint colorpair_)
{
    int i, j;
    int attr = attrib;

    if (colors_started)
	    attr = COLOR_PAIR(colorpair_);

    my_move(top_, left_);
    my_addch(ACS_ULCORNER | attr);    // upper left corner

    for (i=left_+1; i<right_; i++) {
        my_addch(ACS_HLINE | attr);   // top horizontal line
    }

    my_addch(ACS_URCORNER | attr);    // upper right corner

    my_move(bottom_, left_);
    my_addch(ACS_LLCORNER | attr);    // lower left corner

    for (i=left_+1; i<right_; i++)
        my_addch(ACS_HLINE | attr);   // bottom horizontal line

    for (i=top_+1; i<bottom_; i++) {
        my_move(i, left_);
        my_addch(ACS_VLINE | attr);   // left vertical line
    }

    for (i=top_+1; i<bottom_; i++) {
        my_move(i, right_);
        my_addch(ACS_VLINE | attr);   // right vertical line
    }
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_blankBoxNative
  (JNIEnv *env, jobject jo, jint left_, jint top_, jint right_, jint bottom_, 
   jint attrib, jint colorpair_)
{
    int row, col;
    int attr = attrib;

    if (colors_started)
	attr = COLOR_PAIR(colorpair_);

    for (row=top_; row<=bottom_; row++) {

	    my_move(row, left_);
	    for (col=left_; col<=right_; col++)
	        my_addch(' ' | attr);
    }
}

/*
 * Ring the terminal bell.
 */
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_beep
  (JNIEnv *env, jobject jo)
{
    beep();
}

/* Returns true if the terminal is capable of displaying colors.
 */
JNIEXPORT jboolean JNICALL Java_charva_awt_Toolkit_hasColors
  (JNIEnv *env, jobject jo)
{
    return has_colors();
}

/* Returns the number of color-pairs that the terminal supports
 * (provides an interface to ncurses COLOR_PAIRS global variable).
 */
JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getMaxColorPairs
  (JNIEnv *env, jobject jo)
{
    if (!colors_started) {
	    (*env)->ThrowNew(env, (*env)->FindClass(env,
		    "charva/awt/TerminfoCapabilityException"),
		    "startColors() not called yet");
    }
    return (jint) COLOR_PAIRS;
}

/* Return the number of columns that the screen can display.
* The ncurses does not return a reliable value for xterm windows,
* where the screen size can change at runtime.
 */
JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getScreenColumnsNative
  (JNIEnv *env, jobject jo)
{
    return (jint) base_cols;
}

/* Return the number of rows that the screen can display.
 */
JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getScreenRowsNative
  (JNIEnv *env, jobject jo)
{
    return (jint) base_rows;
}

/* Draw a vertical line starting at the current cursor position.
 */
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addVerticalLineNative
  (JNIEnv *env, jobject jo, jint length_, jint attrib_, jint colorpair_)
{
    int x, y, i;
    int attr = attrib_;

    if (colors_started)
	    attr |= COLOR_PAIR(colorpair_);

    // Set current cursor position
    getyx(stdscr, y, x);

    for (i=0; i<length_; i++) {
	    my_move(y+i, x);
	    my_addch(ACS_VLINE | attr);
    }
}

/* Draw a horizontal line starting at the current cursor position.
 */
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addHorizontalLineNative
  (JNIEnv *env, jobject jo, jint length_, jint attrib_, jint colorpair_)
{
    int x, y, i;
    int attr = attrib_;

    if (colors_started)
	attr |= COLOR_PAIR(colorpair_);

    // Set current cursor position
    getyx(stdscr, y, x);

    for (i=0; i<length_; i++) {
	    my_move(y, x+i);
	    my_addch(ACS_HLINE | attr);
    }
}

/* Get the y position of the cursor.
 */
JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_gety
  (JNIEnv *env, jobject jo)
{
    int x, y;

    getyx(stdscr, y, x);
    return y;
}

/* Get the x position of the cursor.
 */
JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getx
  (JNIEnv *env, jobject jo)
{
    int x, y;

    getyx(stdscr, y, x);
    return x;
}

#if ( ! defined _PDCURSES_)
JNIEXPORT jstring JNICALL Java_charva_awt_Toolkit_getStringCapability
  (JNIEnv *env, jobject jo, jstring capname_)
{
    const char* capCname;
    char *capValue;
    jstring ret;

    capCname = (*env)->GetStringUTFChars(env, capname_, NULL);
    capValue = tigetstr((char *) capCname);
    if (capValue == (char *) -1 || capValue == (char *) 0) {
	    (*env)->ThrowNew(env, (*env)->FindClass(env,
		    "charva/awt/TerminfoCapabilityException"), capCname);
    }
    else {
	    ret = (*env)->NewStringUTF(env, capValue);
    }

    (*env)->ReleaseStringUTFChars(env, capname_, capCname);
    return ret;
}


JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getNumericCapability
  (JNIEnv *env, jobject jo, jstring capname_)
{
    const char* capCname;
    jint capValue;

    capCname = (*env)->GetStringUTFChars(env, capname_, NULL);
    capValue = tigetnum((char *) capCname);
    if (capValue < 0) {
	    (*env)->ThrowNew(env, (*env)->FindClass(env,
		    "charva/awt/TerminfoCapabilityException"), capCname);
    }

    (*env)->ReleaseStringUTFChars(env, capname_, capCname);
    return capValue;
}

JNIEXPORT jboolean JNICALL Java_charva_awt_Toolkit_getBooleanCapability
  (JNIEnv *env, jobject jo, jstring capname_)
{
    const char* capCname;
    int capValue;
    jboolean ret;

    capCname = (*env)->GetStringUTFChars(env, capname_, NULL);
    capValue = tigetflag((char *) capCname);
    if (capValue == -1) {
	    (*env)->ThrowNew(env, (*env)->FindClass(env,
		    "charva/awt/TerminfoCapabilityException"), capCname);
    }
    else {
       
	    ret = (capValue != 0);
    }

    (*env)->ReleaseStringUTFChars(env, capname_, capCname);
    return ret;
}

/** An interface to the terminfo "putp()" function.
 */
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_putp
  (JNIEnv *env, jobject jo, jstring jstr_)
{
    const jchar *chrs;
    jboolean isCopy;
    jsize stringlength;
    char *cstring;
    int i;

    /* We don't make the assumption that all the characters are 7-bit ASCII
     * (otherwise we could have just used GetStringUTFChars).
     */
    chrs = (*env)->GetStringChars(env, jstr_, &isCopy);
    stringlength = (*env)->GetStringLength(env, jstr_);

    cstring = malloc(stringlength + 1);
    for (i=0; i<stringlength; i++) {
	    cstring[i] = (char) chrs[i];
    }
    cstring[i] = '\0';	    // terminate the C string

    putp(cstring);
    free(cstring);

    (*env)->ReleaseStringChars(env, jstr_, chrs);
}

#if ( ! defined _USE_CURSES_)
/* I am told that the ncurses function "mcprint" is not 
 * defined in curses.
 */
JNIEXPORT void JNICALL Java_charva_awt_Toolkit_print
  (JNIEnv *env, jobject jo_, jstring jstr_)
{
    const jchar *chrs;
    jboolean isCopy;
    jsize stringlength;
    char *cstring;
    int i, ret;

    /* We don't make the assumption that all the characters are 7-bit ASCII
     * (otherwise we could have just used GetStringUTFChars).
     */
    chrs = (*env)->GetStringChars(env, jstr_, &isCopy);
    stringlength = (*env)->GetStringLength(env, jstr_);

    cstring = malloc(stringlength + 1);
    for (i=0; i<stringlength; i++) {
	    cstring[i] = (char) chrs[i];
    }
    cstring[i] = '\0';	    // terminate the C string
    (*env)->ReleaseStringChars(env, jstr_, chrs);

    ret = mcprint(cstring, strlen(cstring));
    free(cstring);

    if (ret < 0) {
	    if (errno == ENODEV) {
	        (*env)->ThrowNew(env, (*env)->FindClass(env,
		        "charva/awt/TerminfoCapabilityException"),
		        "mc4 or mc5 capability not defined");
	    }
	    else if (errno == ENOMEM) {
	        (*env)->ThrowNew(env, (*env)->FindClass(env,
		        "java/lang/OutOfMemoryError"), "");
	    }
	    else {
	        (*env)->ThrowNew(env, (*env)->FindClass(env,
		        "java/lang/RuntimeException"),
		        strerror(errno));
	    }
    }
}

JNIEXPORT jobject JNICALL Java_charva_awt_Toolkit_getMouseEventInfo
  (JNIEnv *env, jobject jo)
{
    MEVENT event;
    jclass class_MouseEventInfo;
    jmethodID id_MouseEventInfo;
    jint button_state, x, y;
    jobject info;

    if (getmouse(&event) != OK) {
	    (*env)->ThrowNew(env, (*env)->FindClass(env,
		    "java/lang/RuntimeException"),
		    "ncurses getmouse() function returned error");
    }

    class_MouseEventInfo = (*env)->FindClass(env, "charva/awt/MouseEventInfo");
    id_MouseEventInfo = (*env)->GetMethodID(env, class_MouseEventInfo, "<init>", "(III)V");
    button_state = event.bstate;
    x = event.x;
    y = event.y;
    info = (*env)->NewObject(env,
	    class_MouseEventInfo, id_MouseEventInfo, button_state, x, y);
    return info;
}
#endif /* ( ! defined _USE_CURSES_) */
#endif /* (! defined _PDCURSES_) */


JNIEXPORT void JNICALL Java_charva_awt_Toolkit_startColors
  (JNIEnv *env, jobject jo)
{
    /* We checked whether the terminal had color capability when
     * we initialised it.
     */
    if (!hascolors) {
	    (*env)->ThrowNew(env, (*env)->FindClass(env,
		    "charva/awt/TerminfoCapabilityException"),
		    "color capability not defined");
    }
    start_color();
    colors_started = 1;
}

JNIEXPORT void JNICALL Java_charva_awt_Toolkit_initColorPair
  (JNIEnv *env, jobject jo, jint pair_, jint fgnd_, jint bgnd_)
{
    /* We checked whether the terminal had color capability when
     * we initialised it.
     */
    if (!colors_started) {
	    (*env)->ThrowNew(env, (*env)->FindClass(env,
		    "charva/awt/TerminfoCapabilityException"),
		    "startColors() not called yet");
    }

    init_pair(pair_, fgnd_, bgnd_);
}

#ifndef _PDCURSES_
/*************************************************************************
 * Get the tty name.
 */
JNIEXPORT jstring JNICALL Java_charva_awt_Toolkit_getTtyName
  (JNIEnv *env, jobject jo)
{
    char *ttyName=NULL;
    jstring ret;

    ttyName = ttyname(0);
    if (ttyName == NULL) {
	    ttyName = "not_a_tty";
    }

    ret = (*env)->NewStringUTF(env, ttyName);
    return ret;
}
#endif

/* This calls the standard curses "addch" function.
 */
static void my_addch(int chr_)
{
    addch(chr_);
}

/* This calls the standard curses "move" function.
 */
static void my_move(int y_, int x_)
{
    move(y_, x_);
}
