From febdd6d07b5209afe9972a825acf779fe50dd63a Mon Sep 17 00:00:00 2001 From: dwrz Date: Fri, 23 Oct 2026 00:41:03 +0000 Subject: [PATCH] Add terminal pkg --- pkg/terminal/terminal.go | 98 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 pkg/terminal/terminal.go diff --git a/pkg/terminal/terminal.go b/pkg/terminal/terminal.go new file mode 100644 index 0000000..a2dffb8 --- /dev/null +++ b/pkg/terminal/terminal.go @@ -0,0 +1,98 @@ +package terminal + +import ( + "fmt" + "syscall" + "unsafe" +) + +const ( + ClearScreen = "\u001Bc" + CursorHide = "\u001B[?25l" + CursorShow = "\u001B[?25h" + CursorTopLeft = "\u001B[H" + EraseLine = "\u001B[K" + + SetCursorFmt = "\u001B[%d;%dH" +) + +type Terminal struct { + fd uintptr + termios *syscall.Termios +} + +type Size struct { + Rows uint16 + Columns uint16 + XPixels uint16 + YPixels uint16 +} + +func New(fd uintptr) (*Terminal, error) { + var t = &Terminal{ + termios: &syscall.Termios{}, + } + + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + fd, + syscall.TCGETS, + uintptr(unsafe.Pointer(t.termios)), + ); err != 0 { + return nil, fmt.Errorf("ioctl syscall: %w", err) + } + + return t, nil +} + +// Reset the terminal into the original termios. +func (t *Terminal) Reset() error { + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + t.fd, + uintptr(syscall.TCSETS), + uintptr(unsafe.Pointer(t.termios)), + ); err != 0 { + return fmt.Errorf("ioctl syscall: %w", err) + } + + return nil +} + +// SetRaw enables raw mode. +func (t *Terminal) SetRaw() error { + var termios = *t.termios + termios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON + termios.Oflag &^= syscall.OPOST + termios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN + termios.Cflag &^= syscall.CSIZE | syscall.PARENB + termios.Cflag |= syscall.CS8 + termios.Cc[syscall.VMIN] = 1 + termios.Cc[syscall.VTIME] = 0 + + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + t.fd, + uintptr(syscall.TCSETS), + uintptr(unsafe.Pointer(&termios)), + ); err != 0 { + return fmt.Errorf("ioctl syscall: %w", err) + } + + return nil +} + +// Size returns the zero-indexed size of the terminal. +func (t *Terminal) Size() (*Size, error) { + var size = &Size{} + if _, _, err := syscall.Syscall( + syscall.SYS_IOCTL, + t.fd, + syscall.TIOCGWINSZ, + uintptr(unsafe.Pointer(size)), + ); err != 0 { + return nil, fmt.Errorf("ioctl syscall: %w", err) + } + + return size, nil +}