// Copyright 2014-2025 Jesper Larsson
//
// This file is part of Klipspringer, <https://klipspringer.avadeaux.net/>
//
// Klipspringer is free software: you can redistribute it and/or modify it under the terms of the
// GNU General Public License as published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// Klipspringer 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
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with Klipspringer. If
// not, see <https://www.gnu.org/licenses/>.
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <sys/errno.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include "Library.h"
#include "net_avadeaux_klipspringer_Terminal.h" // generated by javac -h
#define SIG_INTERRUPT_GETCHAR SIGHUP
#define METHOD(name) JNICALL Java_net_avadeaux_klipspringer_Terminal_ ## name
static struct termios orig_termios;
static struct sigaction orig_sigact;
static pthread_t getchar_thread;
static bool getchar_active = false, getchar_interrupted;
static pthread_mutex_t getchar_mutex = PTHREAD_MUTEX_INITIALIZER;
JNIEXPORT jboolean
METHOD(inTty) (JNIEnv *env,
jclass jthisClass)
{
return isatty(STDIN_FILENO);
}
JNIEXPORT jboolean
METHOD(outTty) (JNIEnv *env,
jclass jthisClass)
{
return isatty(STDOUT_FILENO);
}
JNIEXPORT jboolean
METHOD(errTty) (JNIEnv *env,
jclass jthisClass)
{
return isatty(STDERR_FILENO);
}
JNIEXPORT void
METHOD(setupNonCanonical) (JNIEnv *env,
jclass jthisClass)
{
struct termios ios;
if (tcgetattr(STDIN_FILENO, &ios) < 0) {
raiseErrorCodeException(env, errno, strerror(errno));
return;
}
orig_termios = ios;
ios.c_lflag &= ~ICANON & ~ECHO;
ios.c_cc[VMIN] = 1;
ios.c_cc[VTIME] = 0;
if (tcsetattr(STDIN_FILENO, TCSANOW, &ios) < 0) {
raiseErrorCodeException(env, errno, strerror(errno));
}
}
JNIEXPORT void
METHOD(cancelNonCanonical) (JNIEnv *env,
jclass jthisClass)
{
if (tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios) < 0) {
raiseErrorCodeException(env, errno, strerror(errno));
}
}
static int lock(JNIEnv *env, pthread_mutex_t *mutex) {
int err = pthread_mutex_lock(mutex);
if (err == 0) { return 0; } // lock ok
raiseErrorCodeException(env, err, strerror(err));
return -1;
}
static int unlock(JNIEnv *env, pthread_mutex_t *mutex) {
int err = pthread_mutex_unlock(mutex);
if (err == 0) { return 0; } // unlock ok
raiseErrorCodeException(env, err, strerror(err));
return -1;
}
static void handle_sig(int signum) {
int err = pthread_mutex_lock(&getchar_mutex);
if (err) {
fprintf(stderr, "failed locking mutex in signal handler: %s\n", strerror(err));
exit(1);
}
getchar_interrupted = true;
err = pthread_mutex_unlock(&getchar_mutex);
if (err) {
fprintf(stderr, "failed unlocking mutex in signal handler: %s\n", strerror(err));
exit(1);
}
}
JNIEXPORT void
METHOD(setupSigaction) (JNIEnv *env,
jclass jthisClass)
{
struct sigaction sh;
sh.sa_handler = handle_sig;
sigemptyset(&sh.sa_mask);
sh.sa_flags = 0;
if (sigaction(SIG_INTERRUPT_GETCHAR, &sh, &orig_sigact) < 0) {
raiseErrorCodeException(env, errno, strerror(errno));
}
}
JNIEXPORT void
METHOD(cancelSigaction) (JNIEnv *env,
jclass jthisClass)
{
if (sigaction(SIG_INTERRUPT_GETCHAR, &orig_sigact, NULL) < 0) {
raiseErrorCodeException(env, errno, strerror(errno));
}
}
JNIEXPORT jint
METHOD(getChar) (JNIEnv *env,
jclass jthisClass)
{
pthread_t me = pthread_self();
bool success = false;
if (lock(env, &getchar_mutex) < 0) { return -1; }
if (!getchar_active) {
getchar_thread = me;
getchar_active = true;
getchar_interrupted = false;
success = true;
}
if (unlock(env, &getchar_mutex) < 0) { return -1; }
if (!success) {
raiseIOException(env, "getChar already in progress");
return -1;
}
int ch = getchar();
if (lock(env, &getchar_mutex) < 0) { return -1; }
getchar_active = false;
bool interrupted = getchar_interrupted; // happened while waiting for mutex?
if (unlock(env, &getchar_mutex) < 0 || interrupted) { return -1; }
return ch;
}
JNIEXPORT void
METHOD(sigThread) (JNIEnv *env,
jclass jthisClass)
{
pthread_t it;
bool success = false;
if (lock(env, &getchar_mutex) < 0) { return; }
success = getchar_active;
it = getchar_thread;
if (unlock(env, &getchar_mutex) < 0 || !success) { return; }
int err = pthread_kill(it, SIG_INTERRUPT_GETCHAR);
if (err) {
raiseErrorCodeException(env, err, strerror(err));
}
}
Version: v4.3.2.2 (2026-05-16T17:03:34+02:00)
Raw file
Source code overview
Klipspringer home