From 2d11b4fa561abdcdd84aaedd4aaeb63ddb61f128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= Date: Thu, 28 Jan 2021 22:51:55 +0100 Subject: [PATCH] xclip multiple clipboard content types. --- internal/api/room/clipboard.go | 48 +++++++++++++++++-- internal/api/room/handler.go | 7 ++- internal/desktop/clipboard.go | 62 ++++++++++++++++++++++--- internal/types/desktop.go | 9 +++- internal/websocket/handler/clipboard.go | 2 +- internal/websocket/manager.go | 2 +- 6 files changed, 112 insertions(+), 18 deletions(-) diff --git a/internal/api/room/clipboard.go b/internal/api/room/clipboard.go index c09e11ef..7731e5b3 100644 --- a/internal/api/room/clipboard.go +++ b/internal/api/room/clipboard.go @@ -7,11 +7,22 @@ import ( ) type ClipboardPayload struct { - Text string `json:"text"` + Text string `json:"text,omitempty"` + HTML string `json:"html,omitempty"` } -func (h *RoomHandler) clipboardRead(w http.ResponseWriter, r *http.Request) { - text, err := h.desktop.ReadClipboard() +func (h *RoomHandler) clipboardGetTargets(w http.ResponseWriter, r *http.Request) { + targets, err := h.desktop.ClipboardGetTargets() + if err != nil { + utils.HttpInternalServerError(w, err) + return + } + + utils.HttpSuccess(w, targets) +} + +func (h *RoomHandler) clipboardGetPlainText(w http.ResponseWriter, r *http.Request) { + text, err := h.desktop.ClipboardGetPlainText() if err != nil { utils.HttpInternalServerError(w, err) return @@ -22,13 +33,40 @@ func (h *RoomHandler) clipboardRead(w http.ResponseWriter, r *http.Request) { }) } -func (h *RoomHandler) clipboardWrite(w http.ResponseWriter, r *http.Request) { +func (h *RoomHandler) clipboardSetPlainText(w http.ResponseWriter, r *http.Request) { data := &ClipboardPayload{} if !utils.HttpJsonRequest(w, r, data) { return } - err := h.desktop.WriteClipboard(data.Text) + err := h.desktop.ClipboardSetPlainText(data.Text) + if err != nil { + utils.HttpInternalServerError(w, err) + return + } + + utils.HttpSuccess(w) +} + +func (h *RoomHandler) clipboardGetRichText(w http.ResponseWriter, r *http.Request) { + html, err := h.desktop.ClipboardGetRichText() + if err != nil { + utils.HttpInternalServerError(w, err) + return + } + + utils.HttpSuccess(w, ClipboardPayload{ + HTML: html, + }) +} + +func (h *RoomHandler) clipboardSetRichText(w http.ResponseWriter, r *http.Request) { + data := &ClipboardPayload{} + if !utils.HttpJsonRequest(w, r, data) { + return + } + + err := h.desktop.ClipboardSetRichText(data.HTML) if err != nil { utils.HttpInternalServerError(w, err) return diff --git a/internal/api/room/handler.go b/internal/api/room/handler.go index de9485a0..ba617c59 100644 --- a/internal/api/room/handler.go +++ b/internal/api/room/handler.go @@ -38,8 +38,11 @@ func (h *RoomHandler) Route(r chi.Router) { }) r.With(auth.HostsOnly).Route("/clipboard", func(r chi.Router) { - r.Get("/", h.clipboardRead) - r.Post("/", h.clipboardWrite) + r.Get("/targets", h.clipboardGetTargets) + r.Get("/text", h.clipboardGetPlainText) + r.Post("/text", h.clipboardSetPlainText) + r.Get("/html", h.clipboardGetRichText) + r.Post("/html", h.clipboardSetRichText) }) r.Route("/keyboard", func(r chi.Router) { diff --git a/internal/desktop/clipboard.go b/internal/desktop/clipboard.go index 6e113bc2..8619229e 100644 --- a/internal/desktop/clipboard.go +++ b/internal/desktop/clipboard.go @@ -7,8 +7,8 @@ import ( "strings" ) -func (manager *DesktopManagerCtx) ReadClipboard() (string, error) { - cmd := exec.Command("xclip", "-selection", "clipboard", "-o") +func (manager *DesktopManagerCtx) ClipboardGetBinary(mime string) ([]byte, error) { + cmd := exec.Command("xclip", "-selection", "clipboard", "-o", "-t", mime) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout @@ -17,15 +17,15 @@ func (manager *DesktopManagerCtx) ReadClipboard() (string, error) { err := cmd.Run() if err != nil { msg := strings.TrimSpace(string(stderr.Bytes())) - return "", fmt.Errorf("%s", msg) + return nil, fmt.Errorf("%s", msg) } - return string(stdout.Bytes()), nil + return stdout.Bytes(), nil } -func (manager *DesktopManagerCtx) WriteClipboard(data string) error { - cmd := exec.Command("xclip", "-selection", "clipboard", "-i") - cmd.Stdin = strings.NewReader(data) +func (manager *DesktopManagerCtx) ClipboardSetBinary(mime string, data []byte) error { + cmd := exec.Command("xclip", "-selection", "clipboard", "-i", "-t", mime) + cmd.Stdin = bytes.NewReader(data) var stderr bytes.Buffer cmd.Stderr = &stderr @@ -38,3 +38,51 @@ func (manager *DesktopManagerCtx) WriteClipboard(data string) error { return nil } + +func (manager *DesktopManagerCtx) ClipboardGetTargets() ([]string, error) { + cmd := exec.Command("xclip", "-selection", "clipboard", "-o", "-t", "TARGETS") + + var stdout, stderr bytes.Buffer + cmd.Stdout = &stdout + cmd.Stderr = &stderr + + err := cmd.Run() + if err != nil { + msg := strings.TrimSpace(string(stderr.Bytes())) + return nil, fmt.Errorf("%s", msg) + } + + var response []string + targets := strings.Split(string(stdout.Bytes()), "\n") + for _, target := range targets { + if target == "" { + continue + } + + if !strings.Contains(target, "/") { + continue + } + + response = append(response, target) + } + + return response, nil +} + +func (manager *DesktopManagerCtx) ClipboardGetPlainText() (string, error) { + bytes, err := manager.ClipboardGetBinary("STRING") + return string(bytes), err +} + +func (manager *DesktopManagerCtx) ClipboardSetPlainText(data string) error { + return manager.ClipboardSetBinary("STRING", []byte(data)) +} + +func (manager *DesktopManagerCtx) ClipboardGetRichText() (string, error) { + bytes, err := manager.ClipboardGetBinary("text/html") + return string(bytes), err +} + +func (manager *DesktopManagerCtx) ClipboardSetRichText(data string) error { + return manager.ClipboardSetBinary("text/html", []byte(data)) +} diff --git a/internal/types/desktop.go b/internal/types/desktop.go index 6e28ea1e..61db56f5 100644 --- a/internal/types/desktop.go +++ b/internal/types/desktop.go @@ -67,8 +67,13 @@ type DesktopManager interface { OnEventError(listener func(error_code uint8, message string, request_code uint8, minor_code uint8)) // clipboard - ReadClipboard() (string, error) - WriteClipboard(data string) error + ClipboardGetBinary(mime string) ([]byte, error) + ClipboardSetBinary(mime string, data []byte) error + ClipboardGetTargets() ([]string, error) + ClipboardGetPlainText() (string, error) + ClipboardSetPlainText(data string) error + ClipboardGetRichText() (string, error) + ClipboardSetRichText(data string) error // drop DropFiles(x int, y int, files []string) bool diff --git a/internal/websocket/handler/clipboard.go b/internal/websocket/handler/clipboard.go index 65751d23..69e21dcf 100644 --- a/internal/websocket/handler/clipboard.go +++ b/internal/websocket/handler/clipboard.go @@ -16,5 +16,5 @@ func (h *MessageHandlerCtx) clipboardSet(session types.Session, payload *message return nil } - return h.desktop.WriteClipboard(payload.Text) + return h.desktop.ClipboardSetPlainText(payload.Text) } diff --git a/internal/websocket/manager.go b/internal/websocket/manager.go index f461a13f..eb35a0a0 100644 --- a/internal/websocket/manager.go +++ b/internal/websocket/manager.go @@ -117,7 +117,7 @@ func (ws *WebSocketManagerCtx) Start() { return } - text, err := ws.desktop.ReadClipboard() + text, err := ws.desktop.ClipboardGetPlainText() if err != nil { ws.logger.Warn().Err(err).Msg("could not get clipboard content") }