Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MacOSX ARM terminal size always is the default #238

Closed
apatrida opened this issue Oct 25, 2024 · 15 comments · Fixed by #239 or #240
Closed

MacOSX ARM terminal size always is the default #238

apatrida opened this issue Oct 25, 2024 · 15 comments · Fixed by #239 or #240

Comments

@apatrida
Copy link

apatrida commented Oct 25, 2024

Mordant 3.0.0

In default Terminal, ITerm2, and Intellij terminal on my ARM Mac, I always end up with terminal size of 79x24.

This program shows a bit more:

fun main() {
    val terminal = Terminal()
    terminal.updateSize()
    terminal.println(terminal.terminalInfo)
    terminal.println(terminal.terminalInterface.javaClass.name)
    terminal.println(terminal.size)
    println(TerminalInterfaceFfmMacos().getTerminalSize())
}

terminal size is 79x24 default, and the FFM interface returns null.

TerminalInfo(ansiLevel=TRUECOLOR, ansiHyperLinks=false, outputInteractive=true, inputInteractive=true, supportsAnsiCursor=true)
com.github.ajalt.mordant.terminal.terminalinterface.ffm.TerminalInterfaceFfmMacos
79×24
null

In FFM getTerminalSize the code returns 0,0 for val size = MacosCLibrary.winsize(arena) and the ioctl check is false.

The JNA version also returns null for getTerminalSize()

TerminalInfo(ansiLevel=TRUECOLOR, ansiHyperLinks=false, outputInteractive=true, inputInteractive=true, supportsAnsiCursor=true)
com.github.ajalt.mordant.terminal.terminalinterface.jna.TerminalInterfaceJnaMacos
79×24
null

This is intended to work, no?

@ajalt
Copy link
Owner

ajalt commented Oct 25, 2024

Yes, that's supposed to work. Maybe the ioct is wrong on ARM? I don't have a mac to test on, but if you have a C compiler, can you try this C code and see what it prints?

#include <stdio.h>
#include <sys/ioctl.h>

int main() {
  struct winsize sz;
  printf("TIOCGWINSZ: 0x%lx\n", TIOCGWINSZ);
  printf("TIOCSWINSZ: 0x%lx\n", TIOCSWINSZ);
  ioctl(0, TIOCGWINSZ, &sz);
  printf("width: %i  height: %i\n", sz.ws_col, sz.ws_row);
  return 0;
}

I'm a little surprised the the JNA module is failing too, since that one shells out to stty instead of the faster native call. Can you run /usr/bin/env stty size and tell me what it prints?

@apatrida
Copy link
Author

apatrida commented Oct 26, 2024

❯ /usr/bin/env stty size
39 114

I must have bonked the JNA test prior, but FFM definitely does not work.

JNA working: (Iterm, Terminal, not Intellij)

TerminalInfo(ansiLevel=TRUECOLOR, ansiHyperLinks=true, outputInteractive=true, inputInteractive=true, supportsAnsiCursor=false)
com.github.ajalt.mordant.terminal.terminalinterface.jna.TerminalInterfaceJnaMacos
114×39
114×39
foo
bar

while FFM continues to be wrong:

TerminalInfo(ansiLevel=TRUECOLOR, ansiHyperLinks=true, outputInteractive=true, inputInteractive=true, supportsAnsiCursor=false)
com.github.ajalt.mordant.terminal.terminalinterface.ffm.TerminalInterfaceFfmMacos
79×24
79×24

Output of C program in Iterm 2: (same terminal as above FFM test)

TIOCGWINSZ: 0x40087468
TIOCSWINSZ: 0x80087467
width: 114  height: 39

Terminal:

TIOCGWINSZ: 0x40087468
TIOCSWINSZ: 0x80087467
width: 230  height: 48

Intellij terminal:

TIOCGWINSZ: 0x40087468
TIOCSWINSZ: 0x80087467
width: 0  height: 0

Warp:

TIOCGWINSZ: 0x40087468
TIOCSWINSZ: 0x80087467
width: 153  height: 45

same terminal back to back:

Kotlin program, JDK 22, Gradle application plugin running after `./gradlew build install` from the bash script, --enable-native-access=ALL-UNNAMED

TerminalInfo(ansiLevel=TRUECOLOR, ansiHyperLinks=true, outputInteractive=true, inputInteractive=true, supportsAnsiCursor=false)
com.github.ajalt.mordant.terminal.terminalinterface.ffm.TerminalInterfaceFfmMacos
79×24

C program

TIOCGWINSZ: 0x40087468
TIOCSWINSZ: 0x80087467
width: 114  height: 39

@apatrida
Copy link
Author

Ok, I thought it was the gradle generated build script, but it's not. Running the resulting class by hand with the classpath has the same affect of being the default size.

I removed all other command line arguments to the JVM. I don't think another dependency could affect this since you are calling directly via FFM.

@apatrida
Copy link
Author

Created a test project with only Mordant, so it is not the dependencies.

@ajalt
Copy link
Owner

ajalt commented Oct 27, 2024

Thanks for the investigation! So the ioctl you shared is indeed different than the one being used in the FFM module; I updated the constant in #239, so once if you can, give that a try and if it still doesn't work we can reopen this issue.

@apatrida
Copy link
Author

crashes.

hs_err_pid49592.log

@apatrida
Copy link
Author

trying this in C:

#include <stddef.h>
#include <stdio.h>
#include <sys/ioctl.h>

int main() {
    struct winsize sz;
    printf("TIOCGWINSZ: 0x%lx\n", TIOCGWINSZ);
    printf("sizeof(winsize): %lu\n", sizeof(struct winsize));
    // Print field offsets
    printf("offset ws_row: %lu\n", offsetof(struct winsize, ws_row));
    printf("offset ws_col: %lu\n", offsetof(struct winsize, ws_col));
    printf("offset ws_xpixel: %lu\n", offsetof(struct winsize, ws_xpixel));
    printf("offset ws_ypixel: %lu\n", offsetof(struct winsize, ws_ypixel));

    int ret = ioctl(0, TIOCGWINSZ, &sz);
    printf("ioctl return: %d\n", ret);
    printf("width: %i  height: %i\n", sz.ws_col, sz.ws_row);
    return 0;
}

results in:

TIOCGWINSZ: 0x40087468
sizeof(winsize): 8
offset ws_row: 0
offset ws_col: 2
offset ws_xpixel: 4
offset ws_ypixel: 6
ioctl return: 0
width: 0  height: 0

seems to match, no?

@apatrida
Copy link
Author

Also 0x00005413L for TIOCGWINSZ doesn't match my header files. Unless mine are ARM only, then this value is wrong for MAC Intel as well. I can dig up an Intel MAC to check this too.

@apatrida
Copy link
Author

Ok, found the problem. whew!

This function

MethodHandlesHolder::handle

needs to add Linker.Option.firstVariadicArg(2) to the linker.downcallHandle() call as a linker option just for ioctl call.

effectively:

if (name == "ioctl") {
    linker.downcallHandle(
        it,
        FunctionDescriptor.of(resLayout, *argLayouts),
        Linker.Option.firstVariadicArg(2)
    )
} else {
    linker.downcallHandle(
        it,
        FunctionDescriptor.of(resLayout, *argLayouts),
    )
} 

but probably better to pass it in for some other way.

@apatrida
Copy link
Author

and for MAC it should always be private val TIOCGWINSZ = 0x40087468L no matter the architecture.

@apatrida
Copy link
Author

result:

❯ ./runsample progress
TerminalInfo(ansiLevel=TRUECOLOR, ansiHyperLinks=true, outputInteractive=true, inputInteractive=true, supportsAnsiCursor=false)
com.github.ajalt.mordant.terminal.terminalinterface.ffm.TerminalInterfaceFfmMacos
114×39 <---- YAY!

@apatrida
Copy link
Author

@ajalt you can re-open?

@ajalt ajalt reopened this Oct 28, 2024
@ajalt
Copy link
Owner

ajalt commented Oct 29, 2024

@apatrida Great find! I implemented your suggestion in #240.

@apatrida
Copy link
Author

tested, works fine here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants