go-keyboard-capture
Keyboard Capture API
A simple and flexible Go API for capturing keyboard events in real-time, including special keys like arrows. Works on Linux and macOS (raw mode not supported on Windows).
Features
- ✅ Capture individual key presses
- ✅ Support for arrow keys and special keys
- ✅ Context-based cancellation for clean shutdown
- ✅ Non-blocking event-driven architecture
- ✅ Easy integration into other projects
Installation
go get github.com/FireAnomaly/go-keyboard-capture
Or copy keyboard.go into your project.
Quick Start
package main
import (
"context"
"fmt"
"github.com/FireAnomaly/keyboard-capture"
)
func main() {
events := make(chan main.KeyEvent)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := main.CaptureKeyboard(ctx, events); err != nil {
fmt.Println("Error:", err)
return
}
for event := range events {
if event.Key != 0 {
fmt.Printf("Key: %c\n", event.Key)
}
}
}
API Reference
Types
KeyEvent
Represents a keyboard event.
type KeyEvent struct {
Key rune // Character for printable keys (e.g., 'a', '1')
Code int // Code for special keys (0 for regular keys)
}
Special Key Codes:
1 - Up arrow
2 - Down arrow
3 - Right arrow
4 - Left arrow
5 - Clear line (Ctrl+L)
Functions
CaptureKeyboard(ctx context.Context, events chan<- KeyEvent) error
Starts capturing keyboard input in a goroutine and sends events to the provided channel.
Parameters:
ctx - Context for cancellation control
events - Channel to receive keyboard events
Returns:
error - Returns error if raw mode cannot be enabled (e.g., on Windows)
Example:
events := make(chan KeyEvent)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := CaptureKeyboard(ctx, events); err != nil {
log.Fatal(err)
}
for event := range events {
// Handle events
}
Complete Examples
Example 1: Basic Usage
package main
import (
"context"
"fmt"
)
func main() {
events := make(chan KeyEvent)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
CaptureKeyboard(ctx, events)
for event := range events {
if event.Key == 'q' {
cancel()
break
}
fmt.Printf("Pressed: %c\n", event.Key)
}
}
Example 2: With Signal Handling
package main
import (
"context"
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
events := make(chan KeyEvent)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Handle Ctrl+C
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
fmt.Println("\nShutting down...")
cancel()
}()
if err := CaptureKeyboard(ctx, events); err != nil {
fmt.Println("Error:", err)
return
}
for event := range events {
if event.Key != 0 {
fmt.Printf("Key: %c\n", event.Key)
} else {
switch event.Code {
case 1: fmt.Println("↑")
case 2: fmt.Println("↓")
case 3: fmt.Println("→")
case 4: fmt.Println("←")
case 5: fmt.Println("Clear Line")
}
}
}
}
package main
import (
"context"
"fmt"
"time"
)
type Position struct {
X, Y int
}
func main() {
events := make(chan KeyEvent)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
CaptureKeyboard(ctx, events)
pos := Position{X: 5, Y: 5}
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
for {
select {
case event, ok := <-events:
if !ok {
return
}
switch event.Code {
case 1: pos.Y-- // Up
case 2: pos.Y++ // Down
case 3: pos.X++ // Right
case 4: pos.X-- // Left
}
if event.Key == 'q' {
cancel()
return
}
case <-ticker.C:
fmt.Printf("\rPosition: (%d, %d) ", pos.X, pos.Y)
}
}
}
| Platform |
Support |
Notes |
| Linux |
✅ Yes |
Full support |
| macOS |
✅ Yes |
Full support |
| Windows |
❌ No |
Raw mode not available via stty |
Best Practices
- Always use context for cancellation: Never rely on the deprecated
Stop() function
- Handle signals: Implement SIGINT/SIGTERM handlers for clean shutdown
- Defer cancel(): Always defer the context cancel function
- Close channels: The API automatically closes the events channel when done
- Error handling: Check the error returned by
CaptureKeyboard()
Troubleshooting
Terminal doesn't respond after program crash
If your program crashes without restoring terminal mode, run:
stty sane
Keys not being captured
Ensure your terminal supports raw mode and the program has proper stdin access.
License
MIT License - Feel free to use in your projects!
Contributing
Contributions are welcome! Please submit issues and pull requests.