diff options
| author | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
|---|---|---|
| committer | Nippy <nippy@rp1.hu> | 2026-05-09 13:33:09 +0200 |
| commit | a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 (patch) | |
| tree | 1a8769217f84bfe9d6216fafaadaf8cdd876d457 /raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard | |
| parent | 34b9c34f982b2596cfa9e368a2d7f74e9a17477c (diff) | |
| download | RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.tar.gz RaveOS-PKGBUILD-a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5.zip | |
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/clipboard')
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 -} |