2

I'm creating a sort of messaging program, where users can send messages to each other. I would like to make this look like any social messaging app where the most recently received message appears at the bottom of the screen and the previous messages are shown above (older messages appear higher up). Is there a way to print a message (String) to a specific line in terminal, or to the bottom line?

Mac OS x

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
H_Lev1
  • 253
  • 4
  • 18
  • Does `System.out#println` not print to the bottom of the terminal? – ifly6 Feb 13 '18 at 18:25
  • As ifly6 said, System.out.println("text"); always prints at the bottom. There is no way to print on a spesific line. If you want more customization you should consider creating a UI either in Swing or JavaFX. – Nick Feb 13 '18 at 18:29
  • You're looking for a Java library that works something like [curses](https://en.wikipedia.org/wiki/Curses_(programming_library)) but in Java. I found [one](https://github.com/kba/jcurses) but it looks like there are others. – stdunbar Feb 13 '18 at 18:31
  • You'll probably need to [clear](https://stackoverflow.com/questions/2979383/java-clear-the-console) the console/terminal and print out the layout you would like each time, or if it's updated (ie: user receives or sends a message). – escapesequence Feb 13 '18 at 18:31
  • @Nick I thought `System.out.println("text");` always prints at the current terminal cursor position, not to the very last line of the terminal. When a terminal is first opened, the cursor is at the top (assuming it's a typical `bash` or Windows command terminal). The OP is basically looking to treat the bottom area of the terminal as a fixed notification area for text messages. – lurker Feb 13 '18 at 18:32
  • @lurker just had a quick check at Oracle's documentation. It terminates the current line by writting writing the line separator string. ( https://docs.oracle.com/javase/7/docs/api/java/io/PrintStream.html#println() ) Sort of like "appending" the text at the end of the console. It doesn't print a new message straight to the bottom (if the terminal is completetly empty), it works it's way there. If OP wants something like a notification area in the console, I idon't know how could that be achieved. – Nick Feb 13 '18 at 18:45
  • @Nick yep that's what I was saying (probably unclearly). ifly6 is indicating it goes to the bottom of the terminal, which it doesn't. So in short, `println` doesn't do what the OP is asking for. – lurker Feb 13 '18 at 19:02

1 Answers1

2

Oh the days of yore! I remember when every village lad, who dreamed to become a Wizard, knew his ANSI control codes by heart. Surely those byte sequences allowed one in the knowledge to produce awesome visions and bright colors of exotic places on text displays lesser ones thought only as dumb terminals.

But the days are gone, and we have high resolution displays and 3D graphics, mouses and touchscreens. Time to go, you old fool! But wait! In the heart of every terminal window, (we are not speaking about Windows now) there beats a golden heart of old VT100 terminal. So yes, it is possible to control cursor positions and scroll areas with Java console output.

The idea is, that the terminal is not so dump after all. It only waits escape sequences for you to tell what special it should do. Instructions start with escape character, which is non-printable ASCII character 27, usually written in hexadecimal 0x1b or octal 033. After that some human readable stuff follows, most often bracket [, some numerical information and a letter. If more numbers are needed, they are separated by semicolons.

For instance, to change font color to red, you output sequence <ESC>[31m like this:

System.out.println("\033[31mThis should appear red");

You'd better open a separate terminal window for fiddling with the codes, because the display turns easily garbled. In fact the colors of shell prompt and directory listings are implemented by these codes and can easily be personalized.

If you want to separate areas from top and bottom of the window from scrolling, there's a code for that, <ESC>[<top>;<bottom>r where you declare the lines from top and bottom, between where the scrolling happens, for instance: \033[2;22r

Links:

And of course answer is not an answer without running code. Right? I have to admit I got little carried away with this. Although it felt strange to use Java for terminal sequences. K&R C or COMMODORE BASIC would have been more fitting. I made very simple demo, but slowed down the printing speed to same level as old 1200 baud modem, so you can easily observe what's happening. Set the SPEED to 0 to disable.

Escape sequences are same kind of markup as HTML text formatting tags, and what's best, The BLINK is still there!

Save as TerminalDemo.java, compile with javac TerminalDemo.java and run with command: java TerminalDemo

import java.io.*;

public class TerminalDemo {
    // Default speed in bits per second for serial line  simulation
    // Set 0 to disable
    static final int SPEED = 1200;

    // ANSI Terminal codes
    static final String ESC = "\033";

    static String clearScreen() { return "\033[2J"; }
    static String cursorHome() { return "\033[H"; }
    static String cursorTo(int row, int column) {
    return String.format("\033[%d;%dH", row, column);
    }
    static String cursorSave() { return "\033[s"; }
    static String cursorRestore() { return "\033[u"; }

    static String scrollScreen() { return "\033[r"; }
    static String scrollSet(int top, int bottom) {
    return String.format("\033[%d;%dr", top, bottom);
    }
    static String scrollUp() { return "\033D"; }
    static String scrollDown() { return "\033D"; }

    static String setAttribute(int attr) {
    return String.format("\033[%dm", attr);
    }

    static final int ATTR_RESET = 0;
    static final int ATTR_BRIGHT = 1;
    static final int ATTR_USCORE = 4;
    static final int ATTR_BLINK = 5;
    static final int ATTR_REVERSE = 7;

    static final int ATTR_FCOL_BLACK = 30;
    static final int ATTR_FCOL_RED = 31;
    static final int ATTR_FCOL_GREEN = 32;
    static final int ATTR_FCOL_YELLOW = 33;

    static final int ATTR__BCOL_BLACK = 40;
    static final int ATTR__BCOL_RED = 41;
    static final int ATTR__BCOL_GREEN = 42;

    public static void main(String[] args) {
    // example string showing some text attributes
    String s = "This \033[31mstring\033[32m should \033[33mchange \033[33m color \033[41m and start \033[5m blinking!\033[0m Isn't that neat?\n";

    // Reset scrolling, clear screen and bring cursor home
    System.out.print(clearScreen());
    System.out.print(scrollScreen());

// Print example string s
slowPrint(s);

// some text attributes
slowPrint("This "
          + setAttribute(ATTR_USCORE) + "should be undescored\n"
      + setAttribute(ATTR_RESET)
      + setAttribute(ATTR_FCOL_RED) + "and this red\n"
      + setAttribute(ATTR_RESET)
      + "some "
      + setAttribute(ATTR_BRIGHT)
      + setAttribute(ATTR_FCOL_YELLOW)
      + setAttribute(ATTR_BLINK) + "BRIGHT YELLOW BLINKIN\n"
      + setAttribute(ATTR_RESET)
      + "could be fun.\n\n"
      + "Please press ENTER");

// Wait for ENTER
try { System.in.read(); } catch(IOException e) {e.printStackTrace();}

// Set scroll area
slowPrint(""
      + clearScreen()
      + scrollSet(2,20)
      + cursorTo(1,1)
      + "Cleared screen and set scroll rows Top: 2 and Bottom: 20\n"
      + cursorTo(21,1)
      + "Bottom area starts here"
      + cursorTo(2,1)
      + "");

// print some random text
slowPrint(randomText(60));

// reset text attributes, reset scroll area and set cursor
// below scroll area
System.out.print(setAttribute(ATTR_RESET));
System.out.print(scrollScreen());
System.out.println(cursorTo(22,1));
    }

    // Slow things down to resemble old serial terminals
    private static void slowPrint(String s) {
    slowPrint(s, SPEED);
    }

    private static void slowPrint(String s, int bps) {
    for (int i = 0; i < s.length(); i++) {
        System.out.print(s.charAt(i));

        if(bps == 0) continue;

        try { Thread.sleep((int)(8000.0 / bps)); }
        catch(InterruptedException ex)
        { Thread.currentThread().interrupt(); }
    }
    }

    // Retursn a character representation of sin graph
    private static String randomText(int lines) {
    String r = "";

    for(int i=0; i<lines; i++) {
        int sin = (int)Math.abs((Math.sin(1.0/20 * i)*30));
        r += setAttribute((sin / 4) + 30);
        for(int j=0; j<80; j++) {
        if(j > 40 + sin)
            break;
        r += (j < (40-sin)) ? " " : "X";
        }
        r += setAttribute(ATTR_RESET) + "\n";
    }
    return r;
    }
}
Janne Tuukkanen
  • 1,620
  • 9
  • 13
  • Wasn't this your question: "Is there a way to print a message (String) to a specific line in terminal, or to the bottom line?" Answer to both is yes. – Janne Tuukkanen Feb 14 '18 at 22:58