summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
committerNippy <nippy@rp1.hu>2026-05-09 13:33:09 +0200
commita2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch)
tree1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard
parent34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff)
downloadRaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz
RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard')
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go630
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go248
-rw-r--r--raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go303
3 files changed, 0 insertions, 1181 deletions
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go
deleted file mode 100644
index d27d6ff..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/clipboard.go
+++ /dev/null
@@ -1,630 +0,0 @@
-package clipboard
-
-import (
- "fmt"
- "io"
- "os"
- "os/exec"
- "path/filepath"
- "syscall"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control"
- wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
-)
-
-const envServe = "_DMS_CLIPBOARD_SERVE"
-const envMime = "_DMS_CLIPBOARD_MIME"
-const envPasteOnce = "_DMS_CLIPBOARD_PASTE_ONCE"
-const envCacheFile = "_DMS_CLIPBOARD_CACHE"
-
-// MaybeServeAndExit intercepts before cobra when re-exec'd as a clipboard
-// child. Reads source data into memory, deletes any cache file, then serves.
-func MaybeServeAndExit() {
- if os.Getenv(envServe) == "" {
- return
- }
-
- mimeType := os.Getenv(envMime)
- pasteOnce := os.Getenv(envPasteOnce) == "1"
- cachePath := os.Getenv(envCacheFile)
-
- var data []byte
- var err error
-
- switch {
- case cachePath != "":
- data, err = os.ReadFile(cachePath)
- os.Remove(cachePath)
- default:
- data, err = io.ReadAll(os.Stdin)
- }
-
- if err != nil {
- fmt.Fprintf(os.Stderr, "clipboard: read source: %v\n", err)
- os.Exit(1)
- }
-
- if err := serveClipboard(data, mimeType, pasteOnce); err != nil {
- fmt.Fprintf(os.Stderr, "clipboard: serve: %v\n", err)
- os.Exit(1)
- }
- os.Exit(0)
-}
-
-func Copy(data []byte, mimeType string) error {
- return copyForkCached(data, mimeType, false)
-}
-
-func CopyOpts(data []byte, mimeType string, foreground, pasteOnce bool) error {
- if foreground {
- return serveClipboard(data, mimeType, pasteOnce)
- }
- return copyForkCached(data, mimeType, pasteOnce)
-}
-
-func CopyReader(data io.Reader, mimeType string, foreground, pasteOnce bool) error {
- if foreground {
- buf, err := io.ReadAll(data)
- if err != nil {
- return fmt.Errorf("read source: %w", err)
- }
- return serveClipboard(buf, mimeType, pasteOnce)
- }
- return copyFork(data, mimeType, pasteOnce)
-}
-
-func newForkCmd(mimeType string, pasteOnce bool, extra ...string) *exec.Cmd {
- cmd := exec.Command(os.Args[0])
- cmd.Stderr = nil
- cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
- cmd.Env = append(os.Environ(),
- envServe+"=1",
- envMime+"="+mimeType,
- )
- if pasteOnce {
- cmd.Env = append(cmd.Env, envPasteOnce+"=1")
- }
- cmd.Env = append(cmd.Env, extra...)
- return cmd
-}
-
-func waitReady(cmd *exec.Cmd) error {
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return fmt.Errorf("stdout pipe: %w", err)
- }
- if err := cmd.Start(); err != nil {
- return fmt.Errorf("start: %w", err)
- }
- var buf [1]byte
- if _, err := stdout.Read(buf[:]); err != nil {
- return fmt.Errorf("waiting for clipboard ready: %w", err)
- }
- return nil
-}
-
-func copyForkCached(data []byte, mimeType string, pasteOnce bool) error {
- cacheFile, err := createClipboardCacheFile()
- if err != nil {
- return fmt.Errorf("create cache file: %w", err)
- }
- cachePath := cacheFile.Name()
-
- if _, err := cacheFile.Write(data); err != nil {
- cacheFile.Close()
- os.Remove(cachePath)
- return fmt.Errorf("write cache file: %w", err)
- }
- if err := cacheFile.Close(); err != nil {
- os.Remove(cachePath)
- return fmt.Errorf("close cache file: %w", err)
- }
-
- cmd := newForkCmd(mimeType, pasteOnce, envCacheFile+"="+cachePath)
- cmd.Stdin = nil
- if err := waitReady(cmd); err != nil {
- os.Remove(cachePath)
- return err
- }
- return nil
-}
-
-func copyFork(data io.Reader, mimeType string, pasteOnce bool) error {
- cmd := newForkCmd(mimeType, pasteOnce)
-
- switch src := data.(type) {
- case *os.File:
- cmd.Stdin = src
- return waitReady(cmd)
-
- default:
- stdin, err := cmd.StdinPipe()
- if err != nil {
- return fmt.Errorf("stdin pipe: %w", err)
- }
-
- stdout, err := cmd.StdoutPipe()
- if err != nil {
- return fmt.Errorf("stdout pipe: %w", err)
- }
-
- if err := cmd.Start(); err != nil {
- return fmt.Errorf("start: %w", err)
- }
- if _, err := io.Copy(stdin, data); err != nil {
- stdin.Close()
- return fmt.Errorf("write stdin: %w", err)
- }
- if err := stdin.Close(); err != nil {
- return fmt.Errorf("close stdin: %w", err)
- }
-
- var buf [1]byte
- if _, err := stdout.Read(buf[:]); err != nil {
- return fmt.Errorf("waiting for clipboard ready: %w", err)
- }
- return nil
- }
-}
-
-func signalReady() {
- if os.Getenv(envServe) == "" {
- return
- }
- os.Stdout.Write([]byte{1})
-}
-
-func createClipboardCacheFile() (*os.File, error) {
- preferredDirs := []string{}
-
- if cacheDir, err := os.UserCacheDir(); err == nil {
- preferredDirs = append(preferredDirs, filepath.Join(cacheDir, "dms", "clipboard"))
- }
- preferredDirs = append(preferredDirs, "/var/tmp/dms/clipboard")
-
- for _, dir := range preferredDirs {
- if err := os.MkdirAll(dir, 0o700); err != nil {
- continue
- }
- cachedData, err := os.CreateTemp(dir, "dms-clipboard-*")
- if err == nil {
- return cachedData, nil
- }
- }
- return os.CreateTemp("", "dms-clipboard-*")
-}
-
-func serveClipboard(data []byte, mimeType string, pasteOnce bool) error {
- display, err := wlclient.Connect("")
- if err != nil {
- return fmt.Errorf("wayland connect: %w", err)
- }
- defer display.Destroy()
-
- ctx := display.Context()
- registry, err := display.GetRegistry()
- if err != nil {
- return fmt.Errorf("get registry: %w", err)
- }
- defer registry.Destroy()
-
- var dataControlMgr *ext_data_control.ExtDataControlManagerV1
- var seat *wlclient.Seat
- var bindErr error
-
- registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
- switch e.Interface {
- case "ext_data_control_manager_v1":
- dataControlMgr = ext_data_control.NewExtDataControlManagerV1(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil {
- bindErr = err
- }
- case "wl_seat":
- if seat != nil {
- return
- }
- seat = wlclient.NewSeat(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil {
- bindErr = err
- }
- }
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- if bindErr != nil {
- return fmt.Errorf("registry bind: %w", bindErr)
- }
- if dataControlMgr == nil {
- return fmt.Errorf("compositor does not support ext_data_control_manager_v1")
- }
- defer dataControlMgr.Destroy()
- if seat == nil {
- return fmt.Errorf("no seat available")
- }
-
- device, err := dataControlMgr.GetDataDevice(seat)
- if err != nil {
- return fmt.Errorf("get data device: %w", err)
- }
- defer device.Destroy()
-
- source, err := dataControlMgr.CreateDataSource()
- if err != nil {
- return fmt.Errorf("create data source: %w", err)
- }
-
- if err := source.Offer(mimeType); err != nil {
- return fmt.Errorf("offer mime type: %w", err)
- }
- if mimeType == "text/plain;charset=utf-8" || mimeType == "text/plain" {
- if err := source.Offer("text/plain"); err != nil {
- return fmt.Errorf("offer text/plain: %w", err)
- }
- if err := source.Offer("text/plain;charset=utf-8"); err != nil {
- return fmt.Errorf("offer text/plain;charset=utf-8: %w", err)
- }
- if err := source.Offer("UTF8_STRING"); err != nil {
- return fmt.Errorf("offer UTF8_STRING: %w", err)
- }
- if err := source.Offer("STRING"); err != nil {
- return fmt.Errorf("offer STRING: %w", err)
- }
- if err := source.Offer("TEXT"); err != nil {
- return fmt.Errorf("offer TEXT: %w", err)
- }
- }
-
- cancelled := make(chan struct{})
- pasted := make(chan struct{}, 1)
-
- source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) {
- _ = syscall.SetNonblock(e.Fd, false)
- file := os.NewFile(uintptr(e.Fd), "pipe")
- defer file.Close()
- _, _ = file.Write(data)
- select {
- case pasted <- struct{}{}:
- default:
- }
- })
-
- source.SetCancelledHandler(func(e ext_data_control.ExtDataControlSourceV1CancelledEvent) {
- close(cancelled)
- })
-
- if err := device.SetSelection(source); err != nil {
- return fmt.Errorf("set selection: %w", err)
- }
-
- display.Roundtrip()
- signalReady()
-
- for {
- select {
- case <-cancelled:
- return nil
- case <-pasted:
- if pasteOnce {
- return nil
- }
- default:
- if err := ctx.Dispatch(); err != nil {
- return nil
- }
- }
- }
-}
-
-func CopyText(text string) error {
- return Copy([]byte(text), "text/plain;charset=utf-8")
-}
-
-func Paste() ([]byte, string, error) {
- display, err := wlclient.Connect("")
- if err != nil {
- return nil, "", fmt.Errorf("wayland connect: %w", err)
- }
- defer display.Destroy()
-
- ctx := display.Context()
- registry, err := display.GetRegistry()
- if err != nil {
- return nil, "", fmt.Errorf("get registry: %w", err)
- }
- defer registry.Destroy()
-
- var dataControlMgr *ext_data_control.ExtDataControlManagerV1
- var seat *wlclient.Seat
- var bindErr error
-
- registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
- switch e.Interface {
- case "ext_data_control_manager_v1":
- dataControlMgr = ext_data_control.NewExtDataControlManagerV1(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil {
- bindErr = err
- }
- case "wl_seat":
- if seat != nil {
- return
- }
- seat = wlclient.NewSeat(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil {
- bindErr = err
- }
- }
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- if bindErr != nil {
- return nil, "", fmt.Errorf("registry bind: %w", bindErr)
- }
-
- if dataControlMgr == nil {
- return nil, "", fmt.Errorf("compositor does not support ext_data_control_manager_v1")
- }
- defer dataControlMgr.Destroy()
-
- if seat == nil {
- return nil, "", fmt.Errorf("no seat available")
- }
-
- device, err := dataControlMgr.GetDataDevice(seat)
- if err != nil {
- return nil, "", fmt.Errorf("get data device: %w", err)
- }
- defer device.Destroy()
-
- offerMimeTypes := make(map[*ext_data_control.ExtDataControlOfferV1][]string)
-
- device.SetDataOfferHandler(func(e ext_data_control.ExtDataControlDeviceV1DataOfferEvent) {
- if e.Id == nil {
- return
- }
- offerMimeTypes[e.Id] = nil
- e.Id.SetOfferHandler(func(me ext_data_control.ExtDataControlOfferV1OfferEvent) {
- offerMimeTypes[e.Id] = append(offerMimeTypes[e.Id], me.MimeType)
- })
- })
-
- var selectionOffer *ext_data_control.ExtDataControlOfferV1
- gotSelection := false
-
- device.SetSelectionHandler(func(e ext_data_control.ExtDataControlDeviceV1SelectionEvent) {
- selectionOffer = e.Id
- gotSelection = true
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- if !gotSelection || selectionOffer == nil {
- return nil, "", fmt.Errorf("no clipboard data")
- }
-
- mimeTypes := offerMimeTypes[selectionOffer]
- selectedMime := selectPreferredMimeType(mimeTypes)
- if selectedMime == "" {
- return nil, "", fmt.Errorf("no supported mime type")
- }
-
- r, w, err := os.Pipe()
- if err != nil {
- return nil, "", fmt.Errorf("create pipe: %w", err)
- }
- defer r.Close()
-
- if err := selectionOffer.Receive(selectedMime, int(w.Fd())); err != nil {
- w.Close()
- return nil, "", fmt.Errorf("receive: %w", err)
- }
- w.Close()
-
- display.Roundtrip()
-
- data, err := io.ReadAll(r)
- if err != nil {
- return nil, "", fmt.Errorf("read: %w", err)
- }
-
- return data, selectedMime, nil
-}
-
-func PasteText() (string, error) {
- data, _, err := Paste()
- if err != nil {
- return "", err
- }
- return string(data), nil
-}
-
-func selectPreferredMimeType(mimes []string) string {
- preferred := []string{
- "text/plain;charset=utf-8",
- "text/plain",
- "UTF8_STRING",
- "STRING",
- "TEXT",
- "image/png",
- "image/jpeg",
- }
-
- for _, pref := range preferred {
- for _, mime := range mimes {
- if mime == pref {
- return mime
- }
- }
- }
-
- if len(mimes) > 0 {
- return mimes[0]
- }
- return ""
-}
-
-func IsImageMimeType(mime string) bool {
- return len(mime) > 6 && mime[:6] == "image/"
-}
-
-type Offer struct {
- MimeType string
- Data []byte
-}
-
-func CopyMulti(offers []Offer, foreground, pasteOnce bool) error {
- if !foreground {
- return copyMultiFork(offers, pasteOnce)
- }
- return copyMultiServe(offers, pasteOnce)
-}
-
-func copyMultiFork(offers []Offer, pasteOnce bool) error {
- args := []string{os.Args[0], "cl", "copy", "--foreground", "--type", "__multi__"}
- if pasteOnce {
- args = append(args, "--paste-once")
- }
-
- cmd := exec.Command(args[0], args[1:]...)
- cmd.Stdin = nil
- cmd.Stdout = nil
- cmd.Stderr = nil
- cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
-
- stdin, err := cmd.StdinPipe()
- if err != nil {
- return fmt.Errorf("stdin pipe: %w", err)
- }
-
- if err := cmd.Start(); err != nil {
- return fmt.Errorf("start: %w", err)
- }
-
- for _, offer := range offers {
- fmt.Fprintf(stdin, "%s\x00%d\x00", offer.MimeType, len(offer.Data))
- if _, err := stdin.Write(offer.Data); err != nil {
- stdin.Close()
- return fmt.Errorf("write offer data: %w", err)
- }
- }
- stdin.Close()
-
- return nil
-}
-
-func copyMultiServe(offers []Offer, pasteOnce bool) error {
- display, err := wlclient.Connect("")
- if err != nil {
- return fmt.Errorf("wayland connect: %w", err)
- }
- defer display.Destroy()
-
- ctx := display.Context()
- registry, err := display.GetRegistry()
- if err != nil {
- return fmt.Errorf("get registry: %w", err)
- }
- defer registry.Destroy()
-
- var dataControlMgr *ext_data_control.ExtDataControlManagerV1
- var seat *wlclient.Seat
- var bindErr error
-
- registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
- switch e.Interface {
- case "ext_data_control_manager_v1":
- dataControlMgr = ext_data_control.NewExtDataControlManagerV1(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil {
- bindErr = err
- }
- case "wl_seat":
- if seat != nil {
- return
- }
- seat = wlclient.NewSeat(ctx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil {
- bindErr = err
- }
- }
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- if bindErr != nil {
- return fmt.Errorf("registry bind: %w", bindErr)
- }
- if dataControlMgr == nil {
- return fmt.Errorf("compositor does not support ext_data_control_manager_v1")
- }
- defer dataControlMgr.Destroy()
- if seat == nil {
- return fmt.Errorf("no seat available")
- }
-
- device, err := dataControlMgr.GetDataDevice(seat)
- if err != nil {
- return fmt.Errorf("get data device: %w", err)
- }
- defer device.Destroy()
-
- source, err := dataControlMgr.CreateDataSource()
- if err != nil {
- return fmt.Errorf("create data source: %w", err)
- }
-
- offerMap := make(map[string][]byte)
- for _, offer := range offers {
- if err := source.Offer(offer.MimeType); err != nil {
- return fmt.Errorf("offer %s: %w", offer.MimeType, err)
- }
- offerMap[offer.MimeType] = offer.Data
- }
-
- cancelled := make(chan struct{})
- pasted := make(chan struct{}, 1)
-
- source.SetSendHandler(func(e ext_data_control.ExtDataControlSourceV1SendEvent) {
- _ = syscall.SetNonblock(e.Fd, false)
- file := os.NewFile(uintptr(e.Fd), "pipe")
- defer file.Close()
-
- if data, ok := offerMap[e.MimeType]; ok {
- _, _ = file.Write(data)
- }
-
- select {
- case pasted <- struct{}{}:
- default:
- }
- })
-
- source.SetCancelledHandler(func(e ext_data_control.ExtDataControlSourceV1CancelledEvent) {
- close(cancelled)
- })
-
- if err := device.SetSelection(source); err != nil {
- return fmt.Errorf("set selection: %w", err)
- }
-
- display.Roundtrip()
-
- for {
- select {
- case <-cancelled:
- return nil
- case <-pasted:
- if pasteOnce {
- return nil
- }
- default:
- if err := ctx.Dispatch(); err != nil {
- return nil
- }
- }
- }
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go
deleted file mode 100644
index 17ad7a9..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/store.go
+++ /dev/null
@@ -1,248 +0,0 @@
-package clipboard
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "image"
- _ "image/gif"
- _ "image/jpeg"
- _ "image/png"
- "os"
- "path/filepath"
- "strings"
- "time"
-
- _ "golang.org/x/image/bmp"
- _ "golang.org/x/image/tiff"
- "hash/fnv"
-
- bolt "go.etcd.io/bbolt"
-)
-
-type StoreConfig struct {
- MaxHistory int
- MaxEntrySize int64
-}
-
-func DefaultStoreConfig() StoreConfig {
- return StoreConfig{
- MaxHistory: 100,
- MaxEntrySize: 5 * 1024 * 1024,
- }
-}
-
-type Entry struct {
- ID uint64
- Data []byte
- MimeType string
- Preview string
- Size int
- Timestamp time.Time
- IsImage bool
- Hash uint64
-}
-
-func Store(data []byte, mimeType string) error {
- return StoreWithConfig(data, mimeType, DefaultStoreConfig())
-}
-
-func StoreWithConfig(data []byte, mimeType string, cfg StoreConfig) error {
- if len(data) == 0 {
- return nil
- }
- if int64(len(data)) > cfg.MaxEntrySize {
- return fmt.Errorf("data too large: %d > %d", len(data), cfg.MaxEntrySize)
- }
-
- dbPath, err := GetDBPath()
- if err != nil {
- return fmt.Errorf("get db path: %w", err)
- }
-
- db, err := bolt.Open(dbPath, 0o644, &bolt.Options{Timeout: 1 * time.Second})
- if err != nil {
- return fmt.Errorf("open db: %w", err)
- }
- defer db.Close()
-
- entry := Entry{
- Data: data,
- MimeType: mimeType,
- Size: len(data),
- Timestamp: time.Now(),
- IsImage: IsImageMimeType(mimeType),
- Hash: computeHash(data),
- }
-
- switch {
- case entry.IsImage:
- entry.Preview = imagePreview(data, mimeType)
- default:
- entry.Preview = textPreview(data)
- }
-
- return db.Update(func(tx *bolt.Tx) error {
- b, err := tx.CreateBucketIfNotExists([]byte("clipboard"))
- if err != nil {
- return err
- }
-
- if err := deduplicateInTx(b, entry.Hash); err != nil {
- return err
- }
-
- id, err := b.NextSequence()
- if err != nil {
- return err
- }
- entry.ID = id
-
- encoded, err := encodeEntry(entry)
- if err != nil {
- return err
- }
-
- if err := b.Put(itob(id), encoded); err != nil {
- return err
- }
-
- return trimLengthInTx(b, cfg.MaxHistory)
- })
-}
-
-func GetDBPath() (string, error) {
- cacheDir, err := os.UserCacheDir()
- if err != nil {
- homeDir, err := os.UserHomeDir()
- if err != nil {
- return "", err
- }
- cacheDir = filepath.Join(homeDir, ".cache")
- }
-
- newDir := filepath.Join(cacheDir, "DankMaterialShell", "clipboard")
- newPath := filepath.Join(newDir, "db")
-
- if _, err := os.Stat(newPath); err == nil {
- return newPath, nil
- }
-
- oldDir := filepath.Join(cacheDir, "dms-clipboard")
- oldPath := filepath.Join(oldDir, "db")
-
- if _, err := os.Stat(oldPath); err == nil {
- if err := os.MkdirAll(newDir, 0o700); err != nil {
- return "", err
- }
- if err := os.Rename(oldPath, newPath); err != nil {
- return "", err
- }
- os.Remove(oldDir)
- return newPath, nil
- }
-
- if err := os.MkdirAll(newDir, 0o700); err != nil {
- return "", err
- }
- return newPath, nil
-}
-
-func deduplicateInTx(b *bolt.Bucket, hash uint64) error {
- c := b.Cursor()
- for k, v := c.Last(); k != nil; k, v = c.Prev() {
- if extractHash(v) != hash {
- continue
- }
- if err := b.Delete(k); err != nil {
- return err
- }
- }
- return nil
-}
-
-func trimLengthInTx(b *bolt.Bucket, maxHistory int) error {
- c := b.Cursor()
- var count int
- for k, _ := c.Last(); k != nil; k, _ = c.Prev() {
- if count < maxHistory {
- count++
- continue
- }
- if err := b.Delete(k); err != nil {
- return err
- }
- }
- return nil
-}
-
-func encodeEntry(e Entry) ([]byte, error) {
- buf := new(bytes.Buffer)
-
- binary.Write(buf, binary.BigEndian, e.ID)
- binary.Write(buf, binary.BigEndian, uint32(len(e.Data)))
- buf.Write(e.Data)
- binary.Write(buf, binary.BigEndian, uint32(len(e.MimeType)))
- buf.WriteString(e.MimeType)
- binary.Write(buf, binary.BigEndian, uint32(len(e.Preview)))
- buf.WriteString(e.Preview)
- binary.Write(buf, binary.BigEndian, int32(e.Size))
- binary.Write(buf, binary.BigEndian, e.Timestamp.Unix())
- if e.IsImage {
- buf.WriteByte(1)
- } else {
- buf.WriteByte(0)
- }
- binary.Write(buf, binary.BigEndian, e.Hash)
-
- return buf.Bytes(), nil
-}
-
-func itob(v uint64) []byte {
- b := make([]byte, 8)
- binary.BigEndian.PutUint64(b, v)
- return b
-}
-
-func computeHash(data []byte) uint64 {
- h := fnv.New64a()
- h.Write(data)
- return h.Sum64()
-}
-
-func extractHash(data []byte) uint64 {
- if len(data) < 8 {
- return 0
- }
- return binary.BigEndian.Uint64(data[len(data)-8:])
-}
-
-func textPreview(data []byte) string {
- text := string(data)
- text = strings.TrimSpace(text)
- text = strings.Join(strings.Fields(text), " ")
-
- if len(text) > 100 {
- return text[:100] + "…"
- }
- return text
-}
-
-func imagePreview(data []byte, format string) string {
- config, imgFmt, err := image.DecodeConfig(bytes.NewReader(data))
- if err != nil {
- return fmt.Sprintf("[[ image %s %s ]]", sizeStr(len(data)), format)
- }
- return fmt.Sprintf("[[ image %s %s %dx%d ]]", sizeStr(len(data)), imgFmt, config.Width, config.Height)
-}
-
-func sizeStr(size int) string {
- units := []string{"B", "KiB", "MiB"}
- var i int
- fsize := float64(size)
- for fsize >= 1024 && i < len(units)-1 {
- fsize /= 1024
- i++
- }
- return fmt.Sprintf("%.0f %s", fsize, units[i])
-}
diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go
deleted file mode 100644
index 327c5c5..0000000
--- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard/watch.go
+++ /dev/null
@@ -1,303 +0,0 @@
-package clipboard
-
-import (
- "context"
- "errors"
- "fmt"
- "io"
- "os"
- "time"
-
- "github.com/AvengeMedia/DankMaterialShell/core/internal/proto/ext_data_control"
- wlclient "github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
-)
-
-type ClipboardChange struct {
- Data []byte
- MimeType string
- MimeTypes []string
-}
-
-func Watch(ctx context.Context, callback func(data []byte, mimeType string)) error {
- display, err := wlclient.Connect("")
- if err != nil {
- return fmt.Errorf("wayland connect: %w", err)
- }
- defer display.Destroy()
-
- wlCtx := display.Context()
- registry, err := display.GetRegistry()
- if err != nil {
- return fmt.Errorf("get registry: %w", err)
- }
- defer registry.Destroy()
-
- var dataControlMgr *ext_data_control.ExtDataControlManagerV1
- var seat *wlclient.Seat
- var bindErr error
-
- registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
- switch e.Interface {
- case "ext_data_control_manager_v1":
- dataControlMgr = ext_data_control.NewExtDataControlManagerV1(wlCtx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil {
- bindErr = err
- }
- case "wl_seat":
- if seat != nil {
- return
- }
- seat = wlclient.NewSeat(wlCtx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil {
- bindErr = err
- }
- }
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- if bindErr != nil {
- return fmt.Errorf("registry bind: %w", bindErr)
- }
-
- if dataControlMgr == nil {
- return fmt.Errorf("compositor does not support ext_data_control_manager_v1")
- }
- defer dataControlMgr.Destroy()
-
- if seat == nil {
- return fmt.Errorf("no seat available")
- }
-
- device, err := dataControlMgr.GetDataDevice(seat)
- if err != nil {
- return fmt.Errorf("get data device: %w", err)
- }
- defer device.Destroy()
-
- offerMimeTypes := make(map[*ext_data_control.ExtDataControlOfferV1][]string)
-
- device.SetDataOfferHandler(func(e ext_data_control.ExtDataControlDeviceV1DataOfferEvent) {
- if e.Id == nil {
- return
- }
- offerMimeTypes[e.Id] = nil
- e.Id.SetOfferHandler(func(me ext_data_control.ExtDataControlOfferV1OfferEvent) {
- offerMimeTypes[e.Id] = append(offerMimeTypes[e.Id], me.MimeType)
- })
- })
-
- device.SetSelectionHandler(func(e ext_data_control.ExtDataControlDeviceV1SelectionEvent) {
- if e.Id == nil {
- return
- }
-
- mimes := offerMimeTypes[e.Id]
- selectedMime := selectPreferredMimeType(mimes)
- if selectedMime == "" {
- return
- }
-
- r, w, err := os.Pipe()
- if err != nil {
- return
- }
-
- if err := e.Id.Receive(selectedMime, int(w.Fd())); err != nil {
- w.Close()
- r.Close()
- return
- }
- w.Close()
-
- go func() {
- defer r.Close()
- data, err := io.ReadAll(r)
- if err != nil || len(data) == 0 {
- return
- }
- callback(data, selectedMime)
- }()
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- for {
- select {
- case <-ctx.Done():
- return ctx.Err()
- default:
- if err := wlCtx.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
- return fmt.Errorf("set read deadline: %w", err)
- }
- if err := wlCtx.Dispatch(); err != nil {
- if isTimeoutError(err) {
- continue
- }
- return fmt.Errorf("dispatch: %w", err)
- }
- }
- }
-}
-
-func WatchAll(ctx context.Context, callback func(data []byte, mimeType string, allMimeTypes []string)) error {
- display, err := wlclient.Connect("")
- if err != nil {
- return fmt.Errorf("wayland connect: %w", err)
- }
- defer display.Destroy()
-
- wlCtx := display.Context()
- registry, err := display.GetRegistry()
- if err != nil {
- return fmt.Errorf("get registry: %w", err)
- }
- defer registry.Destroy()
-
- var dataControlMgr *ext_data_control.ExtDataControlManagerV1
- var seat *wlclient.Seat
- var bindErr error
-
- registry.SetGlobalHandler(func(e wlclient.RegistryGlobalEvent) {
- switch e.Interface {
- case "ext_data_control_manager_v1":
- dataControlMgr = ext_data_control.NewExtDataControlManagerV1(wlCtx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, dataControlMgr); err != nil {
- bindErr = err
- }
- case "wl_seat":
- if seat != nil {
- return
- }
- seat = wlclient.NewSeat(wlCtx)
- if err := registry.Bind(e.Name, e.Interface, e.Version, seat); err != nil {
- bindErr = err
- }
- }
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- if bindErr != nil {
- return fmt.Errorf("registry bind: %w", bindErr)
- }
- if dataControlMgr == nil {
- return fmt.Errorf("compositor does not support ext_data_control_manager_v1")
- }
- defer dataControlMgr.Destroy()
- if seat == nil {
- return fmt.Errorf("no seat available")
- }
-
- device, err := dataControlMgr.GetDataDevice(seat)
- if err != nil {
- return fmt.Errorf("get data device: %w", err)
- }
- defer device.Destroy()
-
- offerMimeTypes := make(map[*ext_data_control.ExtDataControlOfferV1][]string)
-
- device.SetDataOfferHandler(func(e ext_data_control.ExtDataControlDeviceV1DataOfferEvent) {
- if e.Id == nil {
- return
- }
- offerMimeTypes[e.Id] = nil
- e.Id.SetOfferHandler(func(me ext_data_control.ExtDataControlOfferV1OfferEvent) {
- offerMimeTypes[e.Id] = append(offerMimeTypes[e.Id], me.MimeType)
- })
- })
-
- device.SetSelectionHandler(func(e ext_data_control.ExtDataControlDeviceV1SelectionEvent) {
- if e.Id == nil {
- return
- }
-
- mimes := offerMimeTypes[e.Id]
- selectedMime := selectPreferredMimeType(mimes)
- if selectedMime == "" {
- return
- }
-
- mimesCopy := make([]string, len(mimes))
- copy(mimesCopy, mimes)
-
- r, w, err := os.Pipe()
- if err != nil {
- return
- }
-
- if err := e.Id.Receive(selectedMime, int(w.Fd())); err != nil {
- w.Close()
- r.Close()
- return
- }
- w.Close()
-
- go func() {
- defer r.Close()
- data, err := io.ReadAll(r)
- if err != nil || len(data) == 0 {
- return
- }
- callback(data, selectedMime, mimesCopy)
- }()
- })
-
- display.Roundtrip()
- display.Roundtrip()
-
- for {
- select {
- case <-ctx.Done():
- return ctx.Err()
- default:
- if err := wlCtx.SetReadDeadline(time.Now().Add(100 * time.Millisecond)); err != nil {
- return fmt.Errorf("set read deadline: %w", err)
- }
- if err := wlCtx.Dispatch(); err != nil {
- if isTimeoutError(err) {
- continue
- }
- return fmt.Errorf("dispatch: %w", err)
- }
- }
- }
-}
-
-func isTimeoutError(err error) bool {
- if err == nil {
- return false
- }
- if errors.Is(err, os.ErrDeadlineExceeded) {
- return true
- }
- if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
- return true
- }
- return false
-}
-
-func WatchChan(ctx context.Context) (<-chan ClipboardChange, <-chan error) {
- ch := make(chan ClipboardChange, 16)
- errCh := make(chan error, 1)
-
- go func() {
- defer close(ch)
- err := Watch(ctx, func(data []byte, mimeType string) {
- select {
- case ch <- ClipboardChange{Data: data, MimeType: mimeType}:
- default:
- }
- })
- if err != nil && err != context.Canceled {
- errCh <- err
- }
- close(errCh)
- }()
-
- time.Sleep(50 * time.Millisecond)
- return ch, errCh
-}