I have been using a simple piece of FFI code in Chez Scheme to obtain the number of rows and columns of the terminal emulator where a program is running. The procedure used to work when running with the x86 version of Chez (9.5.8).
However, I have recently switched to an Apple Silicon-based development machine and the code no longer returns the correct answer. The version of Chez used on the Apple machine is 9.9.9-pre-release-20.
The same code loads and runs with no errors but always returns
a size of (0 0)
regardless of the actual size of the terminal window. I don't have access to an Intel-based machine to assure that some change didn't sneak in somewhere, but I don't see it and it doesn't appear different in any of the version-control repositories where the procedure is used.
Here's the code:
;;; win-size-demo.ss -- Procedure to return the size (in rows and columns)
;;; of the terminal it is running in.
(import (chezscheme))
;; Load the C runtime library. Only tested on macOS. The other clauses
;; were taken from examples in other files of the Chez Scheme
;; source distribution.
(case (machine-type)
[(i3le ti3le a6le ta6le) (load-shared-object "libc.so.6")]
[(i3osx ti3osx a6osx ta6osx) (load-shared-object "libc.dylib")]
[(i3nt ti3nt a6nt ta6nt) (begin (load-shared-object "msvcrt.dll")
(load-shared-object "kernel32.dll"))]
;; The following is a new addition for Apple Silicon systems.
[(arm64osx tarm64osx) (load-shared-object "libSystem.dylib")]
[else (load-shared-object "libc.so")])
;; The preferred way to interrogate the window size is through the
;; `ioctl` function from the underlying C implementation. Here's
;; how that (used to) works.
;; Define some file descriptors for stdin/out. Couldn't find this
;; documented anywhere. These values are from Chez expediter.c.
(define STDIN_FD 0)
(define STDOUT_FD 1)
;; Value from /Library/Developer/CommandLineTools/SDKs/MacOSX13.3.sdk/usr/includes/sys/ttycom.h.
;; This value is caclulated via a C macro in <sys/ioccom.h>.
(define TIOCGWINSZ #x40087468)
;; From <sys/ttycom.h>.
(define-ftype win-size
(struct
[ws-row unsigned-short] ; number of rows in the window, in characters
[ws-col unsigned-short] ; number of columns in the window, in characters
[ws-xpixel unsigned-short] ; horizontal size of the window, in pixels
[ws-ypixel unsigned-short] ; vertical size of the window, in pixels
))
(define ioctl (foreign-procedure "ioctl" (int int (* win-size)) int))
(define errno (foreign-procedure "(cs)s_errno" () int))
(define strerror (foreign-procedure "(cs)s_strerror" (int) scheme-object))
;; Return the size of the terminal window using the `ioctl` function
;; from the underlying C library.
(define (window-size)
(let* ((win-size-buf (foreign-alloc (ftype-sizeof win-size)))
(win-size-ptr (make-ftype-pointer win-size win-size-buf)))
(let ((the-size (dynamic-wind
(lambda () #f)
(lambda () (let ((ctl-result (ioctl STDIN_FD TIOCGWINSZ win-size-ptr)))
(if (negative? ctl-result)
(let ((cep (current-error-port)))
(display "Error getting display size.\n" cep)
(display (strerror (errno)) cep)
(newline cep)
(list -1 -1))
(list (ftype-ref win-size (ws-row) win-size-ptr)
(ftype-ref win-size (ws-col) win-size-ptr)))))
(lambda () (foreign-free win-size-buf)))))
the-size)))
Reading the release notes for Chez Scheme 9.9.9, the section on changes to the FFI do not seem like they would make any difference. But obviously I'm missing something.
For Chez Scheme 9.9.9 and later, you must explicitly declare varargs functions like ioctl:
(define ioctl (foreign-procedure (__varargs_after 2) "ioctl" (int int (* win-size)) int))
Also, you can use libc.dylib for arm64osx and tarm64osx, so it won't require a separate line in the case statement.