Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I find the console width with Java?

Tags:

java

console

Is there a way to find the width of the console in which my Java program is running?

I would like this to be cross platform if possible...

I have no desire to change the width of the buffer or the window, I just want to know its width so I can properly format text that is being printed to screen.

like image 693
masher Avatar asked Aug 17 '09 06:08

masher


5 Answers

There are no reliable cross-platform solutions to this problem. Indeed, there are situations where it is not possible to know what the real console width is.

(See other answers for approaches that work some of the time and/or on some platforms. But beware of the limitations ...)

For example, on a Linux system you can typically find out the notional terminal dimensions from the LINES and COLUMNS environment variables. While these variables are automatically updated when you resize some "terminal emulator" windows, this is not always the case. Indeed, in the case of a remote console connected via telnet protocol, there is no way to get the actual terminal dimensions to the user's shell.

EDIT: Just to add that if the user changes the dimensions of his/her xterm on Linux after launching a Java app, the Java app won't be notified, and it won't see the new dimensions reflected in its copy of the LINES and COLUMNS environment variables!

EDIT 2: My mistake: LINES and COLUMNS are bash shell variables, and they are not exported to the environment by default. You can "fix" this by running export COLUMNS LINES before you run your Java application.

like image 185
Stephen C Avatar answered Oct 14 '22 21:10

Stephen C


Actually, a Java library already exists to do this in Java: JLine 2. (There's an old version on SourceForce, but that link to GitHub seems to be the latest.)

This worked for me under Linux (Mint 14) and Windows (I don't know what version), using JLine 2.11:

terminalWidth = jline.TerminalFactory.get().getWidth();

JLine promises to work on Mac, too.

I've found that it returns bad widths (like 1!) under the Eclipse console (but even Java's native Console doesn't work under Eclipse!), and, unfortunately, under Cygwin. But I've worked around this with code that checks for unreasonable values (< 10) and just uses 80 in those cases.

Update for JLine 3 (per Mark—thanks, mate!):

terminalWidth = org.jline.terminal.TerminalBuilder.terminal().getWidth()
like image 33
Michael Scheper Avatar answered Oct 14 '22 23:10

Michael Scheper


There's a trick that you can use based on ANSI Escape Codes. They don't provide a direct way to query the console size, but they do have a command for requesting the current cursor position. By moving the cursor to a really high row and column and then requesting the cursor position you can get an accurate measurement.

Combine this with commands to store/restore the cursor position, as in the following example:

Send the following sequences to the terminal (stdout)

"\u001b[s"             // save cursor position
"\u001b[5000;5000H"    // move to col 5000 row 5000
"\u001b[6n"            // request cursor position
"\u001b[u"             // restore cursor position

Now watch stdin, you should receive a sequece that looks like \u001b[25;80R", where 25 is the row count, and 80 the columns.

I first saw this used in the Lanterna library.

Update:

There are really four different ways that I know of to achieve this, but they all make certain assumptions about the environment the program is running in, or the terminal device/emulator it is talking to.

  • Using the VT100 protocol. This is what this solution does, it assumes you are talking over stdin/stdout to a terminal emulator that honors these escape codes. This seems like a relatively safe assumption for a CLI program, but e.g. if someone is using cmd.exe this likely won't work.
  • terminfo/termcap. These are databases with terminal information, which you can query for instance with tput. Operating system dependent, and assumes you are connected to a TTY device. Won't work over ssh for instance.
  • Using the telnet protocol. Telnet has its own affordances for querying the screen size, but of course this only works if people connect to your application via the telnet client, not really an option in most cases.
  • Rely on the shell (e.g. bash), this is what solutions that use COLUMNS/ROWS variables do. Far from universal, but could work quite well if you provide a wrapper script for your app that makes sure the necessary env vars are exported.
like image 29
Arne Brasseur Avatar answered Oct 14 '22 23:10

Arne Brasseur


Edit: See @dave_thompson_085's comment about ProcessBuilder, as that's almost certainly a better approach.

Another answer mentioned running tput cols in a script before you start your command. But if you want to run that after Java has already started, using Runtime.getRuntime().exec(), you'll find that tput can't talk to your terminal, because Java has redirected stdout and stderr. As a not-at-all-portable workaround, you can use the magical /dev/tty device, which refers to the terminal of the current process. That lets you run something like this:

Process p = Runtime.getRuntime().exec(new String[] {
    "bash", "-c", "tput cols 2> /dev/tty" });
// Read the output of this process to get your terminal width

This works for me on Linux, but I wouldn't expect it to work everywhere. It will hopefully work on Mac. It definitely won't work on Windows, though it might with Cygwin.

like image 39
Jack O'Connor Avatar answered Oct 14 '22 23:10

Jack O'Connor


Java 6 has a class java.io.Console, but it unfortunately lacks the functionality you're asking for. Getting the console window width is not possible with the standard Java library and pure, cross-platform Java.

Here is an alternative Java console library which allows you to get the screen size, but it includes a Windows-specific DLL. You might be able to take the source code and compile the C part into a Linux or Mac OS X shared library, so that it will work on those platforms as well.

like image 4
Jesper Avatar answered Oct 14 '22 23:10

Jesper