From 5032b04a4799d9b86de41c43132ed3f126b2e587 Mon Sep 17 00:00:00 2001 From: Noah Metz Date: Tue, 23 Apr 2024 22:10:40 -0600 Subject: [PATCH] Added input and libc files, updated basic example --- examples/basic.go | 40 ++++++++++++++++++++++++++++++---------- go.mod | 7 +++---- input.go | 41 +++++++++++++++++++++++++++++++++++++++++ libc.go | 23 +++++++++++++++++++++++ ncurses.go | 41 ++++++++++++++++++++++++++++++++--------- ncurses_darwin.go | 1 + ncurses_test.go | 45 ++++++++++++++++++++++++++++++++------------- 7 files changed, 162 insertions(+), 36 deletions(-) create mode 100644 input.go create mode 100644 libc.go diff --git a/examples/basic.go b/examples/basic.go index 5e438d5..43b529f 100644 --- a/examples/basic.go +++ b/examples/basic.go @@ -1,23 +1,43 @@ package main import ( - "fmt" + "os" + "os/signal" + "syscall" "git.metznet.ca/MetzNet/go-ncurses" ) func main() { - err := ncurses.Init() - if err != nil { - panic(err) - } + ncurses.SetLocale(0, "") + input, input_active := ncurses.UTF8Listener(100, os.Stdin) - window := ncurses.InitScr.Load()() + os_sigs := make(chan os.Signal, 1) + signal.Notify(os_sigs, syscall.SIGINT, syscall.SIGABRT) - x := ncurses.GetMaxX.Load()(window) - y := ncurses.GetMaxY.Load()(window) + window := ncurses.InitScr() + ret := ncurses.IdlOK(window, true) + if ret != 0 { + panic(ret) + } + ret = ncurses.ScrollOk(window, true) + if ret != 0 { + panic(ret) + } + ncurses.WRefresh(window) - fmt.Printf("%d x %d\n", x, y) + active := true + for active { + select { + case bytes := <-input: + ncurses.WAddStr(window, string(bytes)) + ncurses.WRefresh(window) + case <-os_sigs: + active = false + } + } - ncurses.EndWin.Load()() + ncurses.EndWin() + input_active.Store(false) + close(input) } diff --git a/go.mod b/go.mod index 9569e29..42bd381 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,6 @@ module git.metznet.ca/MetzNet/go-ncurses go 1.22.2 -require ( - github.com/ebitengine/purego v0.7.1 // indirect - golang.org/x/sys v0.7.0 // indirect -) +require github.com/ebitengine/purego v0.7.1 + +require golang.org/x/sys v0.7.0 // indirect diff --git a/input.go b/input.go new file mode 100644 index 0000000..de5cf4e --- /dev/null +++ b/input.go @@ -0,0 +1,41 @@ +package ncurses + +import ( + "os" + "sync/atomic" +) + +func bitmatch(b byte, pattern byte, length int) bool { + mask := ^(byte(1 << (8 - length)) - 1) + return (b ^ pattern) & mask == 0 +} + +func UTF8Listen(file *os.File, channel chan []byte, active *atomic.Bool) { + b := [4]byte{} + for active.Load() { + file.Read(b[0:1]) + if bitmatch(b[0], 0b00000000, 1) { + channel <- b[0:1] + } else if bitmatch(b[0], 0b11000000, 3) { + file.Read(b[1:2]) + channel <- b[0:2] + } else if bitmatch(b[0], 0b11100000, 4) { + file.Read(b[1:3]) + channel <- b[0:3] + } else if bitmatch(b[0], 0b11110000, 5) { + file.Read(b[1:4]) + channel <- b[0:4] + } + } +} + +// To cleanup, set active to false then close the channel +func UTF8Listener(buffer int, file *os.File) (chan []byte, *atomic.Bool) { + channel := make(chan []byte, buffer) + active := new(atomic.Bool) + active.Store(true) + + go UTF8Listen(file, channel, active) + + return channel, active +} diff --git a/libc.go b/libc.go new file mode 100644 index 0000000..6a01ce1 --- /dev/null +++ b/libc.go @@ -0,0 +1,23 @@ +package ncurses + +import ( + "github.com/ebitengine/purego" +) + +var libc uintptr = 0 +func libcFunction[T any](name string) T { + if libc == 0 { + var err error + libc, err = purego.Dlopen(LIBC_PATH, purego.RTLD_GLOBAL | purego.RTLD_NOW) + if err != nil { + panic(err) + } + } + + var tmp = new(T) + purego.RegisterLibFunc(tmp, libc, name) + return *tmp +} + +type setlocaleFP func(category int, locale string) +var SetLocale = libcFunction[setlocaleFP]("setlocale") diff --git a/ncurses.go b/ncurses.go index c857db1..1926f22 100644 --- a/ncurses.go +++ b/ncurses.go @@ -1,24 +1,21 @@ package ncurses import ( - "sync/atomic" - "github.com/ebitengine/purego" ) -var ncurses atomic.Value - +var ncurses uintptr = 0 func ncursesFunction[T any](name string) T { - if ncurses.Load() == nil { - lib, err := purego.Dlopen(LIBRARY_PATH, purego.RTLD_GLOBAL | purego.RTLD_LAZY) + if ncurses == 0 { + var err error + ncurses, err = purego.Dlopen(LIBRARY_PATH, purego.RTLD_GLOBAL | purego.RTLD_LAZY) if err != nil { panic(err) } - ncurses.CompareAndSwap(nil, lib) } var tmp = new(T) - purego.RegisterLibFunc(tmp, ncurses.Load().(uintptr), name) + purego.RegisterLibFunc(tmp, ncurses, name) return *tmp } @@ -51,5 +48,31 @@ var WAddNStr = ncursesFunction[waddstrFP]("waddnstr") var GetMaxY = ncursesFunction[winFP]("getmaxy") var GetMaxX = ncursesFunction[winFP]("getmaxx") -type mvwaddstrFP func(window Window, x, y int, str string) int +type mvwaddstrFP func(window Window, y, x int, str string) int var MvWAddStr = ncursesFunction[mvwaddstrFP]("mvwaddstr") + +type mvwaddchFP func(window Window, y, x int, char rune) int +var MvWAddCh = ncursesFunction[mvwaddchFP]("mvwaddch") + +type waddchFP func(window Window, char rune) int +var WAddCh = ncursesFunction[waddchFP]("waddch") + +type init_colorFP func(color, r, g, b int16) int +var InitColor = ncursesFunction[init_colorFP]("init_color") + +type init_pairFP func(pair, fg, bg int16) int +var InitPair = ncursesFunction[init_pairFP]("init_pair") + +var StartColor = ncursesFunction[voidFP]("start_color") + +type checkFP func() bool +var HasColors = ncursesFunction[checkFP]("has_colors") + +type setflagFP func(window Window, bf bool) int +var ClearOK = ncursesFunction[setflagFP]("clearok") +var IdlOK = ncursesFunction[setflagFP]("idlok") +var ScrollOk = ncursesFunction[setflagFP]("scrollok") + +type wscrlFP func(window Window, n int) int +var WScrl = ncursesFunction[wscrlFP]("wscrl") + diff --git a/ncurses_darwin.go b/ncurses_darwin.go index ea15538..0638b81 100644 --- a/ncurses_darwin.go +++ b/ncurses_darwin.go @@ -1,3 +1,4 @@ package ncurses const LIBRARY_PATH = "libncurses.dylib" +const LIBC_PATH = "libSystem.B.dylib" diff --git a/ncurses_test.go b/ncurses_test.go index fa4ae75..a9d718d 100644 --- a/ncurses_test.go +++ b/ncurses_test.go @@ -5,39 +5,58 @@ import ( ) func StartTest(t *testing.T) Window { - err := Init() - if err != nil { - t.Fatalf("Init err - %s", err) - } - - window := InitScr.Load()() + window := InitScr() return window } func EndTest(t *testing.T) { - ret := EndWin.Load()() + ret := EndWin() if ret != 0 { - t.Fatalf("EndWin result - 0x%x", ret) + t.Fatalf("EndWin result - 0x%02x", ret) } } func TestInitScr(t *testing.T) { - window := StartTest(t) - t.Logf("InitScr result - 0x%x", window) + StartTest(t) EndTest(t) } func TestNewWin(t *testing.T) { StartTest(t) - window := NewWin.Load()(10, 10, 0, 0) + window := NewWin(10, 10, 0, 0) t.Logf("NewWin result - 0x%x", window) - ret := DelWin.Load()(window) + ret := DelWin(window) if ret != 0 { - t.Fatalf("DelWin result - 0x%x", ret) + t.Fatalf("DelWin result - 0x%02x", ret) } EndTest(t) } +func TestColors(t *testing.T) { + StartTest(t) + + ret := StartColor() + if ret != 0 { + t.Fatalf("StartColor result - 0x%02x", ret) + } + + ret = InitColor(0, 1000, 0, 0) + if ret != 0 { + t.Fatalf("InitColor result - 0x%02x", ret) + } + + ret = InitColor(1, 0, 1000, 0) + if ret != 0 { + t.Fatalf("InitColor result - 0x%02x", ret) + } + + ret = InitPair(1, 0, 1) + if ret != 0 { + t.Fatalf("InitPair result - 0x%02x", ret) + } + + EndTest(t) +}