mabe02 / lanterna

Java library for creating text-based GUIs
GNU Lesser General Public License v3.0
2.23k stars 243 forks source link

Headless mode not working in Macos Terminal : "/dev/tty/ (Device not configured)" #504

Open rob3rtb opened 3 years ago

rob3rtb commented 3 years ago

I have tried launching examples from package com.googlecode.lanterna.examples in a Macos Terminal, but the following error occurs:

Exception in thread "main" java.io.IOException: Cannot run program "/bin/stty": /dev/tty (Device not configured)
    at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1128)
    at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1071)
    at com.googlecode.lanterna.terminal.ansi.UnixLikeTTYTerminal.exec(UnixLikeTTYTerminal.java:158)
    at com.googlecode.lanterna.terminal.ansi.UnixLikeTTYTerminal.runSTTYCommand(UnixLikeTTYTerminal.java:150)
    at com.googlecode.lanterna.terminal.ansi.UnixLikeTTYTerminal.saveTerminalSettings(UnixLikeTTYTerminal.java:113)
    at com.googlecode.lanterna.terminal.ansi.UnixLikeTerminal.acquire(UnixLikeTerminal.java:86)
    at com.googlecode.lanterna.terminal.ansi.UnixLikeTTYTerminal.realAcquire(UnixLikeTTYTerminal.java:86)
    at com.googlecode.lanterna.terminal.ansi.UnixLikeTTYTerminal.<init>(UnixLikeTTYTerminal.java:77)
    at com.googlecode.lanterna.terminal.ansi.UnixTerminal.<init>(UnixTerminal.java:90)
    at com.googlecode.lanterna.terminal.ansi.UnixTerminal.<init>(UnixTerminal.java:80)
    at com.googlecode.lanterna.terminal.DefaultTerminalFactory.createUnixTerminal(DefaultTerminalFactory.java:478)
    at com.googlecode.lanterna.terminal.DefaultTerminalFactory.createHeadlessTerminal(DefaultTerminalFactory.java:143)
    at com.googlecode.lanterna.terminal.DefaultTerminalFactory.createTerminal(DefaultTerminalFactory.java:113)
    at hstools.nc.App.testWindow(App.java:32)
    at hstools.nc.App.main(App.java:23)
Caused by: java.io.FileNotFoundException: /dev/tty (Device not configured)
    at java.base/java.io.FileInputStream.open0(Native Method)
    at java.base/java.io.FileInputStream.open(FileInputStream.java:196)
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:139)
    at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:234)
    at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1107)
    ... 14 more

Apparently com.googlecode.lanterna.terminal.NativeGNULinuxTerminal is not found, and as a fallback a UnixTerminal is created, with /dev/tty as terminalDevice, but depending on the current terminal, that does not work.

Note: I found that replacing the terminalDevice (in lanterna code) by the result of commandtty did the trick, in my case it was "/dev/stty000" )

mabe02 commented 3 years ago

Hmm, wonder if this is a regression...? There was some recent changes to that area of the code if I remember correctly. Will check the history.

mabe02 commented 3 years ago

Ok, so looks like the code is similar on 3.0, so probably same behavior there. Can anyone else with Mac verify this is broken? I only have Windows and Linux computers at my disposal.

rednoah commented 3 years ago

lanterna 3.0.3 works for me on macOS 10.15.6 with both iTerm and Terminal (see screenshots posted in https://github.com/mabe02/lanterna/issues/505).

mieubrisse commented 3 years ago

Echoing that this is broken for me as well when I use setForceTextTerminal:

Lanterna: 3.1.0
Java: openjdk 11.0.9
MacOS: 10.15.7
iTerm: 3.4.2
ginkoblongata commented 4 months ago

On my Mac, when I open a Terminal or Term2 window and run the command, I get output:

$ tty
/dev/ttys000
$

If I continue to open additional windows, or run tmux and open panes, I will get output of increasing sequential numbers:

Open a Terminal window:

$ tty
/dev/ttys001
$

Now open a iTerm2 window:

$ tty
/dev/ttys002
$

Now run tmux:

$ tmux
$ (Ctrl-tmux new pane)
$ tty
/dev/ttys003
$

$ (Ctrl-tmux new pane)
$ tty
/dev/ttys004
$

$ (Ctrl-tmux new pane)
$ tty
/dev/ttys005
$

$ (Ctrl-tmux new pane)
$ tty
/dev/ttys006
$

The sequence is steady and increases across the different tmux panes and terminal applications, they take from a common global sequence.

Once the window or pane is closed, those become available for subsequent use.

The system will "fill in" from the now available id, for example closing the windows which had /dev/ttys002 and /dev/ttys003, then opening four new ones would result in:

$ (Ctrl-tmux new pane)
$ tty
/dev/ttys002
$

$ (Ctrl-tmux new pane)
$ tty
/dev/ttys003
$

$ (Ctrl-tmux new pane)
$ tty
/dev/ttys007
$

$ (Ctrl-tmux new pane)
$ tty
/dev/ttys008
$

I suspect instead of having "/dev/tty" hard coded, there could be a call to "tty" and read the output to know which "File" to use, but I haven't looked further into it.

I don't yet know how to apply the OP suggestion.

macOS: 14.2 (23C64)
Lanterna: 3.2.0-SNAPSHOT
Java: openjdk version "17.0.9" 2023-10-17
Terminal: Version 2.14 (452)
iTerm2: Build 3.4.22
tmux -V: tmux 3.3a
ginkoblongata commented 4 months ago

I thought this was happening to me too, but then I remembered I've not had the smoothest luck with running from a Gradle build via something like:

./gradlew someSuch

However, it works when I run from a bash script the sturdy form:

#!/usr/bin/env bash

java -cp "${class_path}" "${main_class_name}"

So that is fine. For development, if I were able to run as started from a Gradle build file, that would be slightly more convenient since then I could have many misc tasks defined there rather than an external bash script. But I suppose I can have some largish single bash script and some selector mechanism there to accomplish the same end result.

If it actually is because of the Gradle script for the OP, then maybe there is something off about how Gradle starts things. I haven't investigated getting it to work there. Maybe something about class loaders or something causes that NativeGNULinuxTerminal to not be found. I have not verified running from bash, if it's using the NativeGNULinuxTerminal or the fallback UnixTerminal.

I had already started putting in some setTtyOverride(tty :String) method in the DefaultTerminalFactory before I stumbled upon that it's apparently caused by trying to run from Gradle, or at least my local experience of it seems to be.

ginkoblongata commented 4 months ago

In the two cases: 1) running from Gradle and getting the above error 2) running from bash script, and not getting the above error & everything runs fine

Both cases, the terminal class resulting from DefaultTerminalFactory is com.googlecode.lanterna.terminal.ansi.UnixTerminal

So I suppose com.googlecode.lanterna.terminal.NativeGNULinuxTerminal is just not loadable on macOs due to some JNA stuff around that posixC not being avalailable. Also, maybe that is only intended for Linux, UnixTerminal seems to work great. I don't know if NativeGNULinuxTerminal should be runnable on Mac or if it has any performance benefits in this env.

But the odd thing is, I try to run 'tty' from exec within the java program right at the spot where it breaks. In both Gradle and bash script, I will get a String back from the process of "not a tty".

I had thought that I could run that and then get something equivalent to what I get from running it on a shell and then just using that in UnixTerminal where it has new File("/dev/tty") but apparently not.

As it turns out after searching through Gradle forums etc, I've concluded that Gradle is not capable of running a simple console application via it's JavaExec.

The inclination to use it is just for the dependencies management that is already in place for the build, but Gradle has been fundamentally broken in it's JavaExec for many years.