Getting terminal window size in Go
2025-12-03This is quite a low level approach and our example will work only on UNIX-like systems, so in practice you would use something like golang.org/x/sys or golang.org/x/term packages.
Having said that, let's get to it. Firstly, we need to search for the operation name. It is TIOCGWINSZ which translates to "Terminal I/O Control - Get Window Size". We can see from its description that it returns row and column counts and a couple of other legacy, unused parameters which we will ignore*.
Let's start with our struct:
type WindowSize struct {
Row uint16
Col uint16
_ uint16
_ uint16
}
* you will notice that our struct contains those two ignored parameters. This is important since we need this struct size to be correct, otherwise we will have issues reading the result.
Now we can add a function to retrieve window size:
func windowSize(fd uintptr) (WindowSize, error) {
var winsize WindowSize
_, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
syscall.TIOCGWINSZ,
uintptr(unsafe.Pointer(&winsize)),
)
if errno != 0 {
return WindowSize{}, errno
}
return winsize, nil
}
This is basically calling ioctl with file descriptor and TIOCGWINSZ as the first and second arguments, and using our winsize as the output. unsafe.Pointer is needed because we cannot convert straight to uintptr. Now we can add some log statements and run it:
package main
import (
"fmt"
"os"
"syscall"
"unsafe"
)
func main() {
winsize, err := windowSize(os.Stdout.Fd())
if err != nil {
panic(err)
}
fmt.Printf("%#v\n", winsize)
fmt.Printf("cols: %d\n", winsize.Col)
fmt.Printf("rows: %d\n", winsize.Row)
}
type WindowSize struct {
Row uint16
Col uint16
_ uint16
_ uint16
}
func windowSize(fd uintptr) (WindowSize, error) {
var winsize WindowSize
_, _, errno := syscall.Syscall(
syscall.SYS_IOCTL,
fd,
syscall.TIOCGWINSZ,
uintptr(unsafe.Pointer(&winsize)),
)
if errno != 0 {
return WindowSize{}, errno
}
return winsize, nil
}
This outputs something like this:
main.WindowSize{Row:0x2d, Col:0x50, _:0x410, _:0x4ec}
cols: 80
rows: 45