2022-12-27 23:05:32 +00:00
# Streaming keyboard input
2023-01-22 07:08:48 +00:00
We use the Gorilla websocket library to handle the websocket connection.
2023-01-22 21:46:54 +00:00
Most of the time, we can use sendkeys (which uses libinput) to effeciently press keys. However, if we need to send a character that sendkeys doesn't know about, we can use the xdotool command. xdotool is also useful if one does not want to use root.
2023-01-22 07:08:48 +00:00
xdotool spawns a new process for each keypress, so it's not as effecient as sendkeys.
To specify xdotool usage, the client should send a message with the format `{kb_cmd:xdotool}:message` where message is a utf-8 string.
2022-12-27 23:05:32 +00:00
``` go
--- streaming keyboard input
func clientConnected(w http.ResponseWriter, r *http.Request) {
keyboard, err := sendkeys.NewKBWrapWithOptions(sendkeys.Noisy)
2023-01-22 21:46:54 +00:00
2022-12-27 23:05:32 +00:00
if err != nil {
panic(err)
}
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
defer c.Close()
// get auth token
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
return
}
if auth.CheckAuthToken(string(message)) != nil {
log.Println("invalid token")
2023-01-01 21:25:32 +00:00
c.WriteMessage(websocket.TextMessage, []byte("invalid token"))
2022-12-27 23:05:32 +00:00
return
}
c.WriteMessage(websocket.TextMessage, []byte("authenticated"))
for {
time.Sleep(25 * time.Millisecond)
_, message, err := c.ReadMessage()
if err != nil {
log.Println("read:", err)
break
}
log.Printf("recv: %s", message)
message_string := string(message)
2023-01-22 00:55:58 +00:00
if message_string == "" {
message_string = "\n"
}
2023-01-22 03:27:59 +00:00
2023-01-22 07:08:48 +00:00
@{send keys to system}
2022-12-27 23:05:32 +00:00
}
}
2023-01-22 07:08:48 +00:00
---
```
# Sending the keys
2023-01-23 01:31:21 +00:00
Sending the keys is a bit tricky as we need to manually convert backspace, tab, enter and modifier keys.
2023-01-22 07:08:48 +00:00
``` go
--- send keys to system
2023-01-23 01:31:21 +00:00
// regex if string has characters we need to convert to key presses
characterRegex, _ := regexp.Compile(`[^\x08]\x08|\t|\n`)
2023-01-22 21:46:54 +00:00
doXDoTool := func(command string, keys string)(err error) {
2023-01-23 01:31:21 +00:00
var cmd *exec.Cmd
if command == "type" {
cmd = exec.Command("xdotool", command, "--delay", "25", keys)
} else {
cmd = exec.Command("xdotool", command, keys)
}
2023-01-22 21:46:54 +00:00
return cmd.Run()
}
2023-01-22 07:08:48 +00:00
if strings.HasPrefix(message_string, "{kb_cmd:xdotool}:") {
message_string = strings.TrimPrefix(message_string, "{kb_cmd:xdotool}:")
if message_string == "" {
message_string = "\n"
}
2023-01-22 21:46:54 +00:00
if characterRegex.MatchString(message_string) {
for _, character := range message_string {
charString := string(character)
if charString == "\n" {
charString = "Enter"
} else if charString == "\t" {
charString = "Tab"
} else if charString == "\b" {
charString = "BackSpace"
} else{
doXDoTool("type", charString)
continue
}
2023-01-23 01:31:21 +00:00
// key is required for special characters
2023-01-22 21:46:54 +00:00
err = doXDoTool("key", charString)
2023-01-23 01:31:21 +00:00
continue
2023-01-22 21:46:54 +00:00
}
continue
2023-01-23 01:31:21 +00:00
} else {
doXDoTool("type", message_string)
2023-01-22 07:08:48 +00:00
}
continue
2023-01-27 23:22:02 +00:00
} else if strings.HasPrefix(message_string, "{kb_cmd:xdotoolk}:") {
message_string = strings.TrimPrefix(message_string, "{kb_cmd:xdotoolk}:")
if message_string == "" {
message_string = "\n"
}
if characterRegex.MatchString(message_string) {
for _, character := range message_string {
charString := string(character)
if charString == "\n" {
charString = "Enter"
} else if charString == "\t" {
charString = "Tab"
} else if charString == "\b" {
charString = "BackSpace"
} else{
doXDoTool("key", charString)
continue
}
// key is required for special characters
err = doXDoTool("key", charString)
continue
}
continue
} else {
doXDoTool("key", message_string)
}
continue
2023-01-22 07:08:48 +00:00
}
2023-01-23 01:31:21 +00:00
if characterRegex.MatchString(message_string) {
for _, character := range message_string {
charString := string(character)
if charString == "\n" {
keyboard.Enter()
continue
}
if charString == "\t" {
keyboard.Tab()
continue
}
if charString == "\b" {
keyboard.BackSpace()
continue
}
err = keyboard.Type(charString)
if err != nil {
log.Println("type:", err)
}
2023-01-22 07:08:48 +00:00
continue
}
2023-01-23 01:31:21 +00:00
continue
}
err = keyboard.Type(message_string)
if err != nil {
log.Println("type:", err)
2023-01-22 07:08:48 +00:00
}
2023-01-22 21:46:54 +00:00
---
2023-01-22 07:08:48 +00:00
```