0a1,97 > /* > ** 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. > */ > 45a143,145 > #include > #include > #include 51d150 < static void my_addch_with_clip(int chr_); 55,62d153 < // Here are the boundaries of the clipping rectangle. < // Note that "right" refers to the righmost column INCLUDED in the rectangle < // and "bottom" refers to the bottom row that is INCLUDED in the rectangle. < static int left, top, right, bottom; < < // This is a local copy of the cursor position, used for clipping. < static int cursorx=0, cursory=0; < 65a157,165 > // 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; 69a170,179 > void throwException(JNIEnv* env, char* name, char* msg) > { > jclass jcls = (*env)->FindClass(env, name); > > if (jcls != NULL) > { > (*env)->ThrowNew(env, jcls, msg); > } > } > 167,168c277 < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_init < (JNIEnv *env, jobject jo) --- > JNIEXPORT void JNICALL Java_charva_awt_Toolkit_init(JNIEnv *env, jobject jo) 170,178c279,311 < char *strcap; < int i; < < initscr(); < keypad(stdscr, TRUE); // enable keyboard mapping < timeout(100); // wait up to 100msec for input < noecho(); /* don't echo input */ < raw(); < nonl(); --- > 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) 180c313 < hascolors = has_colors(); --- > hascolors = has_colors(); 183,191c316,324 < /* 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); --- > /* 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); 194c327,378 < atexit((void (*)(void))endwin); --- > 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()); > } 196c380,385 < Java_charva_awt_Toolkit_resetClipRect(env, jo); --- > // 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()); 215c404 < // Java_charva_awt_Toolkit_resetClipRect(env, jo); --- > // Java_charva_awt_Toolkit_resetClipRectNative(env, jo); 222c411 < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_sync --- > JNIEXPORT void JNICALL Java_charva_awt_Toolkit_syncNative 225c414 < refresh(); --- > refresh(); 248c437,459 < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_setCursor --- > 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 250a462 > int x, y; 251a464,468 > > // Set current cursor position > getyx(stdscr, y, x); > > return y == y_ && x == x_; 254,257c471,472 < /* Draw a string of text, taking into account the clipping rectangle. < */ < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addString < (JNIEnv *env, jobject jo, jstring jstr, jint attrib, jint colorpair) --- > JNIEXPORT jboolean JNICALL Java_charva_awt_Toolkit_setCursorStatus > (JNIEnv *env, jobject jo, jboolean on) 259,266c474,500 < const jchar *chrs; < jboolean isCopy; < jsize stringlength; < int i; < int attr = attrib; < < if (colors_started) < attr |= COLOR_PAIR(colorpair); --- > 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; > } 268,269c502,509 < chrs = (*env)->GetStringChars(env, jstr, &isCopy); < stringlength = (*env)->GetStringLength(env, jstr); --- > /* > * 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; 271,283c511,532 < if (cursory < top || cursory > bottom) { < cursorx += stringlength; < } < else { < for (i=0; i= left && cursorx <= right) < my_addch(chrs[i] | attr); < else { < cursorx++; < move(cursory, cursorx); < } < } < } --- > 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); > } 285c534,535 < (*env)->ReleaseStringChars(env, jstr, chrs); --- > (*env)->DeleteLocalRef(env, jstr); > free(chrs); 288c538 < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addChar --- > JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addCharNative 296c546,587 < my_addch_with_clip(chr_ | attr); --- > 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); 300c591,592 < (JNIEnv *env, jobject jo, jint left_, jint top_, jint right_, jint bottom_, jint colorpair_) --- > (JNIEnv *env, jobject jo, jint left_, jint top_, jint right_, jint bottom_, > jint attrib, jint colorpair_) 303c595 < int attr = 0; --- > int attr = attrib; 308,312c600,601 < // If the top of the box is outside the clipping rectangle, don't bother < // to draw the top. < if (top_ >= top && top_ <= bottom) { < my_move(top_, left_); < my_addch_with_clip(ACS_ULCORNER | attr); // upper left corner --- > my_move(top_, left_); > my_addch(ACS_ULCORNER | attr); // upper left corner 314,318c603,604 < for (i=left_+1; i for (i=left_+1; i my_addch(ACS_HLINE | attr); // top horizontal line 321,324c607 < // If the bottom of the box is outside the clipping rectangle, don't bother < if (bottom_ >= top && bottom_ <= bottom) { < my_move(bottom_, left_); < my_addch_with_clip(ACS_LLCORNER | attr); // lower left corner --- > my_addch(ACS_URCORNER | attr); // upper right corner 326,327c609,610 < for (i=left_+1; i my_move(bottom_, left_); > my_addch(ACS_LLCORNER | attr); // lower left corner 329,331c612,613 < my_move(bottom_, right_); < my_addch_with_clip(ACS_LRCORNER | attr); // lower right corner < } --- > for (i=left_+1; i my_addch(ACS_HLINE | attr); // bottom horizontal line 333,339c615,617 < // If the left side of the box is outside the clipping rectangle, don't < // bother. < if (left_ >= left && left_ <= right) { < for (i=top_+1; i for (i=top_+1; i my_move(i, left_); > my_addch(ACS_VLINE | attr); // left vertical line 341,348c619,622 < // < // If the right side of the box is outside the clipping rectangle, don't < // bother. < if (right_ >= left && right_ <= right) { < for (i=top_+1; i > for (i=top_+1; i my_move(i, right_); > my_addch(ACS_VLINE | attr); // right vertical line 353c627,628 < (JNIEnv *env, jobject jo, jint left_, jint top_, jint right_, jint bottom_, jint colorpair_) --- > (JNIEnv *env, jobject jo, jint left_, jint top_, jint right_, jint bottom_, > jint attrib, jint colorpair_) 356c631 < int attr = 0; --- > int attr = attrib; 362,363d636 < if (row < top || row > bottom) < continue; // do some clipping 367c640 < my_addch_with_clip(' ' | attr); --- > my_addch(' ' | attr); 406c679 < JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getScreenColumns --- > JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getScreenColumnsNative 409,411c682 < int x, y; < getmaxyx(stdscr, y, x); < return (jint) x; --- > return (jint) base_cols; 416c687 < JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getScreenRows --- > JNIEXPORT jint JNICALL Java_charva_awt_Toolkit_getScreenRowsNative 419,421c690 < int x, y; < getmaxyx(stdscr, y, x); < return (jint) y; --- > return (jint) base_rows; 426c695 < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addVerticalLine --- > JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addVerticalLineNative 436,437c705 < x = cursorx; < y = cursory; --- > getyx(stdscr, y, x); 441c709 < my_addch_with_clip(ACS_VLINE | attr); --- > my_addch(ACS_VLINE | attr); 447c715 < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addHorizontalLine --- > JNIEXPORT void JNICALL Java_charva_awt_Toolkit_addHorizontalLineNative 457,458c725 < x = cursorx; < y = cursory; --- > getyx(stdscr, y, x); 462c729 < my_addch_with_clip(ACS_HLINE | attr); --- > my_addch(ACS_HLINE | attr); 488,512d754 < /* Set a clipping rectangle < */ < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_setClipRectNative < (JNIEnv *env, jobject jo, jint left_, jint top_, jint right_, jint bottom_) < { < left = left_; < top = top_; < right = right_; < bottom = bottom_; < } < < /* Reset the clipping rectangle to the screen size. < * Unfortunately this does not work reliably on xterm windows where the window < * size can change at runtime. The ncurses library appears not to update the < * value of the window reliably. < */ < JNIEXPORT void JNICALL Java_charva_awt_Toolkit_resetClipRect < (JNIEnv *env, jobject jo) < { < left = top = 0; < getmaxyx(stdscr, bottom, right); < bottom--; < right--; < } < 566a809 > 728,732c971 < /* This calls the standard curses "addch" function but also updates the < * local copy of the cursor position, so that the clipping works correctly. < * It is called by functions that can clip more efficiently by doing their < * own clipping (such as addString, which knows when it starts whether the < * row is above, inside or below the clipping rectangle). --- > /* This calls the standard curses "addch" function. 737d975 < cursorx++; 740,741c978 < /* This calls the standard curses "move" function but also updates a local < * copy of the cursor position so that clipping works correctly. --- > /* This calls the standard curses "move" function. 746,768d982 < < /* Keep track of the cursor position so that we can use the clipping < * rectangle effectively. < */ < cursorx = x_; < cursory = y_; < } < < /* Write a character to the virtual screen if the cursor position is within < * the clipping rectangle, and (whether the character was clipped or not) < * updates the cursor position. < */ < static void my_addch_with_clip(int chr_) < { < if (cursory >= top && cursory <= bottom && < cursorx >= left && cursorx <= right) { < < my_addch(chr_); < } < else { < cursorx++; < move(cursory, cursorx); < }