jwill / lanterna

Automatically exported from code.google.com/p/lanterna
GNU Lesser General Public License v3.0
0 stars 0 forks source link

getTerminalSize() issues with gnome terminal #61

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
TerminalFacade.createScreen(TerminalFacade.createTextTerminal()).getTerminalSize
()

return {80x20} regardless of the terminal size, and

TerminalFacade.createScreen(TerminalFacade.createTextTerminal()).getTerminal().g
etTerminalSize()

seems to get the correct size but after about a second it throws the following 
exception:

Exception in thread "main" java.io.IOException: Timeout while waiting for 
terminal size report! Maybe your terminal doesn't support cursor position 
report, please consider using a custom size querier                            
at 
com.googlecode.lanterna.terminal.InputEnabledAbstractTerminal.waitForTerminalSiz
eReport(InputEnabledAbstractTerminal.java:79)                  at 
com.googlecode.lanterna.terminal.text.ANSITerminal.getTerminalSize(ANSITerminal.
java:62)                                                       at 
com.googlecode.lanterna.terminal.text.UnixTerminal.getTerminalSize(UnixTerminal.
java:154)                                                      at 
Test.main(Test.java:22)                                         

Original issue reported on code.google.com by olivierb...@gmail.com on 7 Jan 2013 at 12:12

GoogleCodeExporter commented 9 years ago
That's odd, I haven't had any problems with Gnome terminal. Do you have any 
code to reproduce this?

Original comment by mab...@gmail.com on 7 Jan 2013 at 12:15

GoogleCodeExporter commented 9 years ago
Here is the code:

import com.googlecode.lanterna.TerminalFacade;
public class Test {
    public static void main(String[] args) {
      System.out.println(TerminalFacade.createScreen(TerminalFacade.createTextTerminal()).getTerminalSize() + "some string");
      System.out.println(TerminalFacade.createScreen(TerminalFacade.createTextTerminal()).getTerminal().getTerminalSize());
    }
}

and the output in my terminal (it's size is 212x58):

olivier:~$ java -cp "workspace/Test/bin:workspace/Test/lib/lanterna-2.1.2.jar" 
Test
{80x20}some string
^[[58;212R^[[58;212R^[[58;212RException in thread "main" java.io.IOException: 
Timeout while waiting for terminal size report! Maybe your terminal doesn't 
support cursor position report, please consider using a custom size querier
    at com.googlecode.lanterna.terminal.InputEnabledAbstractTerminal.waitForTerminalSizeReport(InputEnabledAbstractTerminal.java:79)
    at com.googlecode.lanterna.terminal.text.ANSITerminal.getTerminalSize(ANSITerminal.java:62)
    at com.googlecode.lanterna.terminal.text.UnixTerminal.getTerminalSize(UnixTerminal.java:154)
    at Test.main(Test.java:5)
olivier:~$ ;212R;212R;212R

Original comment by olivierb...@gmail.com on 7 Jan 2013 at 12:26

GoogleCodeExporter commented 9 years ago
I've found another issue that might to be related to the getTerminalSize() 
problem. When I read the keyboard inputs the space outside the 80x20 region is 
filled with green Xs:

import com.googlecode.lanterna.TerminalFacade;
import com.googlecode.lanterna.screen.Screen;
public class Test {
    public static void main(String[] args) throws InterruptedException {
      Screen s = TerminalFacade.createScreen(TerminalFacade.createTextTerminal());
      s.startScreen();
      s.readInput();
      s.refresh();
      Thread.sleep(1000);
      s.stopScreen();
    }
}

Original comment by olivierb...@gmail.com on 7 Jan 2013 at 1:11

Attachments:

GoogleCodeExporter commented 9 years ago
The green Xs are from the Screen object; whenever the terminal becomes bigger 
it will pad the buffer with those as a kind of visual notification to the 
developer that he/she isn't catching or processing the resize event (or not 
doing it fast enough).

Original comment by mab...@gmail.com on 13 Jan 2013 at 9:35

GoogleCodeExporter commented 9 years ago
Identical problem on Mac OS X (Terminal app and iTerm 2 app).

Error:
| Error Error executing script ImportMgid: java.io.IOException: Timeout while 
waiting for terminal size report! Maybe your terminal doesn't support cursor 
position report, please consider using a custom size querier (NOTE: Stack trace 
has been filtered. Use --verbose to see entire trace.)
java.io.IOException: Timeout while waiting for terminal size report! Maybe your 
terminal doesn't support cursor position report, please consider using a custom 
size querier
        at com.googlecode.lanterna.terminal.InputEnabledAbstractTerminal.waitForTerminalSizeReport(InputEnabledAbstractTerminal.java:79)
        at com.googlecode.lanterna.terminal.text.ANSITerminal.getTerminalSize(ANSITerminal.java:62)
        at com.googlecode.lanterna.terminal.text.UnixTerminal.getTerminalSize(UnixTerminal.java:154)
        at com.googlecode.lanterna.terminal.Terminal$getTerminalSize.call(Unknown Source)

Source code on groovy:

  Screen screen = TerminalFacade.createScreen(TerminalFacade.createTextTerminal())
  GUIScreen textGUI = new GUIScreen(screen)

  if (textGUI == null) {
    System.err.println("Couldn't allocate a terminal!")
    return;
  }
  textGUI.screen.startScreen()
  textGUI.backgroundRenderer.title = 'Import System'

  textGUI.screen.readInput()
  textGUI.screen.refresh()

  def screenSize = textGUI.screen.terminal.getTerminalSize()

Original comment by donbe...@gmail.com on 25 Jan 2013 at 10:10

GoogleCodeExporter commented 9 years ago
Okay, I'm starting to see the problem here. First of all, which layer are you 
trying to use? If you want a bitmap-buffer like access to the terminal, Screen 
is what you need. If you want a dialogs-like interface with build-in windows 
and components, GUIScreen it is. 
The key here is that these two layers behaves a bit differently. In order to 
kick off the GUIScreen, you'll need at least one Window to show. When you call 
GUIScreen.showWindow(...) it will start the whole event-loop and start 
processing events (such as resize the window).
If you use the Screen, you'll have to do the event loop yourself, and keep in 
mind that lanterna isn't asynchronously handling events. It will probably look 
something like this:

void eventLoop() {
   while(isStillAlive()) {
      if(screen.resizePending() || screenNeedsUpdating()) {
         screen.refresh();
      }
      Key key = screen.readInput();
      if(key != null) {
         handleInput(key);
      }
      else {
         idle();
      }
   }
}

Until more close inspection, I can't tell exactly why the terminal size 
reporting doesn't work in the code from comments #2 and #5. The problem in #3 
is that the window is resized while the process is in Thread.sleep(1000). If 
you did another s.refresh() and then a Thread.sleep(1000), I think the terminal 
would resize itself at least once.

Original comment by mab...@gmail.com on 27 Jan 2013 at 4:10

GoogleCodeExporter commented 9 years ago
I found the problem in comment #2. When you create a terminal but doesn't enter 
private mode, lanterna won't change the cbreak setting, meaning that any input 
you type on the keyboard won't go to the input stream of the application until 
you press enter. Size detection works in a way that puts the size of the 
terminal on the input stream, but without the cbreak settings applied, it's not 
available. Probably the cbreak, echo and other functionality should be exposed 
for those who wants to write an application that doesn't operate in private 
mode, I should add that to the wishlish for next lanterna version.

Here's a modified code that works:
        Terminal terminal = TerminalFacade.createTerminal();
        terminal.enterPrivateMode();
        TerminalSize terminalSize = terminal.getTerminalSize();
        terminal.exitPrivateMode();
        System.out.println(terminalSize);

Original comment by mab...@gmail.com on 27 Jan 2013 at 4:17

GoogleCodeExporter commented 9 years ago
Ok thank you! I have another question, is the only way not to show any green Xs 
to catch all resize events and fill the visible area with ' '?

Original comment by olivierb...@gmail.com on 27 Jan 2013 at 1:53

GoogleCodeExporter commented 9 years ago
Yeah, that's pretty much it. Maybe Screen should let you define which character 
to fill expanded space with?

Original comment by mab...@gmail.com on 28 Jan 2013 at 1:59

GoogleCodeExporter commented 9 years ago
That would be nice! At the moment I didn't find a way to catch the rezise fast 
enough no to see any Xs:

for(;;) {
  if (t.resizePending())
    t.refresh();
  final Key k = t.readInput();
  if (k != null) {
    ... (pass the key event to another thread)
  } else {
    try {
      Thread.sleep(1);
    } catch (InterruptedException e) {}
  }
}

where t is a class that extends Screen with

@Override public void refresh() {
  while (resizePending() || size != getTerminalSize()) {
    size = getTerminalSize();
    for (int x = 0; x < size.getColumns(); x++)
      for (int y = 0; y < size.getRows(); y++)
        putString(x, y, " ", null, null);
    super.refresh();
  }
  super.refresh();
}

Original comment by olivierb...@gmail.com on 28 Jan 2013 at 9:16

GoogleCodeExporter commented 9 years ago
You should be able to catch resize events and fill the expanded area without 
showing any Xs, unless you are running lanterna over a slow terminal 
connection. What GUIScreen is doing should work for any Screen. I'll try to 
come up with a simple code sample you can try. Still though, Screen should 
probably expose a way to define what to put in the expanded area when the user 
resizes the terminal...

Original comment by mab...@gmail.com on 28 Jan 2013 at 9:26

GoogleCodeExporter commented 9 years ago
While making a test class for this, I noticed something that may be 
confusing... When you call screen.refresh() after a resize, it will resize the 
internal screen buffer and pad any new space with those green Xs. There isn't 
any way to catch the resize event and immediately put some new content there; 
you actually need to call refresh() first for the Screen to re-dimension 
according to the new terminal size, then add your 'draw' your own characters to 
the newly sized screen and finally call refresh() again to make those new 
changes visible. The fact that you need to call refresh() twice when the 
terminal has been resized isn't completely obvious...

Here's an example:
public class ScreenTester {
    public static void main(String[] args) {
        Terminal terminal = TerminalFacade.createUnixTerminal();
        Screen screen = new Screen(terminal);
        screen.startScreen();
        paintScreenBlue(screen);
        while(true) {
            if(screen.resizePending()) {
                screen.refresh();
                paintScreenBlue(screen);
                screen.refresh();
            }

            Key input = screen.readInput();
            if(input != null && input.getKind() == Key.Kind.Escape) {
                break;
            }
            else {
                try { Thread.sleep(1); } catch(InterruptedException e) {}
            }
        }
        screen.stopScreen();
    }

    private static void paintScreenBlue(Screen screen) {
        ScreenWriter screenWriter = new ScreenWriter(screen);
        screenWriter.setBackgroundColor(Terminal.Color.BLUE);
        screenWriter.fillScreen(' ');
    }
}

When I quickly resize my terminal (I run this on XFCE with the terminal they 
supply), I can just briefly see the Xs for a fraction of a second before the 
second refresh() kicks in.

Key point: Next release of Lanterna should have better handling for size 
events, for now we'll have to deal with something like above

Original comment by mab...@gmail.com on 29 Jan 2013 at 5:44

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
Ah, the beauty of Open Source...

Being new to Lanterna, I ran into the same problem discussed above -- I was 
chagrined to discover that when the Terminal window was resized, the 
newly-exposed area was filled with green x's. Seems like an odd default, but...

After poking around in the source code, and looking at the methods available on 
the Screen object, I see that there is now a setPaddingcharacter(...) method, 
which as the name implies, allows you to set the default padding character -- 
to space, of course. Once you do that, when the screen is resized, no more ugly 
green x's!  ;-)

Original comment by JohnNArm...@gmail.com on 7 Dec 2013 at 4:22

GoogleCodeExporter commented 9 years ago
The point with those green Xs was to alert the developer that a resize event 
wasn't correctly handled are there are some new space on the screen that was 
never updated. However, the way the Screen was working, the updateScreenSize 
method used to do a refresh as well, giving you no way to actually prevent the 
green Xs to show up briefly. That should be fixed now.

Original comment by mab...@gmail.com on 8 Dec 2013 at 9:16