mabe02 / lanterna

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

/dev/tty - no file found #610

Open damnms opened 2 months ago

damnms commented 2 months ago

I have a lanterna app that works quiet well, but i want to change a lot (different windows etc., at the moment its only 1), so i thought i start "fresh" and create everything new. But i fail in the very beginning somehow... and dont know why. Running debian trixie with regular java installed in intellij ultimate.

My code is this:

import com.googlecode.lanterna.gui2.BasicWindow;
import com.googlecode.lanterna.gui2.Button;
import com.googlecode.lanterna.gui2.MultiWindowTextGUI;
import com.googlecode.lanterna.gui2.WindowBasedTextGUI;
import com.googlecode.lanterna.gui2.dialogs.FileDialogBuilder;
import com.googlecode.lanterna.gui2.dialogs.MessageDialogBuilder;
import com.googlecode.lanterna.gui2.dialogs.MessageDialogButton;
import com.googlecode.lanterna.screen.Screen;
import com.googlecode.lanterna.terminal.DefaultTerminalFactory;

import java.io.File;
import java.io.IOException;

public class Test {

    public static void main(String[] args) {
        try {
            DefaultTerminalFactory terminalFactory = new DefaultTerminalFactory();
            Screen screen = terminalFactory.createScreen();

            BasicWindow welcomeWindow = new BasicWindow("Welcome Screen");
            WindowBasedTextGUI textGUI = new MultiWindowTextGUI(screen);

            Button loadButton = new Button("Load File", () -> {
                File file = new FileDialogBuilder()
                        .setTitle("Open File")
                        .setDescription("Choose a file to open")
                        .setActionLabel("Open")
                        .build()
                        .showDialog(textGUI);

                if (file != null) {
                    MessageDialogButton result = new MessageDialogBuilder()
                            .setTitle("File Selected")
                            .setText("You selected: " + file.getAbsolutePath())
                            .addButton(MessageDialogButton.OK)
                            .build()
                            .showDialog(textGUI);
                }
            });

            welcomeWindow.setComponent(loadButton);
            textGUI.addWindow(welcomeWindow);
            textGUI.waitForWindowToClose(welcomeWindow);

            screen.stopScreen();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

But it fails with:

> Task :Test.main()
java.io.IOException: Cannot run program "/bin/stty": /dev/tty (Kein passendes Gerät bzw. keine passende Adresse gefunden)
    at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1170)
    at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1089)
    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 com.googlecode.lanterna.terminal.DefaultTerminalFactory.createScreen(DefaultTerminalFactory.java:444)
    at org.gitlab.jsfdl.adapters.gui.Test.main(Test.java:21)
Caused by: java.io.FileNotFoundException: /dev/tty (Kein passendes Gerät bzw. keine passende Adresse gefunden)
    at java.base/java.io.FileInputStream.open0(Native Method)
    at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
    at java.base/java.io.FileInputStream.<init>(FileInputStream.java:152)
    at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:189)
    at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1126)
    ... 14 more

Caused by: java.io.FileNotFoundException: /dev/tty (Kein passendes Gerät bzw. keine passende Adresse gefunden)

This is wierd because it runs in the console. Anyone an idea why is that?

avl42 commented 2 months ago

Maybe you could single-step through the terminalFactory.createScreen() call? That will make some checks as to whether it is running as a terminal application or as a graphical application. Apparently this guessery fails: it ends up wrongly believing to run in a terminal.

My own guess is, that intellij ultimate "half-way mimicks" a terminal-like environment, just not a complete one (where stty would work).

fadein commented 2 months ago

Did you recently update your JDK? I have discovered that your test program works just fine for me when built and run inside IntelliJ with JDK-21. I get the same error message about /bin/stty when I set the project JDK to version 22.

I have verified this on Linux and macOS with IntelliJ 2024.1.4.

craigraw commented 1 month ago

Java 22 changed behaviour for System.console used in createTerminal():

https://github.com/mabe02/lanterna/blob/5b839cc52ccfff7ba56a7da40a753037af802893/src/main/java/com/googlecode/lanterna/terminal/DefaultTerminalFactory.java#L112

so that System.console no longer returns null when the standard streams are redirected or connected to a virtual terminal.

The workaround for now is to set a system property before calling createTerminal():

System.setProperty("jdk.console", "java.base");

@mabe02 Consider replacing the System.console() != null call with a test to see if the method Console.isTerminal() is available, and using it instead of checking System.console() if so.