Skip to content

Commit

Permalink
Linux Updates
Browse files Browse the repository at this point in the history
- Moved linux keyboard detection into it's own package
- Added support for linux Repeat key events
  • Loading branch information
nathan-fiscaletti committed Oct 28, 2024
1 parent c9c0e41 commit 9d31210
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 79 deletions.
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ This is a simple implementation of a cross-platform key logger in Go.

## Supported Platforms

- Windows
- Linux
- **Windows**

Implemented through the `SetWindowsHookExW(WH_KEYBOARD_LL)` API.

- **Linux**

Implemented through the `/dev/input/event/` filesystem API.

(See [dev-input](https://github.com/nathan-fiscaletti/dev-input) for more information.)

## Usage

Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ module github.com/nathan-fiscaletti/key-logger

go 1.23.2

require github.com/samber/lo v1.47.0
require (
github.com/nathan-fiscaletti/dev-input v1.0.0
github.com/samber/lo v1.47.0
)

require golang.org/x/text v0.16.0 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
github.com/nathan-fiscaletti/dev-input v1.0.0 h1:FYkL6NMRSmXWyqSoMS0G9WLSYIUy4+so4uv5lx/iN48=
github.com/nathan-fiscaletti/dev-input v1.0.0/go.mod h1:UTmQgwEnWYWfg3VGWLP0toMXX+gpVYbd2Fai8qDu8Vs=
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
Expand Down
108 changes: 32 additions & 76 deletions pkg/keyboard/keyboard_linux.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,19 @@
package keyboard

import (
"bytes"
"context"
"encoding/binary"
"fmt"
"io"
"os"
"strings"
"time"

input "github.com/nathan-fiscaletti/dev-input"

"github.com/nathan-fiscaletti/key-logger/pkg/key"
)

const eventSize = 24 // Size of each event struct in bytes

type inputEvent struct {
Time [2]uint64 // Timestamp (seconds and microseconds)
Type uint16 // Event type
Code uint16 // Event code
Value int32 // Event value (1 for key down, 0 for key up)
}
// On linux there is a special event type for repeat events.
const KeyboardEventTypeRepeat EventType = "repeat"

// const eventSize = 24 // Size of each event struct in bytes
func init() {
keyboard = keyboardLinux{}
}
Expand All @@ -34,77 +26,41 @@ func (k keyboardLinux) Events(ctx context.Context) (chan Event, error) {
if k.eventChan == nil {
k.eventChan = make(chan Event)

device, err := findKeyboardDevice()
if err != nil {
return nil, err
}
file, err := os.Open(device)
keyboards, err := input.ListKeyboards()
if err != nil {
return nil, err
}

go func() {
defer file.Close()

for {
event := inputEvent{}
buffer := make([]byte, eventSize)
_, err := file.Read(buffer)
if err != nil {
close(k.eventChan)
k.eventChan = nil
return
}

err = binary.Read(bytes.NewBuffer(buffer), binary.LittleEndian, &event)
if err != nil {
close(k.eventChan)
k.eventChan = nil
return
}
if len(keyboards) < 1 {
return nil, fmt.Errorf("no keyboards found")
}

if event.Type == 1 {
eventType := KeyboardEventTypeUp
if event.Value == 1 {
eventType = KeyboardEventTypeDown
}
k.eventChan <- Event{
Key: key.FindKeyCode(uint32(event.Code)),
EventType: eventType,
Timestamp: time.Unix(int64(event.Time[0]), int64(event.Time[1])),
for _, keyboard := range keyboards {
go func() {
err := keyboard.Listen(ctx, func(e input.Event) {
if e.Type == input.EV_TYPE_KEY {
var eventType EventType
switch e.Value {
case 1:
eventType = KeyboardEventTypeDown
case 2:
eventType = KeyboardEventTypeRepeat
default:
eventType = KeyboardEventTypeUp
}
k.eventChan <- Event{
Key: key.FindKeyCode(uint32(e.Code)),
EventType: eventType,
Timestamp: time.Unix(int64(e.Time[0]), int64(e.Time[1])),
}
}
})
if err != nil {
fmt.Printf("Error listening for keyboard events: %v\n", err)
}
}
}()
}

return k.eventChan, nil
}

func findKeyboardDevice() (string, error) {
for i := 0; i < 255; i++ {
f, err := os.Open(fmt.Sprintf("/sys/class/input/event%d/device/name", i))
if err != nil {
return "", err
}

var data []byte
data, err = io.ReadAll(f)
if err != nil {
return "", err
}
content := string(data)

if strings.Contains(strings.ToLower(content), "mouse") {
continue
}

for _, identifier := range []string{"keyboard", "mx keys"} {
if strings.Contains(strings.ToLower(content), identifier) {
return fmt.Sprintf("/dev/input/event%d", i), nil
}
}()
}
}

return "", nil
return k.eventChan, nil
}

0 comments on commit 9d31210

Please sign in to comment.