Lanterna

A pure Java TUI library with a Swing-like component-based approach. No native dependencies, includes a Swing terminal emulator for development.

TUITerminalJavaPure-JavaComponent-based

GitHub Overview

mabe02/lanterna

Java library for creating text-based GUIs

Stars2,442
Watchers65
Forks260
Created:June 29, 2015
Language:Java
License:GNU Lesser General Public License v3.0

Topics

None

Star History

mabe02/lanterna Star History
Data as of: 7/25/2025, 11:08 AM

Lanterna

Lanterna is a Java library for creating semi-graphical user interfaces in text-only environments, similar to the C library curses but with more functionality. It's implemented in 100% pure Java with no native library dependencies.

Features

Three-Layer Architecture

  • Terminal Layer: Lowest abstraction layer
  • Screen Layer: Middle layer providing double-buffering
  • GUI Layer: Top layer providing complete GUI toolkit

Platform Support

  • Pure Java: No native library dependencies
  • Swing Emulator: Terminal emulator for GUI environments
  • Cross-Platform: Linux, Windows, macOS support
  • Auto-Detection: Automatic selection of optimal terminal implementation

GUI Components

  • Window System: Modal/multi-window support
  • Basic Components: Button, Label, TextBox, ComboBox
  • Layout Managers: LinearLayout, GridLayout
  • Dialogs: MessageDialog, FileDialog

Input/Output Features

  • Keyboard Input: Special keys, modifier keys support
  • Mouse Support: Click and drag events
  • Color Support: 256 colors, RGB color support
  • Unicode Support: Full UTF-8 support

Basic Usage

Installation

<!-- Maven -->
<dependency>
    <groupId>com.googlecode.lanterna</groupId>
    <artifactId>lanterna</artifactId>
    <version>3.1.1</version>
</dependency>
// Gradle
implementation 'com.googlecode.lanterna:lanterna:3.1.1'

Hello World (Terminal Layer)

import com.googlecode.lanterna.terminal.*;
import com.googlecode.lanterna.TerminalSize;
import java.io.IOException;

public class HelloWorldTerminal {
    public static void main(String[] args) throws IOException {
        // Create terminal
        Terminal terminal = new DefaultTerminalFactory().createTerminal();
        
        // Enter private mode
        terminal.enterPrivateMode();
        terminal.clearScreen();
        
        // Move cursor and output text
        terminal.setCursorPosition(10, 5);
        terminal.putCharacter('H');
        terminal.putCharacter('e');
        terminal.putCharacter('l');
        terminal.putCharacter('l');
        terminal.putCharacter('o');
        terminal.putCharacter(',');
        terminal.putCharacter(' ');
        terminal.putCharacter('W');
        terminal.putCharacter('o');
        terminal.putCharacter('r');
        terminal.putCharacter('l');
        terminal.putCharacter('d');
        terminal.putCharacter('!');
        
        terminal.flush();
        
        // Wait for key input
        terminal.readInput();
        
        // Exit private mode
        terminal.exitPrivateMode();
        terminal.close();
    }
}

Using Screen Layer

import com.googlecode.lanterna.screen.*;
import com.googlecode.lanterna.terminal.*;
import com.googlecode.lanterna.graphics.*;
import com.googlecode.lanterna.TextColor;
import java.io.IOException;

public class ScreenExample {
    public static void main(String[] args) throws IOException {
        // Create terminal and screen
        Terminal terminal = new DefaultTerminalFactory().createTerminal();
        Screen screen = new TerminalScreen(terminal);
        
        screen.startScreen();
        screen.clear();
        
        // Drawing with TextGraphics
        TextGraphics textGraphics = screen.newTextGraphics();
        textGraphics.setForegroundColor(TextColor.ANSI.YELLOW);
        textGraphics.setBackgroundColor(TextColor.ANSI.BLUE);
        
        // Draw text
        textGraphics.putString(5, 3, "Lanterna Screen Example");
        
        // Draw rectangle
        textGraphics.drawRectangle(
            new TerminalPosition(3, 2),
            new TerminalSize(25, 5),
            '█'
        );
        
        // Refresh screen
        screen.refresh();
        
        // Wait for key input
        screen.readInput();
        
        screen.stopScreen();
        terminal.close();
    }
}

Using GUI Layer

import com.googlecode.lanterna.gui2.*;
import com.googlecode.lanterna.screen.*;
import com.googlecode.lanterna.terminal.*;
import java.io.IOException;
import java.util.Arrays;

public class GUIExample {
    public static void main(String[] args) throws IOException {
        // Initialize terminal and GUI
        Terminal terminal = new DefaultTerminalFactory().createTerminal();
        Screen screen = new TerminalScreen(terminal);
        screen.startScreen();
        
        // Create GUI
        MultiWindowTextGUI gui = new MultiWindowTextGUI(screen);
        
        // Create window
        BasicWindow window = new BasicWindow("Lanterna GUI Example");
        window.setHints(Arrays.asList(Window.Hint.CENTERED));
        
        // Create panel
        Panel panel = new Panel();
        panel.setLayoutManager(new GridLayout(2));
        
        // Add components
        panel.addComponent(new Label("Name:"));
        TextBox nameBox = new TextBox();
        panel.addComponent(nameBox);
        
        panel.addComponent(new Label("Age:"));
        TextBox ageBox = new TextBox().setValidationPattern("[0-9]*");
        panel.addComponent(ageBox);
        
        panel.addComponent(new EmptySpace());
        Button submitButton = new Button("Submit", new Runnable() {
            @Override
            public void run() {
                MessageDialog.showMessageDialog(
                    gui,
                    "Input",
                    "Name: " + nameBox.getText() + "\n" +
                    "Age: " + ageBox.getText()
                );
            }
        });
        panel.addComponent(submitButton);
        
        // Set panel to window
        window.setComponent(panel);
        
        // Show GUI
        gui.addWindowAndWait(window);
        
        // Cleanup
        screen.stopScreen();
        terminal.close();
    }
}

Custom Component

import com.googlecode.lanterna.gui2.*;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor;

public class ProgressBar extends AbstractComponent<ProgressBar> {
    private double progress = 0.0;
    private String label = "";
    
    public ProgressBar() {
        setPreferredSize(new TerminalSize(20, 1));
    }
    
    public void setProgress(double progress) {
        this.progress = Math.max(0.0, Math.min(1.0, progress));
        invalidate();
    }
    
    public void setLabel(String label) {
        this.label = label;
        invalidate();
    }
    
    @Override
    protected ComponentRenderer<ProgressBar> createDefaultRenderer() {
        return new ComponentRenderer<ProgressBar>() {
            @Override
            public TerminalSize getPreferredSize(ProgressBar component) {
                return new TerminalSize(20, 1);
            }
            
            @Override
            public void drawComponent(TextGUIGraphics graphics, ProgressBar component) {
                TerminalSize size = graphics.getSize();
                int width = size.getColumns();
                int filledWidth = (int)(width * component.progress);
                
                // Draw progress bar
                graphics.setForegroundColor(TextColor.ANSI.GREEN);
                for (int i = 0; i < filledWidth; i++) {
                    graphics.setCharacter(i, 0, '█');
                }
                
                graphics.setForegroundColor(TextColor.ANSI.WHITE);
                for (int i = filledWidth; i < width; i++) {
                    graphics.setCharacter(i, 0, '░');
                }
                
                // Draw label
                if (!component.label.isEmpty()) {
                    String text = component.label + " " + 
                                 (int)(component.progress * 100) + "%";
                    int textX = (width - text.length()) / 2;
                    graphics.putString(textX, 0, text);
                }
            }
        };
    }
}

Theme Customization

import com.googlecode.lanterna.gui2.*;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.graphics.*;

public class CustomTheme extends SimpleTheme {
    public CustomTheme() {
        // Window style
        setWindowBackgroundColor(TextColor.ANSI.BLUE);
        setWindowForegroundColor(TextColor.ANSI.WHITE);
        
        // Button style
        addOverride(Button.class, new ComponentRenderer<Button>() {
            @Override
            public TerminalSize getPreferredSize(Button component) {
                return new TerminalSize(
                    component.getLabel().length() + 4, 3
                );
            }
            
            @Override
            public void drawComponent(TextGUIGraphics graphics, Button button) {
                ThemeDefinition themeDefinition = button.getThemeDefinition();
                
                if (button.isFocused()) {
                    graphics.applyThemeStyle(themeDefinition.getActive());
                } else {
                    graphics.applyThemeStyle(themeDefinition.getNormal());
                }
                
                // Draw button border
                graphics.drawRectangle(
                    TerminalPosition.TOP_LEFT_CORNER,
                    graphics.getSize(),
                    '█'
                );
                
                // Draw label centered
                graphics.putString(
                    2, 1,
                    button.getLabel()
                );
            }
        });
    }
}

Advanced Features

Multi-Window Application

// Create non-modal windows
BasicWindow window1 = new BasicWindow("Window 1");
window1.setHints(Arrays.asList(
    Window.Hint.NO_DECORATIONS,
    Window.Hint.FIXED_POSITION
));
window1.setPosition(new TerminalPosition(5, 3));

BasicWindow window2 = new BasicWindow("Window 2");
window2.setHints(Arrays.asList(
    Window.Hint.NO_DECORATIONS,
    Window.Hint.FIXED_POSITION  
));
window2.setPosition(new TerminalPosition(25, 8));

// Add both windows
gui.addWindow(window1);
gui.addWindow(window2);

Non-blocking Input

// Non-blocking input
KeyStroke keyStroke = screen.pollInput();
if (keyStroke != null) {
    if (keyStroke.getKeyType() == KeyType.Escape) {
        // ESC key pressed
    }
}

Animation

public class AnimatedLabel extends Label {
    private String fullText;
    private int currentIndex = 0;
    
    public AnimatedLabel(String text) {
        super("");
        this.fullText = text;
        
        // Animation timer
        new Timer().scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if (currentIndex <= fullText.length()) {
                    setText(fullText.substring(0, currentIndex));
                    currentIndex++;
                } else {
                    currentIndex = 0;
                }
            }
        }, 0, 100);
    }
}

Ecosystem

Supported Terminals

  • Linux: xterm, gnome-terminal, konsole
  • Windows: cmd.exe, PowerShell, Windows Terminal
  • macOS: Terminal.app, iTerm2
  • Emulators: PuTTY, MinTTY

Related Projects

  • CHARVA: Uses Lanterna as backend
  • Game Development: Used for roguelike games
  • Tool Development: TUI-based development tools

Advantages

  • Pure Java: No native dependencies for high portability
  • Three-Layer Architecture: Flexible abstraction levels
  • Swing-like: Familiar API for Java developers
  • Development Environment: Easy debugging in GUI environments
  • Active Development: Continuous maintenance

Limitations

  • Performance: May be slower than native implementations
  • Limited Features: Advanced graphics features are limited
  • Learning Curve: Understanding three-layer architecture required
  • Documentation: Limited non-English resources

Comparison with Other Libraries

FeatureLanternaJexerCHARVA
Pure JavaYesYesNo (JNI)
API StyleSwing-likeTurbo Vision-likeSwing compatible
FeaturesModerateAdvancedBasic
Development ActivityHighMediumLow
Learning CostMediumHighLow

Summary

Lanterna is an excellent library for developing TUI applications in Java. Its pure Java implementation provides high portability, the three-layer architecture offers flexibility, and the Swing-like API makes it easy to learn. The ability to use the same code from GUI development environments to headless server deployment is a significant advantage.