Skip to content

Instantly share code, notes, and snippets.

@c-bata
Last active May 14, 2019 21:51
Show Gist options
  • Save c-bata/61c3e435c21f42a508a0f6bde139a954 to your computer and use it in GitHub Desktop.
Save c-bata/61c3e435c21f42a508a0f6bde139a954 to your computer and use it in GitHub Desktop.
Read user input from stdin using Kqueue
// kqueue reading sample for https://github.com/c-bata/go-prompt
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
prompt "github.com/c-bata/go-prompt"
"github.com/c-bata/go-prompt/internal/term"
)
const fd = 0
func main() {
if err := term.SetRaw(fd); err != nil {
fmt.Println(err)
return
}
defer term.Restore()
siginput := make(chan struct{})
go func(input chan struct{}) {
kq, err := syscall.Kqueue()
if err != nil {
fmt.Println(err)
return
}
ev1 := syscall.Kevent_t{
Ident: uint64(fd),
Filter: syscall.EVFILT_VNODE,
Flags: syscall.EV_ADD | syscall.EV_ENABLE | syscall.EV_ONESHOT,
Fflags: syscall.NOTE_DELETE | syscall.NOTE_WRITE,
Data: 0,
Udata: nil,
}
for {
events := make([]syscall.Kevent_t, 10)
_, err := syscall.Kevent(kq, []syscall.Kevent_t{ev1}, events, nil)
if err != nil {
panic(err)
}
siginput <- struct{}{}
}
}(siginput)
// Set signal handler
sigquit := make(chan os.Signal, 1)
signal.Notify(sigquit, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
buf := make([]byte, 128)
for {
fmt.Print("> ")
select {
case <-siginput:
n, err := syscall.Read(syscall.Stdin, buf)
if err != nil {
fmt.Printf("[ERROR] cannot read %s", err)
break
}
b := buf[:n]
if key := prompt.GetKey(b); key == prompt.NotDefined {
fmt.Printf("Key '%s' data:'%#v'\n", string(b), b)
} else {
fmt.Printf("Key '%s' data:'%#v'\n", key, b)
if key == prompt.ControlC {
return
}
}
case <-sigquit:
return
}
}
}
@c-bata
Copy link
Author

c-bata commented Dec 20, 2018

sigio

package main

import (
	"fmt"
	"github.com/c-bata/go-prompt/internal/term"
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"github.com/c-bata/go-prompt"
)

const fd = 0

func main() {
	_, _, e := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), uintptr(syscall.F_SETFL),
		uintptr(syscall.O_ASYNC|syscall.O_NONBLOCK))
	if e != 0 {
		fmt.Printf("[ERROR] Cannot set non-blocking mode: %d\n", e)
		return
	}
	_, _, e = syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), uintptr(syscall.F_SETOWN),
		uintptr(syscall.Getpid()))
	if runtime.GOOS != "darwin" && e != 0 {
		fmt.Printf("[ERROR] Cannot set F_SETOWN: %d\n", e)
		return
	}
	defer func() {
		err := syscall.SetNonblock(fd, false)
		if err != nil {
			fmt.Printf("[ERROR] Cannot unset non-blocking mode: %s\n", err)
		}
	}()

	// Set raw mode
	term.SetRaw(fd)
	defer term.Restore()

	// Set signal handler
	sigio := make(chan os.Signal, 1)
	signal.Notify(sigio, syscall.SIGIO)
	sigquit := make(chan os.Signal, 1)
	signal.Notify(sigquit, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGTERM)
	buf := make([]byte, 128)
	for {
		fmt.Print("> ")
		select {
		case <-sigio:
			n, err := syscall.Read(syscall.Stdin, buf)
			if err == syscall.EAGAIN || err == syscall.EWOULDBLOCK {
				continue
			} else if err != nil {
				fmt.Printf("[ERROR] cannot read %s", err)
				break
			}
			b := buf[:n]
			if key := prompt.GetKey(b); key == prompt.NotDefined {
				fmt.Printf("Key '%s' data:'%#v'\n", string(b), b)
			} else {
				fmt.Printf("Key '%s' data:'%#v'\n", key, b)
				if key == prompt.ControlC {
					return
				}
			}
		case <-sigquit:
			return
		}
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment