Pārlūkot izejas kodu

feat(clipboard): add wayland support (#657)

Signed-off-by: xqrs <3bd3bdr@gmail.com>
Co-authored-by: Ayyan <ayn2op@gmail.com>
xqrs 4 mēneši atpakaļ
vecāks
revīzija
9a83762580

+ 1 - 4
README.md

@@ -44,10 +44,7 @@ go build .
 
 ### Wayland clipboard support
 
-`x11-dev` is required for X11 clipboard compatibility:
-
-- Ubuntu: `apt install xwayland`
-- Arch Linux: `pacman -S xorg-xwayland`
+`wl-clipboard` is required for clipboard support.
 
 ## Usage
 

+ 1 - 1
cmd/application.go

@@ -3,11 +3,11 @@ package cmd
 import (
 	"log/slog"
 
+	"github.com/ayn2op/discordo/internal/clipboard"
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/login"
 	"github.com/ayn2op/tview"
 	"github.com/gdamore/tcell/v3"
-	"golang.design/x/clipboard"
 )
 
 type application struct {

+ 1 - 1
cmd/guilds_tree.go

@@ -6,6 +6,7 @@ import (
 	"log/slog"
 	"slices"
 
+	"github.com/ayn2op/discordo/internal/clipboard"
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/ui"
 	"github.com/ayn2op/tview"
@@ -13,7 +14,6 @@ import (
 	"github.com/diamondburned/arikawa/v3/gateway"
 	"github.com/diamondburned/ningen/v3"
 	"github.com/gdamore/tcell/v3"
-	"golang.design/x/clipboard"
 )
 
 type guildsTree struct {

+ 1 - 1
cmd/message_input.go

@@ -15,6 +15,7 @@ import (
 	"unicode"
 
 	"github.com/ayn2op/discordo/internal/cache"
+	"github.com/ayn2op/discordo/internal/clipboard"
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/consts"
 	"github.com/ayn2op/discordo/internal/ui"
@@ -29,7 +30,6 @@ import (
 	"github.com/ncruces/zenity"
 	"github.com/sahilm/fuzzy"
 	"github.com/yuin/goldmark/ast"
-	"golang.design/x/clipboard"
 )
 
 const tmpFilePattern = consts.Name + "_*.md"

+ 1 - 1
cmd/messages_list.go

@@ -14,6 +14,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/ayn2op/discordo/internal/clipboard"
 	"github.com/ayn2op/discordo/internal/config"
 	"github.com/ayn2op/discordo/internal/consts"
 	"github.com/ayn2op/discordo/internal/markdown"
@@ -30,7 +31,6 @@ import (
 	"github.com/yuin/goldmark/ast"
 	"github.com/yuin/goldmark/parser"
 	"github.com/yuin/goldmark/text"
-	"golang.design/x/clipboard"
 )
 
 type messagesList struct {

+ 3 - 3
cmd/state.go

@@ -147,7 +147,7 @@ func onReady(r *gateway.ReadyEvent) {
 
 func onMessageCreate(message *gateway.MessageCreateEvent) {
 	if app.chatView.selectedChannel != nil &&
-	   app.chatView.selectedChannel.ID == message.ChannelID {
+		app.chatView.selectedChannel.ID == message.ChannelID {
 		app.chatView.messagesList.drawMessage(app.chatView.messagesList, message.Message)
 		app.Draw()
 	}
@@ -159,14 +159,14 @@ func onMessageCreate(message *gateway.MessageCreateEvent) {
 
 func onMessageUpdate(message *gateway.MessageUpdateEvent) {
 	if app.chatView.selectedChannel != nil &&
-	   app.chatView.selectedChannel.ID == message.ChannelID {
+		app.chatView.selectedChannel.ID == message.ChannelID {
 		onMessageDelete(&gateway.MessageDeleteEvent{ID: message.ID, ChannelID: message.ChannelID, GuildID: message.GuildID})
 	}
 }
 
 func onMessageDelete(message *gateway.MessageDeleteEvent) {
 	if app.chatView.selectedChannel != nil &&
-	   app.chatView.selectedChannel.ID == message.ChannelID {
+		app.chatView.selectedChannel.ID == message.ChannelID {
 		messages, err := discordState.Cabinet.Messages(message.ChannelID)
 		if err != nil {
 			slog.Error("failed to get messages from state", "err", err, "channel_id", message.ChannelID)

+ 10 - 0
internal/clipboard/clipboard.go

@@ -0,0 +1,10 @@
+package clipboard
+
+import designClipb "golang.design/x/clipboard"
+
+type Format = designClipb.Format
+
+const (
+	FmtText  Format = designClipb.FmtText
+	FmtImage        = designClipb.FmtImage
+)

+ 11 - 0
internal/clipboard/clipboard_default.go

@@ -0,0 +1,11 @@
+//go:build !linux
+
+package clipboard
+
+import designClipb "golang.design/x/clipboard"
+
+var (
+	Init = designClipb.Init
+	Read = designClipb.Read
+	Write = designClipb.Write
+)

+ 67 - 0
internal/clipboard/clipboard_linux.go

@@ -0,0 +1,67 @@
+//go:build linux
+
+package clipboard
+
+import (
+	"bytes"
+	designClipb "golang.design/x/clipboard"
+	"log/slog"
+	"os"
+	"os/exec"
+)
+
+var wayland bool
+
+func Init() error {
+	if _, ok := os.LookupEnv("WAYLAND_DISPLAY"); !ok {
+		return designClipb.Init()
+	}
+	if _, err := exec.LookPath("wl-copy"); err != nil {
+		return err
+	}
+	if _, err := exec.LookPath("wl-paste"); err != nil {
+		return err
+	}
+	wayland = true
+	return nil
+}
+
+func Read(t Format) []byte {
+	if !wayland {
+		return designClipb.Read(designClipb.Format(t))
+	}
+	// -n: Don't print a newline at the end
+	// -t type: MIME type specifier
+	cmd := exec.Command("wl-paste", "-nt", formatToType(t))
+	outBuffer := bytes.Buffer{}
+	cmd.Stdout = &outBuffer
+	if err := cmd.Run(); err != nil {
+		slog.Error("failed to read clipboard", "err", err)
+		return nil
+	}
+	return outBuffer.Bytes()
+}
+
+func Write(t Format, buf []byte) <-chan struct{} {
+	if !wayland {
+		return designClipb.Write(designClipb.Format(t), buf)
+	}
+	// -t type: MIME type specifier
+	cmd := exec.Command("wl-copy", "-t", formatToType(t))
+	cmd.Stdin = bytes.NewReader(buf)
+	if err := cmd.Run(); err != nil {
+		slog.Error("failed to write to clipboard", "err", err)
+	}
+	return nil
+}
+
+func formatToType(t Format) string {
+	switch t {
+	case FmtImage:
+		return "image"
+	case FmtText:
+		fallthrough
+	default:
+		return "text/plain;charset=utf-8"
+	}
+}