Skip to content

Commit

Permalink
feat: fall back to TTY when asking for input on Linux/Mac (#1312)
Browse files Browse the repository at this point in the history
This means that the GUI that was introduced a while back to be able to
ask the user for feedback when the stdin was redirected will no longer
be used on Linux and Mac, but only on Windows.
The main advantage of this is that zero-install using `curl` will
work when no GUI is available (eg when using containers).
  • Loading branch information
quintesse authored Mar 18, 2022
1 parent b259d74 commit d942ada
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 24 deletions.
69 changes: 53 additions & 16 deletions src/main/java/dev/jbang/util/ConsoleInput.java
Original file line number Diff line number Diff line change
@@ -1,41 +1,78 @@
package dev.jbang.util;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class ConsoleInput {
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.*;

public abstract class ConsoleInput {
private final int tries;
private final int timeout;
private final TimeUnit unit;

public ConsoleInput(int tries, int timeout, TimeUnit unit) {
/**
* Will either return a ConsoleInput that enables reading a line from the
* console (using stdin or a tty) or it will return <code>null</code> if no
* console is available.
*/
public static ConsoleInput get(int tries, int timeout, TimeUnit unit) {
if (Util.haveConsole()) {
return stdin(tries, timeout, unit);
} else if (!Util.isWindows()) {
return tty(tries, timeout, unit);
} else {
return null;
}
}

/**
* Returns a regular ConsoleInput based upon System.in
*/
public static ConsoleInput stdin(int tries, int timeout, TimeUnit unit) {
return new ConsoleInput(tries, timeout, unit) {
@Override
protected Callable<String> readerTask() {
return new ConsoleInputReadTask(System.in);
}
};
}

/**
* Returns a ConsoleInput based upon /dev/tty which only works on Linux and Mac.
*/
public static ConsoleInput tty(int tries, int timeout, TimeUnit unit) {
return new ConsoleInput(tries, timeout, unit) {
@Override
protected Callable<String> readerTask() throws IOException {
return new ConsoleInputReadTask(new FileInputStream("/dev/tty"));
}
};
}

private ConsoleInput(int tries, int timeout, TimeUnit unit) {
this.tries = tries;
this.timeout = timeout;
this.unit = unit;
}

protected abstract Callable<String> readerTask() throws IOException;

public String readLine() {
ExecutorService ex = Executors.newSingleThreadExecutor();
String input = null;
try {
// start working
for (int i = 0; i < tries; i++) {
// Util.infoMsg(String.valueOf(i + 1) + ". loop");
Future<String> result = ex.submit(
new ConsoleInputReadTask());
Future<String> result = null;
try {
result = ex.submit(readerTask());
input = result.get(timeout, unit);
break;
} catch (ExecutionException e) {
} catch (ExecutionException | IOException e) {
e.getCause().printStackTrace();
} catch (TimeoutException e) {
// Util.infoMsg("Cancelling reading task");
result.cancel(true);
// Util.infoMsg("\nThread cancelled. input is null");
if (result != null) {
result.cancel(true);
}
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
Expand Down
14 changes: 8 additions & 6 deletions src/main/java/dev/jbang/util/ConsoleInputReadTask.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,31 @@

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.concurrent.Callable;

public class ConsoleInputReadTask implements Callable<String> {
private final InputStream in;

public ConsoleInputReadTask(InputStream in) {
this.in = in;
}

public String call() throws IOException {
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in));
// System.out.println("ConsoleInputReadTask run() called.");
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String input;
do {
// System.out.println("Please type something: ");
try {
// wait until we have data to complete a readLine()
while (!br.ready()) {
Thread.sleep(200);
}
input = br.readLine();
} catch (InterruptedException e) {
// System.out.println("ConsoleInputReadTask() cancelled");
return null;
}
} while ("".equals(input));
// System.out.println("Thank You for providing input!");
return input;
}
}
8 changes: 6 additions & 2 deletions src/main/java/dev/jbang/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -1372,8 +1372,8 @@ public static boolean isBlankString(String str) {
}

public static int askInput(String message, int timeout, int defaultValue, String... options) {
if (!"true".equalsIgnoreCase(System.getenv(JBANG_STDIN_NOTTY))) {
ConsoleInput con = new ConsoleInput(1, timeout, TimeUnit.SECONDS);
ConsoleInput con = ConsoleInput.get(1, timeout, TimeUnit.SECONDS);
if (con != null) {
StringBuilder msg = new StringBuilder(message + "\n\n");
for (int i = 0; i < options.length; i++) {
msg.append("(").append(i + 1).append(") ").append(options[i]).append("\n");
Expand Down Expand Up @@ -1420,6 +1420,10 @@ public static int askInput(String message, int timeout, int defaultValue, String
return -1;
}

public static boolean haveConsole() {
return !"true".equalsIgnoreCase(System.getenv(JBANG_STDIN_NOTTY));
}

private static void setupApplicationIcon() {
try {
Class<?> clazz = Util.class.getClassLoader().loadClass("java.awt.Taskbar");
Expand Down

0 comments on commit d942ada

Please sign in to comment.