From 5d9b072d53f83853e0cbdc22980f44a1f6584067 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Mon, 29 Dec 2025 21:19:47 -0500 Subject: [PATCH 1/5] wip --- go.mod | 2 +- go.sum | 7 +++++ src/terminal_unix.go | 46 +++++++++++++++++++++++++++-- src/tui/light_unix.go | 67 +++++++++++++++++++++++++++++++++++++++---- src/util/chars.go | 1 + src/util/util.go | 14 +++++++-- src/util/util_unix.go | 8 ++++-- 7 files changed, 130 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index e3f1ad9de48..f14eaa79df4 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module github.com/junegunn/fzf require ( github.com/gdamore/tcell/v2 v2.5.4 - github.com/mattn/go-isatty v0.0.17 + github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-runewidth v0.0.14 github.com/mattn/go-shellwords v1.0.12 github.com/rivo/uniseg v0.4.4 diff --git a/go.sum b/go.sum index 6556c5aa547..35a17d199de 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,12 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= +github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= @@ -31,6 +37,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/src/terminal_unix.go b/src/terminal_unix.go index 1ce7854ee45..e1f20c3e1d1 100644 --- a/src/terminal_unix.go +++ b/src/terminal_unix.go @@ -2,26 +2,66 @@ package fzf +/* +#include +#include +#include + +// Wrapper for kill(2) +int native_kill(pid_t pid, int sig) { + return kill(pid, sig); +} + +// Wrapper for getpgid(2) +pid_t native_getpgid(pid_t pid) { + return getpgid(pid); +} +*/ +import "C" import ( "os" "os/signal" "strings" "syscall" + "fmt" - "golang.org/x/sys/unix" +// "golang.org/x/sys/unix" ) +// Kill sends a signal to a process. +func Kill(pid int, signal syscall.Signal) error { + // C.kill returns 0 on success, -1 on failure + res := C.native_kill(C.pid_t(pid), C.int(signal)) + if res != 0 { + return fmt.Errorf("kill failed for pid %d", pid) + } + return nil +} + + +// Getpgid returns the process group ID for the given process ID. +func Getpgid(pid int) (int, error) { + // C.getpgid returns the PGID or -1 on failure + res := C.native_getpgid(C.pid_t(pid)) + if res < 0 { + return 0, fmt.Errorf("getpgid failed for pid %d", pid) + } + return int(res), nil +} + + + func notifyOnResize(resizeChan chan<- os.Signal) { signal.Notify(resizeChan, syscall.SIGWINCH) } func notifyStop(p *os.Process) { pid := p.Pid - pgid, err := unix.Getpgid(pid) + pgid, err := Getpgid(pid) if err == nil { pid = pgid * -1 } - unix.Kill(pid, syscall.SIGSTOP) + Kill(pid, syscall.SIGSTOP) } func notifyOnCont(resizeChan chan<- os.Signal) { diff --git a/src/tui/light_unix.go b/src/tui/light_unix.go index 46188869d63..9a9cf91944d 100644 --- a/src/tui/light_unix.go +++ b/src/tui/light_unix.go @@ -2,18 +2,59 @@ package tui +/* +#include +#include + +// Simple wrapper to call the ioctl +int native_get_winsize(int fd, struct winsize *ws) { + return ioctl(fd, TIOCGWINSZ, ws); +} + +// Wrapper to call the stable POSIX tcgetattr function +int get_terminal_state(int fd, struct termios *t) { + return tcgetattr(fd, t); +} +*/ +import "C" import ( "fmt" "os" "os/exec" "strings" "syscall" + "errors" + "unsafe" "github.com/junegunn/fzf/src/util" - "golang.org/x/sys/unix" +// "golang.org/x/sys/unix" "golang.org/x/term" ) +// State wraps the C termios structure to keep it compatible with your existing code. +type State struct { + termios C.struct_termios +} + +func GetState(fd int) (*State, error) { + var t C.struct_termios + + // Call the C wrapper. 0 is success, -1 is failure. + if C.get_terminal_state(C.int(fd), &t) != 0 { + return nil, errors.New("failed to get terminal state (tcgetattr)") + } + + return &State{termios: t}, nil +} + +// Winsize matches the POSIX terminal window size structure +type Winsize struct { + Rows uint16 + Cols uint16 + Xpixel uint16 + Ypixel uint16 +} + func IsLightRendererSupported() bool { return true } @@ -35,11 +76,12 @@ func (r *LightRenderer) fd() int { func (r *LightRenderer) initPlatform() error { fd := r.fd() - origState, err := term.GetState(fd) + origState, err := GetState(fd) if err != nil { return err } - r.origState = origState + r.origState = (*term.State)(unsafe.Pointer(origState)) + term.MakeRaw(fd) return nil } @@ -111,9 +153,22 @@ func (r *LightRenderer) getch(nonblock bool) (int, bool) { } func (r *LightRenderer) Size() TermSize { - ws, err := unix.IoctlGetWinsize(int(r.ttyin.Fd()), unix.TIOCGWINSZ) - if err != nil { + var ws C.struct_winsize + res := C.native_get_winsize(C.int(r.ttyin.Fd()), &ws) + + if res != 0 { return TermSize{} } - return TermSize{int(ws.Row), int(ws.Col), int(ws.Xpixel), int(ws.Ypixel)} + + return TermSize{int(ws.ws_row), int(ws.ws_col), int(ws.ws_xpixel), int(ws.ws_ypixel)} + +// var cRows, cCols C.int +// rs := C.get_terminal_size(C.int(r.ttyin.Fd()), &cRows, &cCols) + +// ws, err := unix.IoctlGetWinsize(int(r.ttyin.Fd()), unix.TIOCGWINSZ) +// if err != nil { +// return TermSize{} +// } +// return TermSize{int(ws.Row), int(ws.Col), int(ws.Xpixel), int(ws.Ypixel)} +// return TermSize{0,0,0,0} } diff --git a/src/util/chars.go b/src/util/chars.go index 41de92431e0..2b41b602f1f 100644 --- a/src/util/chars.go +++ b/src/util/chars.go @@ -1,5 +1,6 @@ package util +//import "C" import ( "fmt" "unicode" diff --git a/src/util/util.go b/src/util/util.go index 73f53daf6bd..5beeda26c50 100644 --- a/src/util/util.go +++ b/src/util/util.go @@ -1,12 +1,16 @@ package util +/* +#include +*/ +import "C" import ( "math" "os" "strings" "time" - "github.com/mattn/go-isatty" +// "github.com/mattn/go-isatty" "github.com/mattn/go-runewidth" "github.com/rivo/uniseg" ) @@ -140,12 +144,16 @@ func DurWithin( // IsTty returns true if stdin is a terminal func IsTty() bool { - return isatty.IsTerminal(os.Stdin.Fd()) + fd := C.int(os.Stdin.Fd()) + return (C.isatty(fd) != 0) + //return isatty.IsTerminal(os.Stdin.Fd()) } // ToTty returns true if stdout is a terminal func ToTty() bool { - return isatty.IsTerminal(os.Stdout.Fd()) + fd := C.int(os.Stdout.Fd()) + return (C.isatty(fd) != 0) + //return isatty.IsTerminal(os.Stdout.Fd()) } // Once returns a function that returns the specified boolean value only once diff --git a/src/util/util_unix.go b/src/util/util_unix.go index 2991fd2cf36..d0377f0a932 100644 --- a/src/util/util_unix.go +++ b/src/util/util_unix.go @@ -7,7 +7,7 @@ import ( "os/exec" "syscall" - "golang.org/x/sys/unix" +// "golang.org/x/sys/unix" ) // ExecCommand executes the given command with $SHELL @@ -49,5 +49,9 @@ func Read(fd int, b []byte) (int, error) { } func SetStdin(file *os.File) { - unix.Dup2(int(file.Fd()), 0) + //unix.Dup2(int(file.Fd()), 0) + //unix.Close(0) + //r1, _, errno := + syscall.Syscall(syscall.SYS_DUP2, uintptr(file.Fd()), uintptr(0), 0) + //_ = r1 } From 3033716eb0796472a5b78231e4d333b85ef3960e Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Mon, 29 Dec 2025 21:27:17 -0500 Subject: [PATCH 2/5] wip --- build.sh | 2 ++ go.mod | 1 - go.sum | 8 -------- 3 files changed, 2 insertions(+), 9 deletions(-) create mode 100755 build.sh diff --git a/build.sh b/build.sh new file mode 100755 index 00000000000..1a997b3094d --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +#/bin/sh + CGO_ENABLED=1 go build -a -ldflags="-linkmode=internal" . diff --git a/go.mod b/go.mod index f14eaa79df4..7ea294d56b6 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,6 @@ module github.com/junegunn/fzf require ( github.com/gdamore/tcell/v2 v2.5.4 - github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-runewidth v0.0.14 github.com/mattn/go-shellwords v1.0.12 github.com/rivo/uniseg v0.4.4 diff --git a/go.sum b/go.sum index 35a17d199de..4159377c9a1 100644 --- a/go.sum +++ b/go.sum @@ -4,14 +4,6 @@ github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/ github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= From 0538c6bd69c13aecab2d0ab77b8c2cc75618d49e Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Mon, 29 Dec 2025 21:30:17 -0500 Subject: [PATCH 3/5] wip --- src/tui/light_unix.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tui/light_unix.go b/src/tui/light_unix.go index 9a9cf91944d..19fcacc0d67 100644 --- a/src/tui/light_unix.go +++ b/src/tui/light_unix.go @@ -36,6 +36,17 @@ type State struct { termios C.struct_termios } +// Attempt to replace: +// Ref: https://cs.opensource.google/go/x/term/+/refs/tags/v0.38.0:term_unix.go +// func getState(fd int) (*State, error) { +// termios, err := unix.IoctlGetTermios(fd, ioctlReadTermios) +// if err != nil { +// return nil, err +// } +// +// return &State{state{termios: *termios}}, nil +//} + func GetState(fd int) (*State, error) { var t C.struct_termios From c1b25105119d6eb45b573a3a2e706b4494df2746 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Mon, 29 Dec 2025 21:34:09 -0500 Subject: [PATCH 4/5] wip --- build.sh | 6 +++++- src/util/chars.go | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.sh b/build.sh index 1a997b3094d..52a1c8c70b6 100755 --- a/build.sh +++ b/build.sh @@ -1,2 +1,6 @@ #/bin/sh - CGO_ENABLED=1 go build -a -ldflags="-linkmode=internal" . +export CGO_ENABLED=1 +rm -rf /tmp/go-link-* +rm -rf /tmp/go-build* +go clean -r -cache -testcache -modcache +go build -a -ldflags="-linkmode=internal" . diff --git a/src/util/chars.go b/src/util/chars.go index 2b41b602f1f..41de92431e0 100644 --- a/src/util/chars.go +++ b/src/util/chars.go @@ -1,6 +1,5 @@ package util -//import "C" import ( "fmt" "unicode" From 547270f92e0397b3145f9aa655e75599e5adb966 Mon Sep 17 00:00:00 2001 From: Josh Goebel Date: Tue, 30 Dec 2025 05:47:23 -0500 Subject: [PATCH 5/5] wip --- build.sh | 2 +- src/tui/light_unix.go | 59 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/build.sh b/build.sh index 52a1c8c70b6..2bf13d609a1 100755 --- a/build.sh +++ b/build.sh @@ -3,4 +3,4 @@ export CGO_ENABLED=1 rm -rf /tmp/go-link-* rm -rf /tmp/go-build* go clean -r -cache -testcache -modcache -go build -a -ldflags="-linkmode=internal" . +go build -a -buildvcs=false -ldflags="-linkmode=internal" . diff --git a/src/tui/light_unix.go b/src/tui/light_unix.go index 19fcacc0d67..2bb3fb263d0 100644 --- a/src/tui/light_unix.go +++ b/src/tui/light_unix.go @@ -5,6 +5,7 @@ package tui /* #include #include +#include // Simple wrapper to call the ioctl int native_get_winsize(int fd, struct winsize *ws) { @@ -15,6 +16,28 @@ int native_get_winsize(int fd, struct winsize *ws) { int get_terminal_state(int fd, struct termios *t) { return tcgetattr(fd, t); } + +// Helper to manually set the terminal to raw mode using Haiku's C headers +int haiku_make_raw(int fd, struct termios *old) { + struct termios raw; + if (tcgetattr(fd, old) != 0) return -1; + raw = *old; + + // POSIX raw mode flags + raw.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + raw.c_oflag &= ~OPOST; + raw.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + raw.c_cflag &= ~(CSIZE | PARENB); + raw.c_cflag |= CS8; + raw.c_cc[VMIN] = 1; + raw.c_cc[VTIME] = 0; + + return tcsetattr(fd, TCSAFLUSH, &raw); +} + +int haiku_restore(int fd, struct termios *old) { + return tcsetattr(fd, TCSAFLUSH, old); +} */ import "C" import ( @@ -33,9 +56,29 @@ import ( // State wraps the C termios structure to keep it compatible with your existing code. type State struct { + state +} + +type state struct { termios C.struct_termios } +func MakeRaw(fd int) (*State, error) { + var oldState state + if C.haiku_make_raw(C.int(fd), &oldState.termios) != 0 { + return nil, fmt.Errorf("failed to set raw mode on Haiku via CGO") + } + return &State{state: oldState}, nil +} + +func Restore(fd int, oldState *State) error { + //term_state := (*term.State)(unsafe.Pointer(oldState)) + if C.haiku_restore(C.int(fd), &oldState.termios) != 0 { + return fmt.Errorf("haiku: failed to restore terminal") + } + return nil +} + // Attempt to replace: // Ref: https://cs.opensource.google/go/x/term/+/refs/tags/v0.38.0:term_unix.go // func getState(fd int) (*State, error) { @@ -55,7 +98,7 @@ func GetState(fd int) (*State, error) { return nil, errors.New("failed to get terminal state (tcgetattr)") } - return &State{termios: t}, nil + return &State{state{termios: t}}, nil } // Winsize matches the POSIX terminal window size structure @@ -88,12 +131,15 @@ func (r *LightRenderer) fd() int { func (r *LightRenderer) initPlatform() error { fd := r.fd() origState, err := GetState(fd) - if err != nil { +// if err != nil { +// return err +// } + if false { return err } r.origState = (*term.State)(unsafe.Pointer(origState)) - term.MakeRaw(fd) + MakeRaw(fd) return nil } @@ -117,11 +163,14 @@ func openTtyIn() *os.File { } func (r *LightRenderer) setupTerminal() { - term.MakeRaw(r.fd()) + MakeRaw(r.fd()) } func (r *LightRenderer) restoreTerminal() { - term.Restore(r.fd(), r.origState) + + state := (*State)(unsafe.Pointer(r.origState)) + Restore(r.fd(), state) + //Restore(r.fd(), r.origState) } func (r *LightRenderer) updateTerminalSize() {