package handler import ( "encoding/json" "errors" "fmt" "os/exec" "regexp" "strconv" "strings" "time" "github.com/gorilla/websocket" "github.com/labstack/echo/v4" "gitnet.fr/deblan/remote-i3wm-go/internal/action" "gitnet.fr/deblan/remote-i3wm-go/internal/pointer" ) type Message struct { Type string `json:type` } type SimpleMessageData struct { Type string `json:type` Value string `json:value` } type PointerMessageData struct { X string `json:x` Y string `json:y` Click string `json:click` } type MessagesData struct { Type string `json:type` Value []SimpleMessageData `json:value` } type ScreenshotMessageData struct { Quality string `json:quality` Pointer bool `json:pointer` } type MessageResponse struct { Type string `json:"type"` Value string `json:"value"` } func getSimpleMessageValue(msg []byte) string { data := SimpleMessageData{} json.Unmarshal([]byte(msg), &data) return data.Value } func sendMessageResponse(ws *websocket.Conn, r MessageResponse) { value, _ := json.Marshal(r) ws.WriteMessage(websocket.TextMessage, value) } func createActions() action.Actions { actions := action.Actions{ Functions: make(map[string]func(ws *websocket.Conn, msg []byte) error), } actions.Add("pointer", func(ws *websocket.Conn, msg []byte) error { data := PointerMessageData{} json.Unmarshal([]byte(msg), &data) if data.Click != "" { keys := make(map[string]string) keys["left"] = "1" keys["middle"] = "2" keys["right"] = "3" key, exists := keys[data.Click] if !exists { return errors.New("Invalid value") } cmd := exec.Command("xdotool", "click", key) return cmd.Run() } currentX, currentY := pointer.Positions() newX, _ := strconv.ParseFloat(data.X, 32) newY, _ := strconv.ParseFloat(data.Y, 32) x := currentX + newX*2.5 y := currentY + newY*2.5 cmd := exec.Command("xdotool", "mousemove", fmt.Sprintf("%.0f", x), fmt.Sprintf("%.0f", y)) return cmd.Run() }) actions.Add("scroll", func(ws *websocket.Conn, msg []byte) error { value := getSimpleMessageValue(msg) key := "" if value == "down" { key = "5" } else if value == "up" { key = "4" } else { return errors.New("Invalid value") } for i := 0; i < 2; i++ { cmd := exec.Command("xdotool", "click", key) cmd.Run() } return nil }) actions.Add("workspace", func(ws *websocket.Conn, msg []byte) error { value := getSimpleMessageValue(msg) if value == "" { return errors.New("Invalid value") } cmd := exec.Command("i3-msg", fmt.Sprintf("workspace \"%s\"", value)) return cmd.Run() }) actions.Add("volume", func(ws *websocket.Conn, msg []byte) error { value := getSimpleMessageValue(msg) if value == "value" { cmd := exec.Command("amixer", "get", "Master") output, _ := cmd.Output() r := regexp.MustCompile(`\[([0-9]+%)\]`) value := string(r.Find(output)) value = strings.Replace(value, "[", "", 1) value = strings.Replace(value, "]", "", 1) value = strings.Replace(value, "%", "", 1) if value != "" { sendMessageResponse(ws, MessageResponse{ Type: "volume", Value: value, }) } return nil } if value == "up" { cmd := exec.Command("amixer", "set", "Master", "2%+") sendMessageResponse(ws, MessageResponse{ Type: "response", Value: "Volume up", }) return cmd.Run() } if value == "down" { cmd := exec.Command("amixer", "set", "Master", "2%-") sendMessageResponse(ws, MessageResponse{ Type: "response", Value: "Volume down", }) return cmd.Run() } cmd := exec.Command("amixer", "set", "Master", fmt.Sprintf("%s%%", value)) sendMessageResponse(ws, MessageResponse{ Type: "response", Value: fmt.Sprintf("Volume set to %s%%", value), }) return cmd.Run() }) actions.Add("media", func(ws *websocket.Conn, msg []byte) error { value := getSimpleMessageValue(msg) if value == "" { return errors.New("Invalid value") } var arg string if value == "playpause" { arg = "play-pause" } else if value == "next" { arg = "next" } else if value == "prev" { arg = "previous" } else { return errors.New("Invalid value") } cmd := exec.Command("playerctl", "-p", "spotify", arg) err := cmd.Run() if err != nil { return err } time.Sleep(400 * time.Millisecond) cmd = exec.Command("playerctl", "-p", "spotify", "status") output, err := cmd.Output() value = strings.TrimSpace(string(output)) if err != nil { return err } if value == "Playing" { cmd = exec.Command("playerctl", "-p", "spotify", "metadata", "xesam:title") output, err := cmd.Output() value = strings.TrimSpace(string(output)) if err != nil { return err } sendMessageResponse(ws, MessageResponse{ Type: "response", Value: fmt.Sprintf("Playing: %s", value), }) } else { sendMessageResponse(ws, MessageResponse{ Type: "response", Value: "Paused", }) } return nil }) actions.Add("keys", func(ws *websocket.Conn, msg []byte) error { value := strings.TrimSpace(getSimpleMessageValue(msg)) if value == "" { return errors.New("Invalid value") } keys := []string{} for _, key := range strings.Split(value, ",") { if key == "win" { key = "super" } else if key == "ctrl" { key = "Control_L" } else if key == "alt" { key = "Alt_L" } else if key == "tab" { key = "Tab" } if key != "" { keys = append(keys, key) } } if len(keys) == 0 { return errors.New("Invalid value") } cmd := exec.Command("xdotool", "key", strings.Join(keys, "+")) return cmd.Run() }) actions.Add("key", func(ws *websocket.Conn, msg []byte) error { value := strings.TrimSpace(getSimpleMessageValue(msg)) keys := make(map[string]string) keys["up"] = "Up" keys["down"] = "Down" keys["left"] = "Left" keys["right"] = "Right" keys["tab"] = "Tab" keys["backspace"] = "BackSpace" keys["enter"] = "Return" keys["space"] = "space" keys["escape"] = "Escape" key, exists := keys[value] if !exists { return errors.New("Invalid value") } cmd := exec.Command("xdotool", "key", key) return cmd.Run() }) actions.Add("text", func(ws *websocket.Conn, msg []byte) error { value := strings.TrimSpace(getSimpleMessageValue(msg)) if value == "" { return errors.New("Invalid value") } cmd := exec.Command("xdotool", "type", value) return cmd.Run() }) actions.Add("messages", func(ws *websocket.Conn, msg []byte) error { data := MessagesData{} json.Unmarshal([]byte(msg), &data) for _, value := range data.Value { msg, _ := json.Marshal(value) if actions.Has(value.Type) { actions.Exec(value.Type, ws, msg) time.Sleep(400 * time.Millisecond) } } return nil }) return actions } var ( upgrader = websocket.Upgrader{} actions = createActions() ) func WsHandler(c echo.Context) error { ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil) if err != nil { return err } defer ws.Close() for { _, msg, err := ws.ReadMessage() if err != nil { ws.Close() fmt.Printf("%+v\n", "Connection closed") return err } message := Message{} json.Unmarshal([]byte(msg), &message) if message.Type != "" && actions.Has(message.Type) { actions.Exec(message.Type, ws, msg) } sendMessageResponse(ws, MessageResponse{ Type: "statement", Value: "end", }) } return nil }