001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2025-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.common.exec.pty;
019
020import static java.util.Objects.requireNonNull;
021
022import org.jline.terminal.Size;
023import org.jline.terminal.Terminal;
024import org.jline.terminal.TerminalBuilder;
025
026import java.io.IOException;
027import java.nio.file.Path;
028
029public class Demo {
030
031    // FTR https://github.com/JetBrains/pty4j/issues/170
032
033    public static void main(String[] args) throws IOException, InterruptedException {
034        try (Terminal terminal = TerminalBuilder.builder().build()) {
035            terminal.enterRawMode();
036            // NB: terminal.echo() true or false makes no difference (because we're in raw mode)
037
038            int result;
039            // TODO Read from $SHELL (and use cmd.exe on Windows)
040            String[] cmd = {"/usr/bin/fish", "-li"};
041            System.out.println("Starting: " + String.join(" ", cmd));
042            try (var runner =
043                    new PtyRunner(
044                            true,
045                            Path.of("."),
046                            cmd,
047                            System.getenv(),
048                            requireNonNull(terminal.input(), "terminal.input"),
049                            requireNonNull(terminal.output(), "terminal.output"),
050                            null,
051                            true)) {
052                resize(terminal, runner);
053                terminal.handle(Terminal.Signal.WINCH, signal -> resize(terminal, runner));
054                result = runner.waitForExit();
055            }
056            System.out.println("PTY demo exits!");
057            System.exit(result);
058        }
059    }
060
061    private static void resize(Terminal terminal, PtyRunner runner) {
062        Size size = terminal.getSize();
063        var cols = size.getColumns();
064        var rows = size.getRows();
065        if (cols > 0 && rows > 0) runner.size(cols, rows);
066        // Do NOT "terminal.writer().flush()" - when exec, this destroys the child process updates!
067    }
068}