Upload images to your PDS and get instant CDN URLs via images.blue

use indigo auth, implement gnome wayland clipboard copy

evan.jarrett.net 7c5bbc48 a21f186c

verified
+144 -69
cmd/blup/main.go
··· 2 2 3 3 import ( 4 4 "context" 5 + "encoding/json" 5 6 "fmt" 6 7 "io" 8 + "log/slog" 7 9 "net/http" 8 10 "os" 9 11 "path/filepath" ··· 11 13 "time" 12 14 13 15 "github.com/bluesky-social/indigo/api/atproto" 16 + "github.com/bluesky-social/indigo/atproto/atclient" 14 17 lex_util "github.com/bluesky-social/indigo/lex/util" 15 - "github.com/bluesky-social/indigo/xrpc" 16 - oauth "github.com/haileyok/atproto-oauth-golang" 17 18 "github.com/spf13/cobra" 18 19 "tangled.sh/evan.jarrett.net/blup/internal/auth" 20 + "tangled.sh/evan.jarrett.net/blup/internal/clipboard" 19 21 "tangled.sh/evan.jarrett.net/blup/internal/config" 22 + "tangled.sh/evan.jarrett.net/blup/internal/screenshot" 20 23 "tangled.sh/evan.jarrett.net/blup/internal/util" 21 24 ) 22 25 ··· 27 30 28 31 var ( 29 32 authHandle string 33 + logLevel string 30 34 ) 31 35 32 36 func main() { 33 37 var rootCmd = &cobra.Command{ 34 38 Use: Name, 35 39 Short: "AT Protocol image hosting CLI", 40 + PersistentPreRun: func(cmd *cobra.Command, args []string) { 41 + setupLogging() 42 + }, 36 43 } 44 + rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "info", "Log level (debug, info, warn, error)") 37 45 38 - var authCmd = &cobra.Command{ 39 - Use: "auth", 40 - Short: "Authenticate with AT Protocol", 46 + var loginCmd = &cobra.Command{ 47 + Use: "login", 48 + Short: "Log in with AT Protocol", 41 49 RunE: runAuth, 42 50 } 43 - authCmd.Flags().StringVar(&authHandle, "handle", "", "Bluesky (ATProto) handle") 51 + loginCmd.Flags().StringVar(&authHandle, "handle", "", "Bluesky (ATProto) handle") 44 52 45 53 var statusCmd = &cobra.Command{ 46 54 Use: "status", ··· 61 69 RunE: runUpload, 62 70 } 63 71 64 - rootCmd.AddCommand(authCmd, statusCmd, logoutCmd, uploadCmd) 72 + var captureCmd = &cobra.Command{ 73 + Use: "capture", 74 + Short: "Take a screenshot, upload it, and copy URL to clipboard", 75 + RunE: runCapture, 76 + } 77 + 78 + rootCmd.AddCommand(loginCmd, statusCmd, logoutCmd, uploadCmd, captureCmd) 65 79 66 80 var debugTokensCmd = &cobra.Command{ 67 81 Use: "debug-tokens", 68 82 Short: "Debug stored tokens", 69 83 RunE: func(cmd *cobra.Command, args []string) error { 70 - tokens, err := auth.GetStoredTokens() 84 + ctx := context.Background() 85 + store := auth.NewKeyringAuthStore() 86 + sessData, err := store.GetCurrentSession(ctx) 71 87 if err != nil { 72 - return fmt.Errorf("no tokens found: %w", err) 88 + return fmt.Errorf("not authenticated - run 'blup login' first") 73 89 } 74 90 75 - fmt.Printf("Has Access Token: %v\n", tokens.AccessToken) 76 - fmt.Printf("Has Refresh Token: %v\n", tokens.RefreshToken) 77 - fmt.Printf("Expires At: %s\n", time.Unix(tokens.ExpiresAt, 0).Format(time.RFC3339)) 78 - fmt.Printf("Needs Refresh: %v\n", tokens.NeedsRefresh()) 79 - fmt.Printf("Scope: %s\n", tokens.Scope) // Add this line 91 + fmt.Printf("Account DID: %s\n", sessData.AccountDID) 92 + fmt.Printf("Session ID: %s\n", sessData.SessionID) 93 + fmt.Printf("Host URL: %s\n", sessData.HostURL) 94 + fmt.Printf("Auth Server: %s\n", sessData.AuthServerURL) 95 + fmt.Printf("Scopes: %v\n", sessData.Scopes) 96 + fmt.Printf("Has Access Token: %v\n", sessData.AccessToken != "") 97 + fmt.Printf("Has Refresh Token: %v\n", sessData.RefreshToken != "") 80 98 81 99 return nil 82 100 }, ··· 115 133 } 116 134 117 135 func runStatus(cmd *cobra.Command, args []string) error { 118 - tokens, err := auth.GetStoredTokens() 136 + ctx := context.Background() 137 + store := auth.NewKeyringAuthStore() 138 + sessData, err := store.GetCurrentSession(ctx) 119 139 if err != nil { 120 140 fmt.Println("Not authenticated") 121 141 return nil 122 142 } 123 143 124 - expiresAt := time.Unix(tokens.ExpiresAt, 0) 125 144 fmt.Printf("Authenticated: Yes\n") 126 - fmt.Printf("Token expires: %s\n", expiresAt.Format("2006-01-02 15:04:05")) 127 - 128 - if tokens.NeedsRefresh() { 129 - fmt.Println("Token needs refresh (expires within 7 days)") 130 - } 145 + fmt.Printf("Account: %s\n", sessData.AccountDID) 146 + fmt.Printf("PDS: %s\n", sessData.HostURL) 131 147 132 148 return nil 133 149 } 134 150 135 151 func runLogout(cmd *cobra.Command, args []string) error { 136 - if err := auth.DeleteStoredTokens(); err != nil { 152 + cfg, err := config.LoadConfig() 153 + if err != nil { 154 + // No config, nothing to logout 155 + fmt.Println("Not logged in") 156 + return nil 157 + } 158 + 159 + if err := auth.Logout(cfg); err != nil { 137 160 return err 138 161 } 139 162 fmt.Println("Logged out successfully") ··· 141 164 } 142 165 143 166 func runUpload(cmd *cobra.Command, args []string) error { 167 + url, err := uploadImage(args[0]) 168 + if err != nil { 169 + return err 170 + } 171 + fmt.Println(url) 172 + return nil 173 + } 174 + 175 + func runCapture(cmd *cobra.Command, args []string) error { 176 + // Take screenshot using XDG Desktop Portal 177 + fmt.Println("Opening screenshot dialog...") 178 + imagePath, err := screenshot.CaptureScreenshot(true) 179 + if err != nil { 180 + return fmt.Errorf("failed to capture screenshot: %w", err) 181 + } 182 + defer os.Remove(imagePath) // Clean up temp file after upload 183 + 184 + // Upload the screenshot 185 + url, err := uploadImage(imagePath) 186 + if err != nil { 187 + return err 188 + } 189 + 190 + // Copy to clipboard using wl-copy 191 + if err := copyToClipboard(url); err != nil { 192 + fmt.Fprintf(os.Stderr, "Warning: failed to copy to clipboard: %v\n", err) 193 + } 194 + 195 + fmt.Println(url) 196 + return nil 197 + } 198 + 199 + // copyToClipboard copies text to the Wayland clipboard using native protocol 200 + func copyToClipboard(text string) error { 201 + return clipboard.CopyText(text) 202 + } 203 + 204 + // uploadImage handles the core upload logic and returns the CDN URL 205 + func uploadImage(imagePath string) (string, error) { 144 206 // Load config 145 207 cfg, err := config.LoadConfig() 146 208 if err != nil { 147 - fmt.Fprintf(os.Stderr, "Error loading config: %v\nTry running '%s auth'\n", err, Name) 148 - os.Exit(1) 209 + return "", fmt.Errorf("not authenticated, run '%s auth' first: %w", Name, err) 149 210 } 150 211 151 212 ctx := context.Background() 152 - imagepath := args[0] 153 213 154 214 // Open file 155 - file, err := os.Open(imagepath) 215 + file, err := os.Open(imagePath) 156 216 if err != nil { 157 - return fmt.Errorf("failed to open file: %w", err) 217 + return "", fmt.Errorf("failed to open file: %w", err) 158 218 } 159 219 defer file.Close() 160 - imagename := filepath.Base(imagepath) 220 + imagename := filepath.Base(imagePath) 161 221 162 222 // Get file info for size 163 223 fileInfo, err := file.Stat() 164 224 if err != nil { 165 - return fmt.Errorf("Error getting file info: %w", err) 225 + return "", fmt.Errorf("error getting file info: %w", err) 166 226 } 167 227 168 228 fileSize := fileInfo.Size() ··· 171 231 buffer := make([]byte, 512) 172 232 n, err := file.Read(buffer) 173 233 if err != nil && err != io.EOF { 174 - return fmt.Errorf("Error reading file: %w", err) 234 + return "", fmt.Errorf("error reading file: %w", err) 175 235 } 176 236 contentType := http.DetectContentType(buffer[:n]) 177 237 178 238 // Validate it's an image 179 239 if !strings.HasPrefix(contentType, "image/") { 180 - return fmt.Errorf("file is not an image: %s", contentType) 240 + return "", fmt.Errorf("file is not an image: %s", contentType) 181 241 } 182 242 183 243 // Reset file pointer to beginning 184 244 _, err = file.Seek(0, 0) 185 245 if err != nil { 186 - return fmt.Errorf("Error seeking file: %w", err) 246 + return "", fmt.Errorf("error seeking file: %w", err) 187 247 } 188 248 189 - tokens, err := auth.RefreshTokens(cfg) 249 + // Get authenticated session 250 + sess, err := auth.RefreshTokens(cfg) 190 251 if err != nil { 191 - return err 252 + return "", err 192 253 } 193 254 194 - jwk, err := auth.GetStoredJWKS() 195 - if err != nil { 196 - return err 255 + // Get API client from session 256 + apiClient := sess.APIClient() 257 + 258 + // Upload blob using APIRequest for file upload 259 + uploadReq := atclient.NewAPIRequest("POST", "com.atproto.repo.uploadBlob", file) 260 + uploadReq.Headers.Set("Content-Type", contentType) 261 + // Enable retryability 262 + uploadReq.GetBody = func() (io.ReadCloser, error) { 263 + f, err := os.Open(imagePath) 264 + if err != nil { 265 + return nil, err 266 + } 267 + return f, nil 197 268 } 198 269 199 - client := &oauth.XrpcClient{ 200 - UserAgent: userAgent(), 201 - OnDpopPdsNonceChanged: func(did, newNonce string) { 202 - tokens, _ := auth.GetStoredTokens() 203 - if tokens != nil { 204 - tokens.DpopAuthserverNonce = newNonce 205 - tokens.Sub = did 206 - auth.StoreTokens(tokens) 207 - } 208 - auth.RefreshTokens(cfg) 209 - }, 270 + resp, err := apiClient.Do(ctx, uploadReq) 271 + if err != nil { 272 + return "", fmt.Errorf("failed to upload blob: %w", err) 210 273 } 274 + defer resp.Body.Close() 211 275 212 - authArgs := &oauth.XrpcAuthedRequestArgs{ 213 - Did: tokens.Sub, 214 - AccessToken: tokens.AccessToken, 215 - PdsUrl: cfg.PDSHost, 216 - Issuer: cfg.AuthserverIss, 217 - DpopPdsNonce: tokens.DpopAuthserverNonce, 218 - DpopPrivateJwk: jwk, 276 + if resp.StatusCode != http.StatusOK { 277 + body, _ := io.ReadAll(resp.Body) 278 + return "", fmt.Errorf("upload failed with status %d: %s", resp.StatusCode, string(body)) 219 279 } 220 280 221 281 var out atproto.RepoUploadBlob_Output 222 - if err := client.Do(ctx, authArgs, xrpc.Procedure, "*/*", "com.atproto.repo.uploadBlob", nil, file, &out); err != nil { 223 - return err 282 + if err := json.NewDecoder(resp.Body).Decode(&out); err != nil { 283 + return "", fmt.Errorf("failed to decode upload response: %w", err) 224 284 } 225 285 286 + // Create record 226 287 record := map[string]interface{}{ 227 288 "$type": fmt.Sprintf("blue.imgs.%s.image", Name), 228 - "blob": out.Blob, // Use the blob reference from upload 289 + "blob": out.Blob, 229 290 "createdAt": time.Now().Format(time.RFC3339), 230 291 "expiresAt": time.Now().Add(24 * time.Hour).Format(time.RFC3339), 231 292 "filename": imagename, ··· 238 299 } 239 300 240 301 reqBody := map[string]interface{}{ 241 - "repo": tokens.Sub, 302 + "repo": sess.Data.AccountDID.String(), 242 303 "collection": record["$type"], 243 304 "record": record, 244 305 } 245 306 246 - var record_out atproto.RepoCreateRecord_Output 247 - if err := client.Do(ctx, authArgs, xrpc.Procedure, "application/json", "com.atproto.repo.createRecord", nil, reqBody, &record_out); err != nil { 248 - return err 307 + var recordOut atproto.RepoCreateRecord_Output 308 + if err := apiClient.Post(ctx, "com.atproto.repo.createRecord", reqBody, &recordOut); err != nil { 309 + return "", fmt.Errorf("failed to create record: %w", err) 249 310 } 250 311 251 312 // Extract blob CID for server URL ··· 253 314 blobCID := blob.Ref.String() 254 315 converted, err := util.ConvertCIDBase32ToBase62(blobCID) 255 316 if err != nil { 256 - return err 317 + return "", err 257 318 } 258 - compressedUrl := fmt.Sprintf("%s/%s/%s\n", CDN, cfg.Handle, converted) 259 319 260 - fmt.Println(compressedUrl) 261 - return nil 320 + return fmt.Sprintf("%s/%s/%s", CDN, cfg.Handle, converted), nil 262 321 } 263 322 264 - func userAgent() *string { 265 - s := fmt.Sprintf("%s/1.0", Name) 266 - return &s 323 + func setupLogging() { 324 + var level slog.Level 325 + switch strings.ToLower(logLevel) { 326 + case "debug": 327 + level = slog.LevelDebug 328 + case "info": 329 + level = slog.LevelInfo 330 + case "warn": 331 + level = slog.LevelWarn 332 + case "error": 333 + level = slog.LevelError 334 + default: 335 + level = slog.LevelInfo 336 + } 337 + 338 + handler := slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{ 339 + Level: level, 340 + }) 341 + slog.SetDefault(slog.New(handler)) 267 342 }
+22 -49
go.mod
··· 1 1 module tangled.sh/evan.jarrett.net/blup 2 2 3 - go 1.24.4 3 + go 1.25 4 4 5 5 require ( 6 - github.com/bluesky-social/indigo v0.0.0-20250621010046-488d1b91889b 6 + github.com/bluesky-social/indigo v0.0.0-20251223190123-598fbf0e146e 7 7 //github.com/gen2brain/beeep v0.11.1 8 - github.com/golang-jwt/jwt/v5 v5.2.2 9 - github.com/haileyok/atproto-oauth-golang v0.0.3-0.20250622200753-e07caa5274a7 8 + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 10 9 github.com/ipfs/go-cid v0.5.0 11 - github.com/lestrrat-go/jwx/v2 v2.1.6 12 10 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c 13 11 github.com/spf13/cobra v1.9.1 14 12 github.com/zalando/go-keyring v0.2.6 15 - //golang.design/x/clipboard v0.7.1 13 + //golang.design/x/clipboard v0.7.1 16 14 ) 15 + 16 + require github.com/godbus/dbus/v5 v5.1.0 17 17 18 18 require ( 19 19 al.essio.dev/pkg/shellescape v1.6.0 // indirect 20 - github.com/carlmjohnson/versioninfo v0.22.5 // indirect 20 + github.com/beorn7/perks v1.0.1 // indirect 21 + github.com/cespare/xxhash/v2 v2.3.0 // indirect 21 22 github.com/danieljoos/wincred v1.2.2 // indirect 22 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect 23 - github.com/felixge/httpsnoop v1.0.4 // indirect 24 - github.com/go-logr/logr v1.4.3 // indirect 25 - github.com/go-logr/stdr v1.2.2 // indirect 26 - github.com/goccy/go-json v0.10.5 // indirect 27 - github.com/godbus/dbus/v5 v5.1.0 // indirect 28 - github.com/gogo/protobuf v1.3.2 // indirect 29 - github.com/google/uuid v1.6.0 // indirect 30 - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 31 - github.com/hashicorp/go-retryablehttp v0.7.8 // indirect 32 - github.com/hashicorp/golang-lru v1.0.2 // indirect 23 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 24 + github.com/earthboundkid/versioninfo/v2 v2.24.1 // indirect 25 + github.com/google/go-querystring v1.1.0 // indirect 26 + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect 33 27 github.com/inconshreveable/mousetrap v1.1.0 // indirect 34 - github.com/ipfs/bbloom v0.0.4 // indirect 35 - github.com/ipfs/boxo v0.32.0 // indirect 36 - github.com/ipfs/go-block-format v0.2.1 // indirect 37 - github.com/ipfs/go-datastore v0.8.2 // indirect 38 - github.com/ipfs/go-ipfs-blockstore v1.3.1 // indirect 39 - github.com/ipfs/go-ipfs-ds-help v1.1.1 // indirect 40 - github.com/ipfs/go-ipld-cbor v0.2.0 // indirect 41 - github.com/ipfs/go-ipld-format v0.6.1 // indirect 42 - github.com/ipfs/go-log v1.0.5 // indirect 43 - github.com/ipfs/go-log/v2 v2.6.0 // indirect 44 - github.com/ipfs/go-metrics-interface v0.3.0 // indirect 45 28 github.com/klauspost/cpuid/v2 v2.2.10 // indirect 46 - github.com/lestrrat-go/blackmagic v1.0.4 // indirect 47 - github.com/lestrrat-go/httpcc v1.0.1 // indirect 48 - github.com/lestrrat-go/httprc v1.0.6 // indirect 49 - github.com/lestrrat-go/iter v1.0.2 // indirect 50 - github.com/lestrrat-go/option v1.0.1 // indirect 51 - github.com/mattn/go-isatty v0.0.20 // indirect 52 29 github.com/minio/sha256-simd v1.0.1 // indirect 53 30 github.com/mr-tron/base58 v1.2.0 // indirect 54 31 github.com/multiformats/go-base32 v0.1.0 // indirect ··· 56 33 github.com/multiformats/go-multibase v0.2.0 // indirect 57 34 github.com/multiformats/go-multihash v0.2.3 // indirect 58 35 github.com/multiformats/go-varint v0.0.7 // indirect 59 - github.com/opentracing/opentracing-go v1.2.0 // indirect 60 - github.com/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a // indirect 61 - github.com/segmentio/asm v1.2.0 // indirect 36 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 37 + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 38 + github.com/prometheus/client_golang v1.22.0 // indirect 39 + github.com/prometheus/client_model v0.6.2 // indirect 40 + github.com/prometheus/common v0.63.0 // indirect 41 + github.com/prometheus/procfs v0.16.1 // indirect 62 42 github.com/spaolacci/murmur3 v1.1.0 // indirect 63 43 github.com/spf13/pflag v1.0.6 // indirect 64 44 github.com/whyrusleeping/cbor-gen v0.3.1 // indirect 65 - go.opentelemetry.io/auto/sdk v1.1.0 // indirect 66 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect 67 - go.opentelemetry.io/otel v1.36.0 // indirect 68 - go.opentelemetry.io/otel/metric v1.36.0 // indirect 69 - go.opentelemetry.io/otel/trace v1.36.0 // indirect 70 - go.uber.org/atomic v1.11.0 // indirect 71 - go.uber.org/multierr v1.11.0 // indirect 72 - go.uber.org/zap v1.27.0 // indirect 45 + gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b // indirect 46 + gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 // indirect 73 47 golang.org/x/crypto v0.39.0 // indirect 74 - golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 // indirect 75 - golang.org/x/image v0.28.0 // indirect 76 - golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f // indirect 77 48 golang.org/x/sys v0.33.0 // indirect 49 + golang.org/x/time v0.8.0 // indirect 78 50 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect 51 + google.golang.org/protobuf v1.36.6 // indirect 79 52 lukechampine.com/blake3 v1.4.1 // indirect 80 53 )
+30 -733
go.sum
··· 1 1 al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA= 2 2 al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= 3 - cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 4 - cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 5 - cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 6 - cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 7 - cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 8 - cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 9 - cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 10 - cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 11 - cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 12 - cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 13 - cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 14 - cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 15 - cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 16 - cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 17 - cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 18 - cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 19 - cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 20 - cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= 21 - cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= 22 - cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= 23 - cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 24 - cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 25 - cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 26 - cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 27 - cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 28 - cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 29 - cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 30 - cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 31 - cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 32 - cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 33 - cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 34 - cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 35 - cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 36 - cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 37 - cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 38 - cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 39 - cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 40 - cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 41 - dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 42 - github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 43 - github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 44 - github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= 45 - github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 46 - github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 47 - github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 48 - github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 49 - github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= 50 - github.com/bluesky-social/indigo v0.0.0-20250621010046-488d1b91889b h1:QniihTdfvYFr8oJZgltN0VyWSWa28v/0DiIVFHy6nfg= 51 - github.com/bluesky-social/indigo v0.0.0-20250621010046-488d1b91889b/go.mod h1:8FlFpF5cIq3DQG0kEHqyTkPV/5MDQoaWLcVwza5ZPJU= 52 - github.com/carlmjohnson/versioninfo v0.22.5 h1:O00sjOLUAFxYQjlN/bzYTuZiS0y6fWDQjMRvwtKgwwc= 53 - github.com/carlmjohnson/versioninfo v0.22.5/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8= 54 - github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 55 - github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 56 - github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 57 - github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 58 - github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 59 - github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 60 - github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 61 - github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 62 - github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 63 - github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 64 - github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 65 - github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 3 + github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 4 + github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 5 + github.com/bluesky-social/indigo v0.0.0-20251223190123-598fbf0e146e h1:dEM6bfzMfkRI39GLinuhQan47HzdrkqIzJkl/zRvz8s= 6 + github.com/bluesky-social/indigo v0.0.0-20251223190123-598fbf0e146e/go.mod h1:KIy0FgNQacp4uv2Z7xhNkV3qZiUSGuRky97s7Pa4v+o= 7 + github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 8 + github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 66 9 github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 67 10 github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0= 68 11 github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= 69 - github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 70 - github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 71 12 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 72 13 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 73 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= 74 - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= 75 - github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 76 - github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 77 - github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 78 - github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 79 - github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 80 - github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 81 - github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 82 - github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 83 - github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= 84 - github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= 85 - github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 86 - github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 87 - github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 88 - github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 89 - github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 90 - github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 91 - github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 92 - github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 93 - github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= 94 - github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 95 - github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 96 - github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 97 - github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= 98 - github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= 99 - github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= 100 - github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 14 + github.com/earthboundkid/versioninfo/v2 v2.24.1 h1:SJTMHaoUx3GzjjnUO1QzP3ZXK6Ee/nbWyCm58eY3oUg= 15 + github.com/earthboundkid/versioninfo/v2 v2.24.1/go.mod h1:VcWEooDEuyUJnMfbdTh0uFN4cfEIg+kHMuWB2CDCLjw= 101 16 github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= 102 17 github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 103 - github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 104 - github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 105 18 github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= 106 19 github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 107 - github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 108 - github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 109 - github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 110 - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 111 - github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 112 - github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 113 - github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 114 - github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 115 - github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 116 - github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 117 - github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 118 - github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= 119 - github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 120 - github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 121 - github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 122 - github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 123 - github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 124 - github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 125 - github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 126 - github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 127 - github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 128 - github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 129 - github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 130 - github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 131 - github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 132 - github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 133 - github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 134 - github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= 135 - github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 136 - github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 137 - github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 138 - github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 139 - github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 140 - github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 141 - github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 142 - github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 143 - github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 144 - github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 145 20 github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 146 - github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 147 - github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 148 - github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 149 - github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 150 21 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 151 22 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 152 - github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 153 - github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 154 - github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 155 - github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 156 - github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 157 - github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 158 - github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 159 - github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 160 - github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 161 - github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 162 - github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 163 - github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 164 - github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 165 - github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 166 - github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 167 - github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 23 + github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 24 + github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 168 25 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 169 26 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 170 - github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 171 - github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 172 - github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 173 - github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 174 - github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 175 - github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 176 - github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 177 - github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k= 178 - github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 179 - github.com/haileyok/atproto-oauth-golang v0.0.3-0.20250622200753-e07caa5274a7 h1:P3oEaumqbWobYKGRj1Ku4jdgNxMogNlrLZJ097GkKmc= 180 - github.com/haileyok/atproto-oauth-golang v0.0.3-0.20250622200753-e07caa5274a7/go.mod h1:vVRo6BPEmWOZnYk9LtXLzBPzfkY63fUaBahA+o4h55Q= 181 - github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 182 - github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 183 - github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 184 - github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 185 - github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 186 - github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 187 - github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 188 - github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 189 - github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 190 - github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 191 - github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 192 - github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= 193 - github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= 194 - github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 195 - github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 196 - github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 197 - github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 198 - github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 199 - github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 200 - github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 201 - github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 202 - github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= 203 - github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 204 - github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 205 - github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 206 - github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 207 - github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 208 - github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 209 - github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 210 - github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 211 - github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 27 + github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= 28 + github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= 212 29 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 213 30 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 214 - github.com/ipfs/bbloom v0.0.4 h1:Gi+8EGJ2y5qiD5FbsbpX/TMNcJw8gSqr7eyjHa4Fhvs= 215 - github.com/ipfs/bbloom v0.0.4/go.mod h1:cS9YprKXpoZ9lT0n/Mw/a6/aFV6DTjTLYHeA+gyqMG0= 216 - github.com/ipfs/boxo v0.32.0 h1:rBs3P53Wt9bFW9WJwVdkzLtzYCXAj2bMjM7+1nrazZw= 217 - github.com/ipfs/boxo v0.32.0/go.mod h1:VEtO3gOmr+sXGodalaTV9Vvsp3qVYegc4Rcu08Iw+wM= 218 - github.com/ipfs/go-block-format v0.2.1 h1:96kW71XGNNa+mZw/MTzJrCpMhBWCrd9kBLoKm9Iip/Q= 219 - github.com/ipfs/go-block-format v0.2.1/go.mod h1:frtvXHMQhM6zn7HvEQu+Qz5wSTj+04oEH/I+NjDgEjk= 220 31 github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= 221 32 github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk= 222 - github.com/ipfs/go-datastore v0.8.2 h1:Jy3wjqQR6sg/LhyY0NIePZC3Vux19nLtg7dx0TVqr6U= 223 - github.com/ipfs/go-datastore v0.8.2/go.mod h1:W+pI1NsUsz3tcsAACMtfC+IZdnQTnC/7VfPoJBQuts0= 224 - github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= 225 - github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= 226 - github.com/ipfs/go-ipfs-blockstore v1.3.1 h1:cEI9ci7V0sRNivqaOr0elDsamxXFxJMMMy7PTTDQNsQ= 227 - github.com/ipfs/go-ipfs-blockstore v1.3.1/go.mod h1:KgtZyc9fq+P2xJUiCAzbRdhhqJHvsw8u2Dlqy2MyRTE= 228 - github.com/ipfs/go-ipfs-ds-help v1.1.1 h1:B5UJOH52IbcfS56+Ul+sv8jnIV10lbjLF5eOO0C66Nw= 229 - github.com/ipfs/go-ipfs-ds-help v1.1.1/go.mod h1:75vrVCkSdSFidJscs8n4W+77AtTpCIAdDGAwjitJMIo= 230 - github.com/ipfs/go-ipfs-util v0.0.3 h1:2RFdGez6bu2ZlZdI+rWfIdbQb1KudQp3VGwPtdNCmE0= 231 - github.com/ipfs/go-ipfs-util v0.0.3/go.mod h1:LHzG1a0Ig4G+iZ26UUOMjHd+lfM84LZCrn17xAKWBvs= 232 - github.com/ipfs/go-ipld-cbor v0.2.0 h1:VHIW3HVIjcMd8m4ZLZbrYpwjzqlVUfjLM7oK4T5/YF0= 233 - github.com/ipfs/go-ipld-cbor v0.2.0/go.mod h1:Cp8T7w1NKcu4AQJLqK0tWpd1nkgTxEVB5C6kVpLW6/0= 234 - github.com/ipfs/go-ipld-format v0.6.1 h1:lQLmBM/HHbrXvjIkrydRXkn+gc0DE5xO5fqelsCKYOQ= 235 - github.com/ipfs/go-ipld-format v0.6.1/go.mod h1:8TOH1Hj+LFyqM2PjSqI2/ZnyO0KlfhHbJLkbxFa61hs= 236 - github.com/ipfs/go-log v1.0.5 h1:2dOuUCB1Z7uoczMWgAyDck5JLb72zHzrMnGnCNNbvY8= 237 - github.com/ipfs/go-log v1.0.5/go.mod h1:j0b8ZoR+7+R99LD9jZ6+AJsrzkPbSXbZfGakb5JPtIo= 238 - github.com/ipfs/go-log/v2 v2.1.3/go.mod h1:/8d0SH3Su5Ooc31QlL1WysJhvyOTDCjcCZ9Axpmri6g= 239 - github.com/ipfs/go-log/v2 v2.6.0 h1:2Nu1KKQQ2ayonKp4MPo6pXCjqw1ULc9iohRqWV5EYqg= 240 - github.com/ipfs/go-log/v2 v2.6.0/go.mod h1:p+Efr3qaY5YXpx9TX7MoLCSEZX5boSWj9wh86P5HJa8= 241 - github.com/ipfs/go-metrics-interface v0.3.0 h1:YwG7/Cy4R94mYDUuwsBfeziJCVm9pBMJ6q/JR9V40TU= 242 - github.com/ipfs/go-metrics-interface v0.3.0/go.mod h1:OxxQjZDGocXVdyTPocns6cOLwHieqej/jos7H4POwoY= 243 - github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= 244 - github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= 245 - github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 246 - github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 247 - github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 248 - github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 249 - github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 250 - github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 251 - github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 252 33 github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= 253 34 github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= 254 - github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 255 - github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 256 - github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 257 - github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 258 - github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 259 - github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 260 - github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 261 - github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 262 - github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= 263 - github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= 264 - github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= 265 - github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= 266 - github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= 267 - github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= 268 - github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= 269 - github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= 270 - github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA= 271 - github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU= 272 - github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= 273 - github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= 274 - github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 275 - github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 276 - github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 277 - github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 278 - github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 279 - github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 280 - github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 281 - github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 282 35 github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= 283 36 github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= 284 - github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 285 - github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 286 - github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 287 - github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 288 - github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 289 - github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 290 - github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 291 - github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 292 - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 293 - github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 294 - github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 295 37 github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= 296 38 github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= 297 39 github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= ··· 304 46 github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= 305 47 github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= 306 48 github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= 307 - github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= 308 - github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= 309 - github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= 310 - github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= 311 - github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 312 - github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 49 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 50 + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 313 51 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= 314 52 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= 315 - github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 316 - github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= 317 - github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 318 53 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 319 54 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 320 - github.com/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a h1:cgqrm0F3zwf9IPzca7xN4w+Zy6MC9ZkPvAC8QEWa/iQ= 321 - github.com/polydawn/refmt v0.89.1-0.20231129105047-37766d95467a/go.mod h1:ocZfO/tLSHqfScRDNTJbAJR1by4D1lewauX9OwTaPuY= 322 - github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 323 - github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 324 - github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 325 - github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 326 - github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 327 - github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 328 - github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 55 + github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= 56 + github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= 57 + github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= 58 + github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= 59 + github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= 60 + github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= 61 + github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= 62 + github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= 329 63 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 330 - github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 331 - github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 332 - github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= 333 - github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= 334 - github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= 335 - github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= 336 - github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 337 - github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= 338 - github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 339 - github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= 340 - github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec= 341 - github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 342 - github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 343 - github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= 344 - github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= 345 64 github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= 346 65 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 347 - github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= 348 - github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 349 - github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= 350 66 github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= 351 67 github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= 352 - github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 353 - github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 354 68 github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= 355 69 github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 356 - github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= 357 - github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 358 70 github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 359 71 github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 360 - github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 361 - github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 362 - github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 363 - github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 364 - github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 365 - github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 366 - github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 367 72 github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 368 73 github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 369 - github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 370 - github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 371 - github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ= 372 - github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= 373 74 github.com/whyrusleeping/cbor-gen v0.3.1 h1:82ioxmhEYut7LBVGhGq8xoRkXPLElVuh5mV67AFfdv0= 374 75 github.com/whyrusleeping/cbor-gen v0.3.1/go.mod h1:pM99HXyEbSQHcosHc0iW7YFmwnscr+t9Te4ibko05so= 375 - github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 376 - github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 377 - github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 378 - github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 379 - github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 380 - github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 381 76 github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= 382 77 github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= 383 - go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= 384 - go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= 385 - go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= 386 - go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 387 - go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 388 - go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 389 - go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 390 - go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 391 - go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 392 - go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= 393 - go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 394 - go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 395 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= 396 - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= 397 - go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= 398 - go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= 399 - go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= 400 - go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= 401 - go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs= 402 - go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY= 403 - go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= 404 - go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= 405 - go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= 406 - go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= 407 - go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 408 - go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 409 - go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= 410 - go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 411 - go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 412 - go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 413 - go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 414 - go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 415 - go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 416 - go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 417 - go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 418 - go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 419 - go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= 420 - go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= 421 - go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= 422 - golang.design/x/clipboard v0.7.1 h1:OEG3CmcYRBNnRwpDp7+uWLiZi3hrMRJpE9JkkkYtz2c= 423 - golang.design/x/clipboard v0.7.1/go.mod h1:i5SiIqj0wLFw9P/1D7vfILFK0KHMk7ydE72HRrUIgkg= 424 - golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 425 - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 426 - golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 427 - golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 428 - golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 429 - golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 430 - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 431 - golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 432 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 78 + gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b h1:CzigHMRySiX3drau9C6Q5CAbNIApmLdat5jPMqChvDA= 79 + gitlab.com/yawning/secp256k1-voi v0.0.0-20230925100816-f2616030848b/go.mod h1:/y/V339mxv2sZmYYR64O07VuCpdNZqCTwO8ZcouTMI8= 80 + gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02 h1:qwDnMxjkyLmAFgcfgTnfJrmYKWhHnci3GjDqcZp1M3Q= 81 + gitlab.com/yawning/tuplehash v0.0.0-20230713102510-df83abbf9a02/go.mod h1:JTnUj0mpYiAsuZLmKjTx/ex3AtMowcCgnE7YNyCEP0I= 433 82 golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= 434 83 golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= 435 - golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 436 - golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 437 - golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 438 - golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 439 - golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 440 - golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 441 - golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 442 - golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 443 - golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 444 - golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 445 - golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476 h1:Wdx0vgH5Wgsw+lF//LJKmWOJBLWX6nprsMqnf99rYDE= 446 - golang.org/x/exp/shiny v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:ygj7T6vSGhhm/9yTpOQQNvuAUFziTH7RUiH74EoE2C8= 447 - golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 448 - golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 449 - golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE= 450 - golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY= 451 - golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 452 - golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 453 - golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 454 - golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 455 - golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 456 - golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 457 - golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 458 - golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 459 - golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 460 - golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 461 - golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 462 - golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 463 - golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 464 - golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 465 - golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f h1:/n+PL2HlfqeSiDCuhdBbRNlGS/g2fM4OHufalHaTVG8= 466 - golang.org/x/mobile v0.0.0-20250606033058-a2a15c67f36f/go.mod h1:ESkJ836Z6LpG6mTVAhA48LpfW/8fNR0ifStlH2axyfg= 467 - golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 468 - golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 469 - golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 470 - golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 471 - golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 472 - golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 473 - golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 474 - golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 475 - golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 476 - golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 477 - golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 478 - golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 479 - golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 480 - golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 481 - golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 482 - golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 483 - golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 484 - golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 485 - golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 486 - golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 487 - golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 488 - golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 489 - golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 490 - golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 491 - golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 492 - golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 493 - golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 494 - golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 495 - golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 496 - golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 497 - golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 498 - golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 499 - golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 500 - golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 501 - golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 502 - golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 503 - golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 504 - golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 505 - golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 506 - golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 507 - golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 508 - golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 509 - golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 510 - golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 511 - golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 512 - golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 513 - golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= 514 - golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 515 - golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 516 - golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 517 - golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 518 - golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 519 - golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 520 - golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 521 - golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 522 - golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 523 - golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 524 - golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 525 - golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 526 - golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 527 - golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 528 - golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 529 - golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 530 - golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 531 - golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 532 - golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 533 - golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 534 - golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 535 - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 536 - golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 537 - golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 538 - golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 539 - golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 540 - golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 541 - golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 542 - golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 543 - golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 544 - golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 545 - golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 546 - golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 547 - golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 548 - golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 549 - golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 550 - golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 551 - golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 552 - golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 553 - golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 554 - golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 555 - golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 556 - golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 557 - golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 558 - golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 559 - golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 560 - golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 561 - golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 562 - golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 563 - golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 564 - golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 565 - golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 566 - golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 567 - golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 568 - golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 569 - golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 570 - golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 571 - golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 572 - golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 573 - golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 574 - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 575 - golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 576 - golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 577 - golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 578 - golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 579 - golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 580 - golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 581 - golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 582 - golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 583 - golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 584 - golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 585 - golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 586 - golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 587 - golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 588 - golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 589 84 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 590 - golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 591 - golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 592 85 golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= 593 86 golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 594 - golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 595 - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 596 - golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 597 - golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 598 - golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 599 - golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 600 - golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 601 - golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 602 - golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 603 - golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 604 - golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 605 - golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 606 - golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 607 - golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 608 - golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 609 - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 610 - golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 611 - golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 612 - golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 613 - golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 614 - golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 615 - golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 616 - golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 617 - golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 618 - golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 619 - golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 620 - golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 621 - golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 622 - golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 623 - golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 624 - golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 625 - golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 626 - golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 627 - golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 628 - golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 629 - golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 630 - golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 631 - golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 632 - golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 633 - golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 634 - golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 635 - golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 636 - golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 637 - golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 638 - golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 639 - golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 640 - golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 641 - golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 642 - golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 643 - golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 644 - golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 645 - golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 646 - golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 647 - golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 648 - golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 649 - golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 650 - golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 651 - golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 652 - golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 653 - golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 654 - golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 655 - golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 656 - golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 657 - golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 658 - golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 659 - golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 660 - golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 661 - golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 662 - golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 663 - golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 664 - golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 665 - golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 666 - golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 667 - golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 668 - golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 669 - golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 87 + golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= 88 + golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 670 89 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 671 - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 672 90 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= 673 91 golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= 674 - google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 675 - google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 676 - google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 677 - google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 678 - google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 679 - google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 680 - google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 681 - google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 682 - google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 683 - google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 684 - google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 685 - google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 686 - google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 687 - google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 688 - google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 689 - google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 690 - google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 691 - google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 692 - google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 693 - google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= 694 - google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= 695 - google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8= 696 - google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 697 - google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 698 - google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 699 - google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 700 - google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 701 - google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 702 - google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 703 - google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 704 - google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 705 - google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 706 - google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 707 - google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 708 - google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 709 - google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 710 - google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 711 - google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 712 - google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 713 - google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 714 - google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 715 - google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 716 - google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 717 - google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 718 - google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 719 - google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 720 - google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 721 - google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 722 - google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 723 - google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 724 - google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 725 - google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 726 - google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 727 - google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 728 - google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 729 - google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 730 - google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 731 - google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 732 - google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 733 - google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 734 - google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 735 - google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 736 - google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 737 - google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 738 - google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 739 - google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 740 - google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 741 - google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 742 - google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= 743 - google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= 744 - google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 745 - google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 746 - google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 747 - google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 748 - google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 749 - google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 750 - google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 751 - google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 752 - google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 753 - google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 754 - google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 755 - google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 756 - google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 757 - google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= 758 - google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 759 - google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 760 - google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 761 - google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 762 - google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 763 - google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= 764 - google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 765 - google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 766 - google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 767 - google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 768 - google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 769 - google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 770 - google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 771 - google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 772 - google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 773 - google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 774 - google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 775 - google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 92 + google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 93 + google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 776 94 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 777 - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 778 - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 779 - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 780 - gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 781 - gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 782 - gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 783 - gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 784 - gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 785 - gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 786 - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 787 - gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 788 95 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 789 96 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 790 - honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 791 - honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 792 - honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 793 - honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 794 - honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 795 - honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 796 - honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 797 97 lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= 798 98 lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo= 799 - rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 800 - rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 801 - rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
+2 -2
internal/auth/client-metadata.json
··· 1 1 { 2 - "client_id": "https://blup.imgs.blue/oauth/client-metadata.json", 2 + "client_id": "https://blup.imgs.blue/oauth-client-metadata.json", 3 3 "client_name": "Blup", 4 4 "client_uri": "https://blup.imgs.blue", 5 5 "grant_types": [ 6 6 "authorization_code", 7 7 "refresh_token" 8 8 ], 9 - "scope": "atproto transition:generic", 9 + "scope": "atproto repo:blue.imgs.blup.image blob:image/*", 10 10 "response_types": [ 11 11 "code" 12 12 ],
+20 -2
internal/auth/metadata.go
··· 3 3 import ( 4 4 _ "embed" 5 5 "encoding/json" 6 + "strings" 7 + 8 + "github.com/bluesky-social/indigo/atproto/auth/oauth" 6 9 ) 7 10 8 11 // NOTE: if the server-side metadata changes, this file will also need to change. 12 + // 9 13 //go:embed client-metadata.json 10 14 var metadataJson []byte 15 + 11 16 type ClientMetadata struct { 12 17 ClientID string `json:"client_id"` 13 18 ClientName string `json:"client_name"` ··· 23 28 TokenEndpointAuthMethod string `json:"token_endpoint_auth_method"` 24 29 TokenEndpointAuthSigningAlg string `json:"token_endpoint_auth_signing_alg"` 25 30 } 26 - 27 31 28 32 func GetClientMetadata() ClientMetadata { 29 33 var cm ClientMetadata 30 34 json.Unmarshal(metadataJson, &cm) 31 35 return cm 32 - } 36 + } 37 + 38 + // GetClientConfig returns the OAuth client configuration for indigo 39 + func GetClientConfig() oauth.ClientConfig { 40 + metadata := GetClientMetadata() 41 + 42 + // Split space-separated scopes from metadata into slice 43 + scopes := strings.Fields(metadata.Scope) 44 + 45 + return oauth.NewPublicConfig( 46 + metadata.ClientID, 47 + metadata.RedirectURIs[0], 48 + scopes, 49 + ) 50 + }
+183 -310
internal/auth/oauth.go
··· 5 5 "context" 6 6 "encoding/json" 7 7 "fmt" 8 - "io" 9 - "net" 8 + "log/slog" 10 9 "net/http" 11 10 "net/url" 12 11 "strings" 13 12 "time" 14 13 15 - "github.com/bluesky-social/indigo/atproto/syntax" 16 - "github.com/golang-jwt/jwt/v5" 17 - oauth "github.com/haileyok/atproto-oauth-golang" 18 - oauth_helpers "github.com/haileyok/atproto-oauth-golang/helpers" 19 - "github.com/lestrrat-go/jwx/v2/jwk" 14 + "github.com/bluesky-social/indigo/atproto/auth/oauth" 20 15 "github.com/pkg/browser" 21 16 "tangled.sh/evan.jarrett.net/blup/internal/config" 22 17 ) 23 18 24 19 type OAuthFlow struct { 25 - client *oauth.Client 20 + app *oauth.ClientApp 26 21 cfg *config.Config 27 - jwk jwk.Key 28 - server *http.Server 29 - authSuccess chan *StoredTokens 22 + store *KeyringAuthStore 23 + authSuccess chan *oauth.ClientSessionData 30 24 authError chan error 31 25 savedState string 32 - issuer string 33 - nonce string 34 - verifier string 35 26 } 36 27 37 28 func NewOAuthFlow(cfg *config.Config) (*OAuthFlow, error) { 38 - // Get or generate JWKS 39 - key, err := GetStoredJWKS() 40 - if err != nil { 41 - // Generate new key if none exists 42 - var prefix *string 43 - key, err = oauth_helpers.GenerateKey(prefix) 44 - if err != nil { 45 - return nil, fmt.Errorf("failed to generate key: %w", err) 46 - } 47 - 48 - // Store the new key 49 - if err := StoreJWKS(key); err != nil { 50 - return nil, fmt.Errorf("failed to store JWKS: %w", err) 51 - } 52 - } 29 + store := NewKeyringAuthStore() 30 + clientConfig := GetClientConfig() 53 31 54 - clientMetadata := GetClientMetadata() 32 + // Debug: show what we're requesting 33 + metadata := GetClientMetadata() 34 + slog.Debug("OAuth config", 35 + "client_id", clientConfig.ClientID, 36 + "callback_url", clientConfig.CallbackURL, 37 + "scopes", clientConfig.Scopes, 38 + "scope_raw", metadata.Scope, 39 + ) 55 40 56 - // Create OAuth client 57 - clientConfig := oauth.ClientArgs{ 58 - ClientJwk: key, 59 - ClientId: clientMetadata.ClientID, 60 - RedirectUri: clientMetadata.RedirectURIs[0], 61 - } 62 - 63 - client, err := oauth.NewClient(clientConfig) 64 - if err != nil { 65 - return nil, fmt.Errorf("failed to create OAuth client: %w", err) 66 - } 41 + app := oauth.NewClientApp(&clientConfig, store) 67 42 68 43 return &OAuthFlow{ 69 - client: client, 44 + app: app, 70 45 cfg: cfg, 71 - jwk: key, 72 - authSuccess: make(chan *StoredTokens, 1), 46 + store: store, 47 + authSuccess: make(chan *oauth.ClientSessionData, 1), 73 48 authError: make(chan error, 1), 74 49 }, nil 75 50 } 76 51 77 - func (f *OAuthFlow) Authenticate() (*StoredTokens, error) { 52 + func (f *OAuthFlow) Authenticate() (*oauth.ClientSessionData, error) { 78 53 ctx := context.Background() 79 - var authServer, did, pdsHost string 80 - var err error 81 - 82 - // Initialize authorization 83 54 cfg := f.cfg 84 55 85 - if cfg.Handle != "" { 86 - did, err = ResolveHandle(ctx, cfg.Handle) 87 - if err != nil { 88 - return nil, err 89 - } 90 - pdsHost, err = ResolveService(ctx, did) 91 - if err != nil { 92 - return nil, err 93 - } 94 - authServer, err = f.client.ResolvePdsAuthServer(ctx, pdsHost) 95 - if err != nil { 96 - return nil, err 97 - } 98 - cfg.PDSHost = pdsHost 99 - cfg.AuthserverIss = authServer 100 - config.SaveConfig(cfg) 101 - 102 - } else { 103 - authServer = cfg.AuthserverIss 56 + if cfg.Handle == "" { 57 + return nil, fmt.Errorf("handle is required") 104 58 } 105 59 106 - meta, err := f.client.FetchAuthServerMetadata(ctx, authServer) 60 + // Start the OAuth flow - this handles handle resolution, PAR, etc. 61 + // Note: StartAuthFlow internally calls SaveAuthRequestInfo which stores the state 62 + redirectURL, err := f.app.StartAuthFlow(ctx, cfg.Handle) 107 63 if err != nil { 108 - return nil, err 64 + return nil, fmt.Errorf("failed to start auth flow: %w", err) 109 65 } 110 66 111 - clientMetadata := GetClientMetadata() 112 - 113 - parResp, err := f.client.SendParAuthRequest(ctx, authServer, meta, cfg.Handle, clientMetadata.Scope, f.jwk) 67 + // Get the state from our store (saved by SaveAuthRequestInfo during StartAuthFlow) 68 + f.savedState, err = f.store.GetPendingAuthState() 114 69 if err != nil { 115 - return nil, err 70 + return nil, fmt.Errorf("failed to get auth state: %w", err) 116 71 } 117 - 118 - f.savedState = parResp.State 119 - f.issuer = meta.Issuer 120 - f.verifier = parResp.PkceVerifier 121 - f.nonce = parResp.DpopAuthserverNonce 122 - 123 - authUrl, _ := url.Parse(meta.AuthorizationEndpoint) 124 - authUrl.RawQuery = fmt.Sprintf("client_id=%s&request_uri=%s", url.QueryEscape(clientMetadata.ClientID), parResp.RequestUri) 72 + slog.Debug("OAuth state retrieved", "state", f.savedState) 125 73 126 74 go f.waitForAuthSSE() 127 75 128 76 // Open browser to authorization URL 129 77 fmt.Printf("Opening browser for authentication...\n") 130 - if err := browser.OpenURL(authUrl.String()); err != nil { 78 + if err := browser.OpenURL(redirectURL); err != nil { 131 79 fmt.Printf("Failed to open browser automatically.\n") 132 - fmt.Printf("Please open this URL manually:\n%s\n", authUrl) 80 + fmt.Printf("Please open this URL manually:\n%s\n", redirectURL) 133 81 } 134 82 135 83 // Wait for callback 136 84 fmt.Println("Waiting for authentication...") 137 85 138 86 select { 139 - case tokens := <-f.authSuccess: 87 + case sess := <-f.authSuccess: 140 88 fmt.Println("Authentication successful!") 141 - return tokens, nil 89 + 90 + // Save as current session 91 + if err := f.store.SetCurrentSession(ctx, sess); err != nil { 92 + return nil, fmt.Errorf("failed to save current session: %w", err) 93 + } 94 + 95 + // Update config with PDS host 96 + cfg.PDSHost = sess.HostURL 97 + cfg.AuthserverIss = sess.AuthServerURL 98 + config.SaveConfig(cfg) 99 + 100 + return sess, nil 142 101 case err := <-f.authError: 143 102 return nil, fmt.Errorf("authentication failed: %w", err) 144 103 case <-time.After(5 * time.Minute): ··· 146 105 } 147 106 } 148 107 149 - func (f *OAuthFlow) waitForAuthSSE() (string, error) { 108 + func (f *OAuthFlow) waitForAuthSSE() { 150 109 clientMetadata := GetClientMetadata() 151 - req, err := http.NewRequest("GET", 152 - fmt.Sprintf("%s/oauth/events?session=%s", clientMetadata.ClientURI, f.savedState), 153 - nil) 154 - if err != nil { 155 - f.authError <- err 156 - } 110 + sseURL := fmt.Sprintf("%s/oauth/events?session=%s", clientMetadata.ClientURI, f.savedState) 111 + slog.Debug("SSE connecting", "url", sseURL) 157 112 158 - req.Header.Set("Accept", "text/event-stream") 159 - req.Header.Set("Cache-Control", "no-cache") 113 + req, err := http.NewRequest("GET", sseURL, nil) 114 + if err != nil { 115 + f.authError <- err 116 + return 117 + } 160 118 161 - client := &http.Client{Timeout: 5 * time.Minute} 162 - resp, err := client.Do(req) 163 - if err != nil { 164 - f.authError <- err 165 - } 166 - defer resp.Body.Close() 119 + req.Header.Set("Accept", "text/event-stream") 120 + req.Header.Set("Cache-Control", "no-cache") 167 121 168 - reader := bufio.NewReader(resp.Body) 122 + client := &http.Client{Timeout: 5 * time.Minute} 123 + resp, err := client.Do(req) 124 + if err != nil { 125 + slog.Debug("SSE connection error", "error", err) 126 + f.authError <- err 127 + return 128 + } 129 + defer resp.Body.Close() 130 + 131 + slog.Debug("SSE connected", "status", resp.StatusCode) 132 + reader := bufio.NewReader(resp.Body) 133 + 134 + for { 135 + line, err := reader.ReadString('\n') 136 + if err != nil { 137 + slog.Debug("SSE read error", "error", err) 138 + f.authError <- err 139 + return 140 + } 169 141 170 - for { 171 - line, err := reader.ReadString('\n') 172 - if err != nil { 173 - f.authError <- err 174 - } 142 + line = strings.TrimSpace(line) 143 + if line != "" && !strings.HasPrefix(line, ":") { 144 + slog.Debug("SSE received", "line", line) 145 + } 175 146 176 - if strings.HasPrefix(line, "event: auth-complete") { 177 - // Read data line 178 - dataLine, _ := reader.ReadString('\n') 179 - if strings.HasPrefix(dataLine, "data: ") { 180 - data := strings.TrimPrefix(dataLine, "data: ") 181 - var authData struct { 182 - Code string `json:"code"` 183 - Iss string `json:"iss"` 147 + if strings.HasPrefix(line, "event: auth-complete") { 148 + // Read data line 149 + dataLine, _ := reader.ReadString('\n') 150 + slog.Debug("SSE auth data", "data", dataLine) 151 + if after, ok := strings.CutPrefix(dataLine, "data: "); ok { 152 + data := after 153 + var authData struct { 154 + Code string `json:"code"` 155 + Iss string `json:"iss"` 184 156 State string `json:"state"` 185 - } 186 - json.Unmarshal([]byte(data), &authData) 187 - f.handleCallback(req.Context(), authData.Code, authData.Iss, authData.State) 188 - } 189 - } 190 - } 157 + } 158 + if err := json.Unmarshal([]byte(data), &authData); err != nil { 159 + f.authError <- fmt.Errorf("failed to parse auth data: %w", err) 160 + return 161 + } 162 + slog.Debug("SSE auth complete", "iss", authData.Iss, "state", authData.State) 163 + f.handleCallback(req.Context(), authData.Code, authData.Iss, authData.State) 164 + return 165 + } 166 + } 167 + } 191 168 } 192 169 193 170 func (f *OAuthFlow) handleCallback(ctx context.Context, code, iss, state string) { 194 - // Get authorization code from query params 195 171 if state == "" || iss == "" || code == "" { 196 172 f.authError <- fmt.Errorf("request missing needed parameters") 173 + return 197 174 } 198 175 199 - if state != f.savedState { 200 - f.authError <- fmt.Errorf("session state does not match response state") 201 - } 202 - 203 - if iss != f.issuer { 204 - f.authError <- fmt.Errorf("incoming iss did not match authserver iss") 205 - } 176 + // Build URL values for ProcessCallback 177 + params := url.Values{} 178 + params.Set("code", code) 179 + params.Set("iss", iss) 180 + params.Set("state", state) 206 181 207 - initialTokenResp, err := f.client.InitialTokenRequest(ctx, code, iss, f.verifier, f.nonce, f.jwk) 182 + // ProcessCallback handles token exchange and session creation 183 + sess, err := f.app.ProcessCallback(ctx, params) 208 184 if err != nil { 209 - f.authError <- err 185 + f.authError <- fmt.Errorf("failed to process callback: %w", err) 186 + return 210 187 } 211 188 212 - if initialTokenResp.Scope != GetClientMetadata().Scope { 213 - f.authError <- fmt.Errorf("did not receive correct scopes from token request") 214 - } 215 - 216 - // Store tokens 217 - tokens, err := StoreTokensFromLibrary(initialTokenResp) 218 - if err != nil { 219 - f.authError <- fmt.Errorf("failed to store tokens: %w", err) 220 - } 221 - 222 - // Send code through channel 223 - f.authSuccess <- tokens 189 + f.authSuccess <- sess 224 190 } 225 191 226 - // RefreshTokens refreshes the access token using the stored refresh token 227 - func RefreshTokens(cfg *config.Config) (*StoredTokens, error) { 228 - // Check if we have valid tokens 229 - tokens, _ := GetStoredTokens() 192 + // GetSession retrieves the current session, refreshing tokens if needed 193 + func GetSession(cfg *config.Config) (*oauth.ClientSession, error) { 194 + ctx := context.Background() 195 + store := NewKeyringAuthStore() 230 196 231 - flow, err := NewOAuthFlow(cfg) 197 + // Check for current session 198 + sessData, err := store.GetCurrentSession(ctx) 232 199 if err != nil { 233 - return nil, err 234 - } 235 - var newTokens *oauth.TokenResponse 236 - 237 - if tokens == nil { 238 - return flow.Authenticate() 239 - } 240 - if tokens.NeedsRefresh() { 241 - ctx := context.Background() 242 - newTokens, err = flow.client.RefreshTokenRequest(ctx, tokens.RefreshToken, cfg.AuthserverIss, tokens.DpopAuthserverNonce, flow.jwk) 243 - 244 - if err != nil { 245 - return nil, fmt.Errorf("failed to refresh tokens: %w", err) 246 - } 247 - return StoreTokensFromLibrary(newTokens) 200 + return nil, fmt.Errorf("no active session") 248 201 } 249 202 250 - return tokens, nil 251 - } 203 + clientConfig := GetClientConfig() 204 + app := oauth.NewClientApp(&clientConfig, store) 252 205 253 - func GetPDSFromToken(accessToken string) (string, error) { 254 - // Parse without verification (we just need to read claims) 255 - token, _, err := new(jwt.Parser).ParseUnverified(accessToken, jwt.MapClaims{}) 206 + // Resume the session 207 + sess, err := app.ResumeSession(ctx, sessData.AccountDID, sessData.SessionID) 256 208 if err != nil { 257 - return "", fmt.Errorf("failed to parse JWT: %w", err) 209 + return nil, fmt.Errorf("failed to resume session: %w", err) 258 210 } 259 211 260 - claims, ok := token.Claims.(jwt.MapClaims) 261 - if !ok { 262 - return "", fmt.Errorf("failed to get claims") 263 - } 264 - 265 - // Extract the 'aud' field 266 - aud, ok := claims["aud"].(string) 267 - if !ok { 268 - return "", fmt.Errorf("'aud' field not found or not a string") 269 - } 270 - 271 - // Convert did:web:... to https://... 272 - if after, ok0 :=strings.CutPrefix(aud, "did:web:"); ok0 { 273 - pdsHost := after 274 - return "https://" + pdsHost, nil 275 - } 276 - 277 - return "", fmt.Errorf("unexpected 'aud' format: %s", aud) 212 + return sess, nil 278 213 } 279 214 280 - func ResolveHandle(ctx context.Context, handle string) (string, error) { 281 - var did string 215 + // RefreshTokens refreshes the access token if needed and returns the session 216 + func RefreshTokens(cfg *config.Config) (*oauth.ClientSession, error) { 217 + ctx := context.Background() 218 + store := NewKeyringAuthStore() 282 219 283 - _, err := syntax.ParseHandle(handle) 284 - if err != nil { 285 - return "", err 286 - } 287 - 288 - recs, err := net.LookupTXT(fmt.Sprintf("_atproto.%s", handle)) 289 - if err == nil { 290 - for _, rec := range recs { 291 - if strings.HasPrefix(rec, "did=") { 292 - did = strings.Split(rec, "did=")[1] 293 - break 294 - } 295 - } 296 - } 297 - 298 - // Try external DNS if system DNS failed or returned no DID 299 - if did == "" { 300 - externalDNS := []string{"8.8.8.8"} 301 - for _, dnsServer := range externalDNS { 302 - externalDID, err := lookupTXTExternal(fmt.Sprintf("_atproto.%s", handle), dnsServer) 303 - if err == nil && externalDID != "" { 304 - did = externalDID 305 - break 306 - } 307 - } 308 - } 309 - 310 - if did == "" { 311 - req, err := http.NewRequestWithContext( 312 - ctx, 313 - "GET", 314 - fmt.Sprintf("https://%s/.well-known/atproto-did", handle), 315 - nil, 316 - ) 220 + // Check for legacy tokens and migrate 221 + if HasLegacyTokens() { 222 + DeleteLegacyTokens() 223 + fmt.Println("Your stored credentials are from an older version.") 224 + fmt.Println("Please re-authenticate.") 225 + // Trigger new auth flow 226 + flow, err := NewOAuthFlow(cfg) 317 227 if err != nil { 318 - return "", err 228 + return nil, err 319 229 } 320 - 321 - resp, err := http.DefaultClient.Do(req) 230 + sess, err := flow.Authenticate() 322 231 if err != nil { 323 - return "", err 232 + return nil, err 324 233 } 325 - defer resp.Body.Close() 326 - 327 - if resp.StatusCode != http.StatusOK { 328 - io.Copy(io.Discard, resp.Body) 329 - return "", fmt.Errorf("unable to resolve handle") 330 - } 234 + // Resume the session to return a ClientSession 235 + clientConfig := GetClientConfig() 236 + app := oauth.NewClientApp(&clientConfig, store) 237 + return app.ResumeSession(ctx, sess.AccountDID, sess.SessionID) 238 + } 331 239 332 - b, err := io.ReadAll(resp.Body) 240 + // Check for current session 241 + sessData, err := store.GetCurrentSession(ctx) 242 + if err != nil { 243 + // No session, need to authenticate 244 + flow, err := NewOAuthFlow(cfg) 333 245 if err != nil { 334 - return "", err 246 + return nil, err 335 247 } 336 - 337 - maybeDid := string(b) 338 - 339 - if _, err := syntax.ParseDID(maybeDid); err != nil { 340 - return "", fmt.Errorf("unable to resolve handle") 248 + sess, err := flow.Authenticate() 249 + if err != nil { 250 + return nil, err 341 251 } 342 - 343 - did = maybeDid 344 - } 345 - 346 - return did, nil 347 - } 348 - 349 - func lookupTXTExternal(domain, dnsServer string) (string, error) { 350 - resolver := &net.Resolver{ 351 - PreferGo: true, 352 - Dial: func(ctx context.Context, network, address string) (net.Conn, error) { 353 - d := net.Dialer{ 354 - Timeout: 5 * time.Second, 355 - } 356 - return d.DialContext(ctx, network, dnsServer+":53") 357 - }, 252 + // Resume the session to return a ClientSession 253 + clientConfig := GetClientConfig() 254 + app := oauth.NewClientApp(&clientConfig, store) 255 + return app.ResumeSession(ctx, sess.AccountDID, sess.SessionID) 358 256 } 359 257 360 - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 361 - defer cancel() 258 + clientConfig := GetClientConfig() 259 + app := oauth.NewClientApp(&clientConfig, store) 362 260 363 - recs, err := resolver.LookupTXT(ctx, domain) 261 + // Resume the session - this will auto-refresh tokens on 401 262 + sess, err := app.ResumeSession(ctx, sessData.AccountDID, sessData.SessionID) 364 263 if err != nil { 365 - return "", err 366 - } 367 - 368 - for _, rec := range recs { 369 - if strings.HasPrefix(rec, "did=") { 370 - return strings.Split(rec, "did=")[1], nil 264 + // Session invalid, need to re-authenticate 265 + flow, err := NewOAuthFlow(cfg) 266 + if err != nil { 267 + return nil, err 268 + } 269 + newSess, err := flow.Authenticate() 270 + if err != nil { 271 + return nil, err 371 272 } 273 + return app.ResumeSession(ctx, newSess.AccountDID, newSess.SessionID) 372 274 } 373 275 374 - return "", nil 276 + return sess, nil 375 277 } 376 278 377 - func ResolveService(ctx context.Context, did string) (string, error) { 378 - type Identity struct { 379 - Service []struct { 380 - ID string `json:"id"` 381 - Type string `json:"type"` 382 - ServiceEndpoint string `json:"serviceEndpoint"` 383 - } `json:"service"` 384 - } 279 + // Logout revokes tokens and clears the session 280 + func Logout(cfg *config.Config) error { 281 + ctx := context.Background() 282 + store := NewKeyringAuthStore() 385 283 386 - var ustr string 387 - if strings.HasPrefix(did, "did:plc:") { 388 - ustr = fmt.Sprintf("https://plc.directory/%s", did) 389 - } else if strings.HasPrefix(did, "did:web:") { 390 - ustr = fmt.Sprintf("https://%s/.well-known/did.json", strings.TrimPrefix(did, "did:web:")) 391 - } else { 392 - return "", fmt.Errorf("did was not a supported did type") 393 - } 394 - 395 - req, err := http.NewRequestWithContext(ctx, "GET", ustr, nil) 284 + sessData, err := store.GetCurrentSession(ctx) 396 285 if err != nil { 397 - return "", err 286 + // No session to logout 287 + return nil 398 288 } 399 289 400 - resp, err := http.DefaultClient.Do(req) 401 - if err != nil { 402 - return "", err 403 - } 404 - defer resp.Body.Close() 290 + clientConfig := GetClientConfig() 291 + app := oauth.NewClientApp(&clientConfig, store) 405 292 406 - if resp.StatusCode != 200 { 407 - io.Copy(io.Discard, resp.Body) 408 - return "", fmt.Errorf("could not find identity in plc registry") 409 - } 410 - 411 - var identity Identity 412 - if err := json.NewDecoder(resp.Body).Decode(&identity); err != nil { 413 - return "", err 414 - } 415 - 416 - var service string 417 - for _, svc := range identity.Service { 418 - if svc.ID == "#atproto_pds" { 419 - service = svc.ServiceEndpoint 420 - } 293 + // Logout revokes tokens and deletes session 294 + if err := app.Logout(ctx, sessData.AccountDID, sessData.SessionID); err != nil { 295 + // Log but don't fail - just clear local state 296 + fmt.Printf("Warning: failed to revoke tokens: %v\n", err) 421 297 } 422 298 423 - if service == "" { 424 - return "", fmt.Errorf("could not find atproto_pds service in identity services") 425 - } 426 - 427 - return service, nil 299 + // Clear current session reference 300 + return store.ClearCurrentSession() 428 301 }
+116 -67
internal/auth/storage.go
··· 1 1 package auth 2 2 3 3 import ( 4 + "context" 4 5 "encoding/json" 5 - "time" 6 + "fmt" 6 7 7 - oauth "github.com/haileyok/atproto-oauth-golang" 8 - "github.com/lestrrat-go/jwx/v2/jwk" 8 + "github.com/bluesky-social/indigo/atproto/auth/oauth" 9 + "github.com/bluesky-social/indigo/atproto/syntax" 9 10 "github.com/zalando/go-keyring" 10 11 ) 11 12 12 13 const ( 13 - keyringService = "blup" 14 - keyringUser = "oauth-tokens" 15 - keyringJWKS = "oauth-jwks" // Separate key for JWKS 14 + keyringService = "blup" 15 + currentSessionKey = "current-session" 16 + sessionKeyPrefix = "session:" 17 + authRequestPrefix = "auth-request:" 18 + pendingAuthStateKey = "pending-auth-state" 19 + legacyTokensKey = "oauth-tokens" 20 + legacyJWKSKey = "oauth-jwks" 16 21 ) 17 22 18 - type StoredTokens struct { 19 - DpopAuthserverNonce string `json:"nonce"` 20 - AccessToken string `json:"access_token"` 21 - RefreshToken string `json:"refresh_token"` 22 - ExpiresAt int64 `json:"expires_at"` 23 - Scope string `json:"scope"` 24 - Sub string `json:"sub"` 25 - TokenType string `json:"token_type"` 23 + // KeyringAuthStore implements oauth.ClientAuthStore using the system keyring 24 + type KeyringAuthStore struct{} 25 + 26 + func NewKeyringAuthStore() *KeyringAuthStore { 27 + return &KeyringAuthStore{} 26 28 } 27 29 28 - // StoreJWKS stores the private key separately 29 - func StoreJWKS(key jwk.Key) error { 30 - // Convert the key to JSON 31 - data, err := json.Marshal(key) 32 - if err != nil { 33 - return err 34 - } 35 - 36 - return keyring.Set(keyringService, keyringJWKS, string(data)) 30 + // sessionKey creates the keyring key for a session 31 + func sessionKey(did syntax.DID, sessionID string) string { 32 + return fmt.Sprintf("%s%s:%s", sessionKeyPrefix, did.String(), sessionID) 37 33 } 38 34 39 - // GetStoredJWKS retrieves the stored private key 40 - func GetStoredJWKS() (jwk.Key, error) { 41 - data, err := keyring.Get(keyringService, keyringJWKS) 35 + // GetSession retrieves a session from the keyring 36 + func (s *KeyringAuthStore) GetSession(ctx context.Context, did syntax.DID, sessionID string) (*oauth.ClientSessionData, error) { 37 + data, err := keyring.Get(keyringService, sessionKey(did, sessionID)) 42 38 if err != nil { 43 39 return nil, err 44 40 } 45 41 46 - // Parse the key from JSON 47 - key, err := jwk.ParseKey([]byte(data)) 48 - if err != nil { 42 + var sess oauth.ClientSessionData 43 + if err := json.Unmarshal([]byte(data), &sess); err != nil { 49 44 return nil, err 50 45 } 51 46 52 - return key, nil 47 + return &sess, nil 53 48 } 54 49 55 - func StoreTokensFromLibrary(tokens *oauth.TokenResponse) (*StoredTokens, error) { 56 - stored := &StoredTokens{ 57 - DpopAuthserverNonce: tokens.DpopAuthserverNonce, 58 - AccessToken: tokens.AccessToken, 59 - RefreshToken: tokens.RefreshToken, 60 - ExpiresAt: time.Now().Unix() + int64(tokens.ExpiresIn), 61 - Scope: tokens.Scope, 62 - Sub: tokens.Sub, 63 - TokenType: tokens.TokenType, 50 + // SaveSession stores a session in the keyring 51 + func (s *KeyringAuthStore) SaveSession(ctx context.Context, sess oauth.ClientSessionData) error { 52 + data, err := json.Marshal(sess) 53 + if err != nil { 54 + return err 64 55 } 65 56 66 - data, err := json.Marshal(stored) 57 + return keyring.Set(keyringService, sessionKey(sess.AccountDID, sess.SessionID), string(data)) 58 + } 59 + 60 + // DeleteSession removes a session from the keyring 61 + func (s *KeyringAuthStore) DeleteSession(ctx context.Context, did syntax.DID, sessionID string) error { 62 + return keyring.Delete(keyringService, sessionKey(did, sessionID)) 63 + } 64 + 65 + // authRequestKey creates the keyring key for an auth request 66 + func authRequestKey(state string) string { 67 + return fmt.Sprintf("%s%s", authRequestPrefix, state) 68 + } 69 + 70 + // GetAuthRequestInfo retrieves pending auth request info 71 + func (s *KeyringAuthStore) GetAuthRequestInfo(ctx context.Context, state string) (*oauth.AuthRequestData, error) { 72 + data, err := keyring.Get(keyringService, authRequestKey(state)) 67 73 if err != nil { 68 74 return nil, err 69 75 } 70 76 71 - if keyring.Set(keyringService, keyringUser, string(data)) != nil { 77 + var info oauth.AuthRequestData 78 + if err := json.Unmarshal([]byte(data), &info); err != nil { 72 79 return nil, err 73 80 } 74 81 75 - return stored, nil 82 + return &info, nil 76 83 } 77 84 78 - func StoreTokens(tokens *StoredTokens) (*StoredTokens, error) { 79 - 80 - data, err := json.Marshal(tokens) 85 + // SaveAuthRequestInfo stores pending auth request info 86 + func (s *KeyringAuthStore) SaveAuthRequestInfo(ctx context.Context, info oauth.AuthRequestData) error { 87 + data, err := json.Marshal(info) 81 88 if err != nil { 82 - return nil, err 89 + return err 83 90 } 84 91 85 - if keyring.Set(keyringService, keyringUser, string(data)) != nil { 86 - return nil, err 92 + // Save the auth request data 93 + if err := keyring.Set(keyringService, authRequestKey(info.State), string(data)); err != nil { 94 + return err 87 95 } 88 96 89 - return tokens, nil 97 + // Also save the state as the current pending auth (for SSE correlation) 98 + return keyring.Set(keyringService, pendingAuthStateKey, info.State) 99 + } 100 + 101 + // GetPendingAuthState returns the state of the current pending auth request 102 + func (s *KeyringAuthStore) GetPendingAuthState() (string, error) { 103 + return keyring.Get(keyringService, pendingAuthStateKey) 90 104 } 91 105 92 - func GetStoredTokens() (*StoredTokens, error) { 93 - data, err := keyring.Get(keyringService, keyringUser) 106 + // ClearPendingAuthState removes the pending auth state 107 + func (s *KeyringAuthStore) ClearPendingAuthState() error { 108 + return keyring.Delete(keyringService, pendingAuthStateKey) 109 + } 110 + 111 + // DeleteAuthRequestInfo removes pending auth request info 112 + func (s *KeyringAuthStore) DeleteAuthRequestInfo(ctx context.Context, state string) error { 113 + return keyring.Delete(keyringService, authRequestKey(state)) 114 + } 115 + 116 + // CurrentSessionRef stores reference to the current active session 117 + type CurrentSessionRef struct { 118 + DID string `json:"did"` 119 + SessionID string `json:"session_id"` 120 + } 121 + 122 + // GetCurrentSession retrieves the current active session for the CLI 123 + func (s *KeyringAuthStore) GetCurrentSession(ctx context.Context) (*oauth.ClientSessionData, error) { 124 + refData, err := keyring.Get(keyringService, currentSessionKey) 94 125 if err != nil { 95 126 return nil, err 96 127 } 97 128 98 - var tokens StoredTokens 99 - if err := json.Unmarshal([]byte(data), &tokens); err != nil { 129 + var ref CurrentSessionRef 130 + if err := json.Unmarshal([]byte(refData), &ref); err != nil { 131 + return nil, err 132 + } 133 + 134 + did, err := syntax.ParseDID(ref.DID) 135 + if err != nil { 100 136 return nil, err 101 137 } 102 138 103 - return &tokens, nil 139 + return s.GetSession(ctx, did, ref.SessionID) 104 140 } 105 141 106 - func DeleteStoredTokens() error { 107 - return keyring.Delete(keyringService, keyringUser) 142 + // SetCurrentSession sets the current active session reference 143 + func (s *KeyringAuthStore) SetCurrentSession(ctx context.Context, sess *oauth.ClientSessionData) error { 144 + ref := CurrentSessionRef{ 145 + DID: sess.AccountDID.String(), 146 + SessionID: sess.SessionID, 147 + } 148 + 149 + data, err := json.Marshal(ref) 150 + if err != nil { 151 + return err 152 + } 153 + 154 + return keyring.Set(keyringService, currentSessionKey, string(data)) 108 155 } 109 156 110 - func DeleteStoredJWKS() error { 111 - return keyring.Delete(keyringService, keyringJWKS) 157 + // ClearCurrentSession removes the current session reference 158 + func (s *KeyringAuthStore) ClearCurrentSession() error { 159 + return keyring.Delete(keyringService, currentSessionKey) 112 160 } 113 161 114 - // DeleteAll removes both tokens and JWKS 115 - func DeleteAll() error { 116 - if err := DeleteStoredTokens(); err != nil { 117 - return err 118 - } 119 - return DeleteStoredJWKS() 162 + // HasLegacyTokens checks if old-format tokens exist (for migration) 163 + func HasLegacyTokens() bool { 164 + _, err := keyring.Get(keyringService, legacyTokensKey) 165 + return err == nil 120 166 } 121 167 122 - func (t *StoredTokens) NeedsRefresh() bool { 123 - sevenDaysFromNow := time.Now().Unix() + (7 * 24 * 60 * 60) 124 - return t.ExpiresAt <= sevenDaysFromNow 168 + // DeleteLegacyTokens removes old-format tokens and JWKS 169 + func DeleteLegacyTokens() error { 170 + // Ignore errors - keys might not exist 171 + keyring.Delete(keyringService, legacyTokensKey) 172 + keyring.Delete(keyringService, legacyJWKSKey) 173 + return nil 125 174 }
+12
internal/clipboard/clipboard.go
··· 1 + // Package clipboard provides native clipboard access for Wayland. 2 + package clipboard 3 + 4 + // CopyText copies the given text to the system clipboard. 5 + // On Wayland, this uses the wlr-data-control protocol directly, 6 + // without requiring external tools like wl-copy. 7 + // 8 + // The implementation forks a background process to serve paste 9 + // requests until another application takes clipboard ownership. 10 + func CopyText(text string) error { 11 + return copyTextPlatform(text) 12 + }
+688
internal/clipboard/clipboard_wayland.c
··· 1 + /* 2 + * Native Wayland clipboard implementation 3 + * Supports both wlr-data-control (preferred) and popup surface hack (fallback) 4 + */ 5 + 6 + #define _GNU_SOURCE 7 + #include <wayland-client.h> 8 + #include <string.h> 9 + #include <stdlib.h> 10 + #include <unistd.h> 11 + #include <fcntl.h> 12 + #include <signal.h> 13 + #include <errno.h> 14 + #include <sys/mman.h> 15 + 16 + #include "wlr-data-control.h" 17 + #include "xdg-shell.h" 18 + #include "clipboard_wayland.h" 19 + 20 + /* State structure for clipboard operations */ 21 + struct clipboard_state { 22 + /* Display connection */ 23 + struct wl_display *display; 24 + struct wl_registry *registry; 25 + 26 + /* Core globals */ 27 + struct wl_compositor *compositor; 28 + struct wl_shm *shm; 29 + struct wl_seat *seat; 30 + struct wl_data_device_manager *data_device_manager; 31 + 32 + /* Shell (for popup surface) */ 33 + struct xdg_wm_base *xdg_wm_base; 34 + 35 + /* Preferred protocol (if available) */ 36 + struct zwlr_data_control_manager_v1 *data_control_manager; 37 + 38 + /* Keyboard tracking */ 39 + struct wl_keyboard *keyboard; 40 + uint32_t keyboard_serial; 41 + int have_keyboard_focus; 42 + 43 + /* Surface (for popup hack) */ 44 + struct wl_surface *surface; 45 + struct xdg_surface *xdg_surface; 46 + struct xdg_toplevel *xdg_toplevel; 47 + struct wl_buffer *buffer; 48 + struct wl_shm_pool *shm_pool; 49 + int surface_configured; 50 + 51 + /* wlr-data-control objects */ 52 + struct zwlr_data_control_source_v1 *wlr_source; 53 + struct zwlr_data_control_device_v1 *wlr_device; 54 + 55 + /* Core wayland data objects */ 56 + struct wl_data_source *data_source; 57 + struct wl_data_device *data_device; 58 + 59 + /* Clipboard content */ 60 + char *text; 61 + size_t text_len; 62 + 63 + /* State flags */ 64 + int selection_set; 65 + int cancelled; 66 + }; 67 + 68 + /* Forward declarations */ 69 + static int copy_via_data_control(struct clipboard_state *state); 70 + static int copy_via_popup_surface(struct clipboard_state *state); 71 + static void cleanup(struct clipboard_state *state); 72 + 73 + /* 74 + * wlr-data-control source listener 75 + */ 76 + static void wlr_source_send(void *data, 77 + struct zwlr_data_control_source_v1 *source, 78 + const char *mime_type, int32_t fd) { 79 + struct clipboard_state *state = data; 80 + if (state->text && state->text_len > 0) { 81 + size_t written = 0; 82 + while (written < state->text_len) { 83 + ssize_t ret = write(fd, state->text + written, state->text_len - written); 84 + if (ret < 0) { 85 + if (errno == EINTR) continue; 86 + break; 87 + } 88 + written += ret; 89 + } 90 + } 91 + close(fd); 92 + } 93 + 94 + static void wlr_source_cancelled(void *data, 95 + struct zwlr_data_control_source_v1 *source) { 96 + struct clipboard_state *state = data; 97 + state->cancelled = 1; 98 + } 99 + 100 + static const struct zwlr_data_control_source_v1_listener wlr_source_listener = { 101 + .send = wlr_source_send, 102 + .cancelled = wlr_source_cancelled, 103 + }; 104 + 105 + /* 106 + * wlr-data-control device listener (for offer/selection events we ignore) 107 + */ 108 + static void wlr_device_data_offer(void *data, 109 + struct zwlr_data_control_device_v1 *device, 110 + struct zwlr_data_control_offer_v1 *offer) { 111 + if (offer) zwlr_data_control_offer_v1_destroy(offer); 112 + } 113 + 114 + static void wlr_device_selection(void *data, 115 + struct zwlr_data_control_device_v1 *device, 116 + struct zwlr_data_control_offer_v1 *offer) { 117 + if (offer) zwlr_data_control_offer_v1_destroy(offer); 118 + } 119 + 120 + static void wlr_device_finished(void *data, 121 + struct zwlr_data_control_device_v1 *device) { 122 + struct clipboard_state *state = data; 123 + state->cancelled = 1; 124 + } 125 + 126 + static void wlr_device_primary_selection(void *data, 127 + struct zwlr_data_control_device_v1 *device, 128 + struct zwlr_data_control_offer_v1 *offer) { 129 + if (offer) zwlr_data_control_offer_v1_destroy(offer); 130 + } 131 + 132 + static const struct zwlr_data_control_device_v1_listener wlr_device_listener = { 133 + .data_offer = wlr_device_data_offer, 134 + .selection = wlr_device_selection, 135 + .finished = wlr_device_finished, 136 + .primary_selection = wlr_device_primary_selection, 137 + }; 138 + 139 + /* 140 + * Core wl_data_source listener (for popup surface path) 141 + */ 142 + static void data_source_target(void *data, struct wl_data_source *source, 143 + const char *mime_type) { 144 + /* Ignore */ 145 + } 146 + 147 + static void data_source_send(void *data, struct wl_data_source *source, 148 + const char *mime_type, int32_t fd) { 149 + struct clipboard_state *state = data; 150 + if (state->text && state->text_len > 0) { 151 + size_t written = 0; 152 + while (written < state->text_len) { 153 + ssize_t ret = write(fd, state->text + written, state->text_len - written); 154 + if (ret < 0) { 155 + if (errno == EINTR) continue; 156 + break; 157 + } 158 + written += ret; 159 + } 160 + } 161 + close(fd); 162 + } 163 + 164 + static void data_source_cancelled(void *data, struct wl_data_source *source) { 165 + struct clipboard_state *state = data; 166 + state->cancelled = 1; 167 + } 168 + 169 + static void data_source_dnd_drop_performed(void *data, struct wl_data_source *source) {} 170 + static void data_source_dnd_finished(void *data, struct wl_data_source *source) {} 171 + static void data_source_action(void *data, struct wl_data_source *source, uint32_t action) {} 172 + 173 + static const struct wl_data_source_listener data_source_listener = { 174 + .target = data_source_target, 175 + .send = data_source_send, 176 + .cancelled = data_source_cancelled, 177 + .dnd_drop_performed = data_source_dnd_drop_performed, 178 + .dnd_finished = data_source_dnd_finished, 179 + .action = data_source_action, 180 + }; 181 + 182 + /* 183 + * Keyboard listener - captures focus serial 184 + */ 185 + static void keyboard_keymap(void *data, struct wl_keyboard *keyboard, 186 + uint32_t format, int32_t fd, uint32_t size) { 187 + close(fd); 188 + } 189 + 190 + static void keyboard_enter(void *data, struct wl_keyboard *keyboard, 191 + uint32_t serial, struct wl_surface *surface, 192 + struct wl_array *keys) { 193 + struct clipboard_state *state = data; 194 + state->keyboard_serial = serial; 195 + state->have_keyboard_focus = 1; 196 + } 197 + 198 + static void keyboard_leave(void *data, struct wl_keyboard *keyboard, 199 + uint32_t serial, struct wl_surface *surface) { 200 + /* Ignore */ 201 + } 202 + 203 + static void keyboard_key(void *data, struct wl_keyboard *keyboard, 204 + uint32_t serial, uint32_t time, uint32_t key, uint32_t state_w) { 205 + /* Ignore */ 206 + } 207 + 208 + static void keyboard_modifiers(void *data, struct wl_keyboard *keyboard, 209 + uint32_t serial, uint32_t mods_depressed, 210 + uint32_t mods_latched, uint32_t mods_locked, 211 + uint32_t group) { 212 + /* Ignore */ 213 + } 214 + 215 + static void keyboard_repeat_info(void *data, struct wl_keyboard *keyboard, 216 + int32_t rate, int32_t delay) { 217 + /* Ignore */ 218 + } 219 + 220 + static const struct wl_keyboard_listener keyboard_listener = { 221 + .keymap = keyboard_keymap, 222 + .enter = keyboard_enter, 223 + .leave = keyboard_leave, 224 + .key = keyboard_key, 225 + .modifiers = keyboard_modifiers, 226 + .repeat_info = keyboard_repeat_info, 227 + }; 228 + 229 + /* 230 + * xdg_wm_base listener - handle ping/pong 231 + */ 232 + static void xdg_wm_base_ping(void *data, struct xdg_wm_base *xdg_wm_base, 233 + uint32_t serial) { 234 + xdg_wm_base_pong(xdg_wm_base, serial); 235 + } 236 + 237 + static const struct xdg_wm_base_listener xdg_wm_base_listener = { 238 + .ping = xdg_wm_base_ping, 239 + }; 240 + 241 + /* 242 + * xdg_surface listener - handle configure 243 + */ 244 + static void xdg_surface_configure(void *data, struct xdg_surface *xdg_surface, 245 + uint32_t serial) { 246 + struct clipboard_state *state = data; 247 + xdg_surface_ack_configure(xdg_surface, serial); 248 + state->surface_configured = 1; 249 + } 250 + 251 + static const struct xdg_surface_listener xdg_surface_listener = { 252 + .configure = xdg_surface_configure, 253 + }; 254 + 255 + /* 256 + * xdg_toplevel listener - handle configure/close 257 + */ 258 + static void xdg_toplevel_configure(void *data, struct xdg_toplevel *toplevel, 259 + int32_t width, int32_t height, 260 + struct wl_array *states) { 261 + /* Ignore size hints */ 262 + } 263 + 264 + static void xdg_toplevel_close(void *data, struct xdg_toplevel *toplevel) { 265 + struct clipboard_state *state = data; 266 + state->cancelled = 1; 267 + } 268 + 269 + static void xdg_toplevel_configure_bounds(void *data, struct xdg_toplevel *toplevel, 270 + int32_t width, int32_t height) { 271 + /* Ignore */ 272 + } 273 + 274 + static void xdg_toplevel_wm_capabilities(void *data, struct xdg_toplevel *toplevel, 275 + struct wl_array *capabilities) { 276 + /* Ignore */ 277 + } 278 + 279 + static const struct xdg_toplevel_listener xdg_toplevel_listener = { 280 + .configure = xdg_toplevel_configure, 281 + .close = xdg_toplevel_close, 282 + .configure_bounds = xdg_toplevel_configure_bounds, 283 + .wm_capabilities = xdg_toplevel_wm_capabilities, 284 + }; 285 + 286 + /* 287 + * wl_seat listener - get capabilities 288 + */ 289 + static void seat_capabilities(void *data, struct wl_seat *seat, uint32_t caps) { 290 + struct clipboard_state *state = data; 291 + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !state->keyboard) { 292 + state->keyboard = wl_seat_get_keyboard(seat); 293 + wl_keyboard_add_listener(state->keyboard, &keyboard_listener, state); 294 + } 295 + } 296 + 297 + static void seat_name(void *data, struct wl_seat *seat, const char *name) { 298 + /* Ignore */ 299 + } 300 + 301 + static const struct wl_seat_listener seat_listener = { 302 + .capabilities = seat_capabilities, 303 + .name = seat_name, 304 + }; 305 + 306 + /* 307 + * Registry listener - discover globals 308 + */ 309 + static void registry_global(void *data, struct wl_registry *registry, 310 + uint32_t name, const char *interface, uint32_t version) { 311 + struct clipboard_state *state = data; 312 + 313 + if (strcmp(interface, "wl_compositor") == 0) { 314 + state->compositor = wl_registry_bind(registry, name, 315 + &wl_compositor_interface, version < 4 ? version : 4); 316 + } 317 + else if (strcmp(interface, "wl_shm") == 0) { 318 + state->shm = wl_registry_bind(registry, name, 319 + &wl_shm_interface, 1); 320 + } 321 + else if (strcmp(interface, "wl_seat") == 0) { 322 + state->seat = wl_registry_bind(registry, name, 323 + &wl_seat_interface, version < 2 ? version : 2); 324 + wl_seat_add_listener(state->seat, &seat_listener, state); 325 + } 326 + else if (strcmp(interface, "wl_data_device_manager") == 0) { 327 + state->data_device_manager = wl_registry_bind(registry, name, 328 + &wl_data_device_manager_interface, version < 3 ? version : 3); 329 + } 330 + else if (strcmp(interface, "xdg_wm_base") == 0) { 331 + state->xdg_wm_base = wl_registry_bind(registry, name, 332 + &xdg_wm_base_interface, 1); 333 + xdg_wm_base_add_listener(state->xdg_wm_base, &xdg_wm_base_listener, state); 334 + } 335 + else if (strcmp(interface, "zwlr_data_control_manager_v1") == 0) { 336 + state->data_control_manager = wl_registry_bind(registry, name, 337 + &zwlr_data_control_manager_v1_interface, version < 2 ? version : 2); 338 + } 339 + } 340 + 341 + static void registry_global_remove(void *data, struct wl_registry *registry, 342 + uint32_t name) { 343 + /* Ignore */ 344 + } 345 + 346 + static const struct wl_registry_listener registry_listener = { 347 + .global = registry_global, 348 + .global_remove = registry_global_remove, 349 + }; 350 + 351 + /* 352 + * Create 1x1 transparent buffer via wl_shm 353 + */ 354 + static int create_buffer(struct clipboard_state *state) { 355 + int width = 1, height = 1; 356 + int stride = width * 4; 357 + int size = stride * height; 358 + 359 + /* Create anonymous file */ 360 + int fd = memfd_create("clipboard-buffer", MFD_CLOEXEC); 361 + if (fd < 0) return -1; 362 + 363 + if (ftruncate(fd, size) < 0) { 364 + close(fd); 365 + return -1; 366 + } 367 + 368 + /* Create shm pool */ 369 + state->shm_pool = wl_shm_create_pool(state->shm, fd, size); 370 + close(fd); 371 + 372 + if (!state->shm_pool) return -1; 373 + 374 + /* Create buffer (zero bytes = transparent ARGB) */ 375 + state->buffer = wl_shm_pool_create_buffer(state->shm_pool, 0, 376 + width, height, stride, WL_SHM_FORMAT_ARGB8888); 377 + 378 + return state->buffer ? 0 : -1; 379 + } 380 + 381 + /* 382 + * Create popup surface to get keyboard focus 383 + */ 384 + static int create_popup_surface(struct clipboard_state *state) { 385 + /* Create wl_surface */ 386 + state->surface = wl_compositor_create_surface(state->compositor); 387 + if (!state->surface) return -1; 388 + 389 + /* Create xdg_surface */ 390 + state->xdg_surface = xdg_wm_base_get_xdg_surface(state->xdg_wm_base, state->surface); 391 + if (!state->xdg_surface) return -1; 392 + xdg_surface_add_listener(state->xdg_surface, &xdg_surface_listener, state); 393 + 394 + /* Create xdg_toplevel */ 395 + state->xdg_toplevel = xdg_surface_get_toplevel(state->xdg_surface); 396 + if (!state->xdg_toplevel) return -1; 397 + xdg_toplevel_add_listener(state->xdg_toplevel, &xdg_toplevel_listener, state); 398 + xdg_toplevel_set_title(state->xdg_toplevel, "clipboard"); 399 + 400 + /* Commit to trigger configure */ 401 + wl_surface_commit(state->surface); 402 + 403 + /* Wait for configure event */ 404 + int timeout = 100; 405 + while (!state->surface_configured && timeout-- > 0) { 406 + if (wl_display_dispatch(state->display) < 0) return -1; 407 + } 408 + if (!state->surface_configured) return -1; 409 + 410 + /* Create and attach buffer */ 411 + if (create_buffer(state) < 0) return -1; 412 + 413 + wl_surface_attach(state->surface, state->buffer, 0, 0); 414 + wl_surface_damage(state->surface, 0, 0, 1, 1); 415 + wl_surface_commit(state->surface); 416 + 417 + return 0; 418 + } 419 + 420 + /* 421 + * Destroy popup surface 422 + */ 423 + static void destroy_popup_surface(struct clipboard_state *state) { 424 + if (state->xdg_toplevel) { 425 + xdg_toplevel_destroy(state->xdg_toplevel); 426 + state->xdg_toplevel = NULL; 427 + } 428 + if (state->xdg_surface) { 429 + xdg_surface_destroy(state->xdg_surface); 430 + state->xdg_surface = NULL; 431 + } 432 + if (state->buffer) { 433 + wl_buffer_destroy(state->buffer); 434 + state->buffer = NULL; 435 + } 436 + if (state->shm_pool) { 437 + wl_shm_pool_destroy(state->shm_pool); 438 + state->shm_pool = NULL; 439 + } 440 + if (state->surface) { 441 + wl_surface_destroy(state->surface); 442 + state->surface = NULL; 443 + } 444 + } 445 + 446 + /* 447 + * Fork to background and serve clipboard 448 + */ 449 + static int fork_and_serve(struct clipboard_state *state) { 450 + pid_t pid = fork(); 451 + if (pid < 0) return -5; 452 + 453 + if (pid > 0) { 454 + /* Parent: detach from display (child owns it now) and return success */ 455 + state->display = NULL; 456 + state->text = NULL; 457 + return 0; 458 + } 459 + 460 + /* Child: detach from terminal */ 461 + int devnull = open("/dev/null", O_RDWR); 462 + if (devnull >= 0) { 463 + dup2(devnull, STDIN_FILENO); 464 + dup2(devnull, STDOUT_FILENO); 465 + dup2(devnull, STDERR_FILENO); 466 + if (devnull > STDERR_FILENO) close(devnull); 467 + } 468 + 469 + signal(SIGHUP, SIG_IGN); 470 + signal(SIGPIPE, SIG_IGN); 471 + if (chdir("/") < 0) { /* Ignore error */ } 472 + 473 + /* Event loop until cancelled */ 474 + while (!state->cancelled && wl_display_dispatch(state->display) >= 0); 475 + 476 + cleanup(state); 477 + _exit(0); 478 + } 479 + 480 + /* 481 + * Copy via wlr-data-control (simple path, no popup needed) 482 + */ 483 + static int copy_via_data_control(struct clipboard_state *state) { 484 + /* Create source */ 485 + state->wlr_source = zwlr_data_control_manager_v1_create_data_source( 486 + state->data_control_manager); 487 + if (!state->wlr_source) return -4; 488 + 489 + zwlr_data_control_source_v1_add_listener(state->wlr_source, 490 + &wlr_source_listener, state); 491 + 492 + /* Offer MIME types */ 493 + zwlr_data_control_source_v1_offer(state->wlr_source, "text/plain"); 494 + zwlr_data_control_source_v1_offer(state->wlr_source, "text/plain;charset=utf-8"); 495 + zwlr_data_control_source_v1_offer(state->wlr_source, "TEXT"); 496 + zwlr_data_control_source_v1_offer(state->wlr_source, "STRING"); 497 + zwlr_data_control_source_v1_offer(state->wlr_source, "UTF8_STRING"); 498 + 499 + /* Get device */ 500 + state->wlr_device = zwlr_data_control_manager_v1_get_data_device( 501 + state->data_control_manager, state->seat); 502 + if (!state->wlr_device) return -4; 503 + 504 + zwlr_data_control_device_v1_add_listener(state->wlr_device, 505 + &wlr_device_listener, state); 506 + 507 + /* Set selection */ 508 + zwlr_data_control_device_v1_set_selection(state->wlr_device, state->wlr_source); 509 + wl_display_roundtrip(state->display); 510 + 511 + state->selection_set = 1; 512 + 513 + /* Fork to background */ 514 + return fork_and_serve(state); 515 + } 516 + 517 + /* 518 + * Copy via popup surface hack (fallback for GNOME) 519 + */ 520 + static int copy_via_popup_surface(struct clipboard_state *state) { 521 + /* Create data source */ 522 + state->data_source = wl_data_device_manager_create_data_source( 523 + state->data_device_manager); 524 + if (!state->data_source) return -4; 525 + 526 + wl_data_source_add_listener(state->data_source, &data_source_listener, state); 527 + 528 + /* Offer MIME types */ 529 + wl_data_source_offer(state->data_source, "text/plain"); 530 + wl_data_source_offer(state->data_source, "text/plain;charset=utf-8"); 531 + wl_data_source_offer(state->data_source, "TEXT"); 532 + wl_data_source_offer(state->data_source, "STRING"); 533 + wl_data_source_offer(state->data_source, "UTF8_STRING"); 534 + 535 + /* Get data device */ 536 + state->data_device = wl_data_device_manager_get_data_device( 537 + state->data_device_manager, state->seat); 538 + if (!state->data_device) return -4; 539 + 540 + /* Create popup surface to get keyboard focus */ 541 + if (create_popup_surface(state) < 0) return -4; 542 + 543 + /* Wait for keyboard focus (with timeout) */ 544 + int timeout = 100; 545 + while (!state->have_keyboard_focus && timeout-- > 0) { 546 + if (wl_display_dispatch(state->display) < 0) { 547 + destroy_popup_surface(state); 548 + return -3; 549 + } 550 + } 551 + 552 + if (!state->have_keyboard_focus) { 553 + destroy_popup_surface(state); 554 + return -6; /* Focus timeout */ 555 + } 556 + 557 + /* Set selection with the serial from keyboard focus */ 558 + wl_data_device_set_selection(state->data_device, 559 + state->data_source, state->keyboard_serial); 560 + wl_display_roundtrip(state->display); 561 + 562 + state->selection_set = 1; 563 + 564 + /* Destroy popup surface */ 565 + destroy_popup_surface(state); 566 + 567 + /* Fork to background */ 568 + return fork_and_serve(state); 569 + } 570 + 571 + /* 572 + * Cleanup all resources 573 + */ 574 + static void cleanup(struct clipboard_state *state) { 575 + destroy_popup_surface(state); 576 + 577 + if (state->wlr_source) { 578 + zwlr_data_control_source_v1_destroy(state->wlr_source); 579 + state->wlr_source = NULL; 580 + } 581 + if (state->wlr_device) { 582 + zwlr_data_control_device_v1_destroy(state->wlr_device); 583 + state->wlr_device = NULL; 584 + } 585 + if (state->data_source) { 586 + wl_data_source_destroy(state->data_source); 587 + state->data_source = NULL; 588 + } 589 + if (state->data_device) { 590 + wl_data_device_destroy(state->data_device); 591 + state->data_device = NULL; 592 + } 593 + if (state->keyboard) { 594 + wl_keyboard_destroy(state->keyboard); 595 + state->keyboard = NULL; 596 + } 597 + if (state->data_control_manager) { 598 + zwlr_data_control_manager_v1_destroy(state->data_control_manager); 599 + state->data_control_manager = NULL; 600 + } 601 + if (state->xdg_wm_base) { 602 + xdg_wm_base_destroy(state->xdg_wm_base); 603 + state->xdg_wm_base = NULL; 604 + } 605 + if (state->data_device_manager) { 606 + wl_data_device_manager_destroy(state->data_device_manager); 607 + state->data_device_manager = NULL; 608 + } 609 + if (state->seat) { 610 + wl_seat_destroy(state->seat); 611 + state->seat = NULL; 612 + } 613 + if (state->shm) { 614 + wl_shm_destroy(state->shm); 615 + state->shm = NULL; 616 + } 617 + if (state->compositor) { 618 + wl_compositor_destroy(state->compositor); 619 + state->compositor = NULL; 620 + } 621 + if (state->registry) { 622 + wl_registry_destroy(state->registry); 623 + state->registry = NULL; 624 + } 625 + if (state->display) { 626 + wl_display_disconnect(state->display); 627 + state->display = NULL; 628 + } 629 + if (state->text) { 630 + free(state->text); 631 + state->text = NULL; 632 + } 633 + } 634 + 635 + /* 636 + * Main entry point: copy text to clipboard 637 + */ 638 + int clipboard_copy_text(const char *text, size_t len) { 639 + struct clipboard_state state = {0}; 640 + int result = 0; 641 + 642 + /* Store text to copy */ 643 + state.text = malloc(len); 644 + if (!state.text) return -4; 645 + memcpy(state.text, text, len); 646 + state.text_len = len; 647 + 648 + /* Ignore SIGPIPE */ 649 + signal(SIGPIPE, SIG_IGN); 650 + 651 + /* Connect to display */ 652 + state.display = wl_display_connect(NULL); 653 + if (!state.display) { 654 + result = -1; 655 + goto error; 656 + } 657 + 658 + /* Get registry */ 659 + state.registry = wl_display_get_registry(state.display); 660 + wl_registry_add_listener(state.registry, &registry_listener, &state); 661 + 662 + /* Roundtrip to get globals */ 663 + wl_display_roundtrip(state.display); 664 + 665 + /* Second roundtrip to ensure seat capabilities are processed */ 666 + wl_display_roundtrip(state.display); 667 + 668 + /* Choose path based on available protocols */ 669 + if (state.data_control_manager && state.seat) { 670 + /* Preferred: wlr-data-control (no popup needed) */ 671 + result = copy_via_data_control(&state); 672 + } else if (state.data_device_manager && state.compositor && 673 + state.shm && state.seat && state.xdg_wm_base) { 674 + /* Fallback: popup surface hack */ 675 + result = copy_via_popup_surface(&state); 676 + } else { 677 + result = -2; /* Missing required protocols */ 678 + } 679 + 680 + if (result == 0) { 681 + /* Success - parent process, cleanup done by fork */ 682 + return 0; 683 + } 684 + 685 + error: 686 + cleanup(&state); 687 + return result; 688 + }
+54
internal/clipboard/clipboard_wayland.go
··· 1 + //go:build linux 2 + 3 + package clipboard 4 + 5 + /* 6 + #cgo pkg-config: wayland-client 7 + #cgo CFLAGS: -I${SRCDIR} 8 + #include <stdlib.h> 9 + #include "clipboard_wayland.h" 10 + */ 11 + import "C" 12 + import ( 13 + "errors" 14 + "unsafe" 15 + ) 16 + 17 + var ( 18 + ErrNoDisplay = errors.New("cannot connect to Wayland display") 19 + ErrMissingProtocols = errors.New("missing required Wayland protocols") 20 + ErrDisplayError = errors.New("Wayland display error") 21 + ErrAllocation = errors.New("resource allocation failed") 22 + ErrFork = errors.New("fork failed") 23 + ErrFocusTimeout = errors.New("keyboard focus timeout") 24 + ) 25 + 26 + func copyTextPlatform(text string) error { 27 + if len(text) == 0 { 28 + return nil 29 + } 30 + 31 + cText := C.CString(text) 32 + defer C.free(unsafe.Pointer(cText)) 33 + 34 + result := C.clipboard_copy_text(cText, C.size_t(len(text))) 35 + 36 + switch result { 37 + case 0: 38 + return nil 39 + case -1: 40 + return ErrNoDisplay 41 + case -2: 42 + return ErrMissingProtocols 43 + case -3: 44 + return ErrDisplayError 45 + case -4: 46 + return ErrAllocation 47 + case -5: 48 + return ErrFork 49 + case -6: 50 + return ErrFocusTimeout 51 + default: 52 + return errors.New("unknown clipboard error") 53 + } 54 + }
+26
internal/clipboard/clipboard_wayland.h
··· 1 + #ifndef CLIPBOARD_WAYLAND_H 2 + #define CLIPBOARD_WAYLAND_H 3 + 4 + #include <stddef.h> 5 + 6 + /* 7 + * Copy text to the Wayland clipboard. 8 + * Uses wlr-data-control if available (Sway, KDE), otherwise falls back to 9 + * popup surface hack with wl_data_device (GNOME). 10 + * 11 + * Parameters: 12 + * text - The text to copy 13 + * len - Length of text in bytes 14 + * 15 + * Returns: 16 + * 0 on success 17 + * -1: Cannot connect to Wayland display 18 + * -2: Missing required Wayland protocols 19 + * -3: Display error during operation 20 + * -4: Resource allocation failed 21 + * -5: Fork failed 22 + * -6: Keyboard focus timeout (popup surface hack) 23 + */ 24 + int clipboard_copy_text(const char *text, size_t len); 25 + 26 + #endif /* CLIPBOARD_WAYLAND_H */
+10
internal/clipboard/generate.go
··· 1 + //go:build linux 2 + 3 + package clipboard 4 + 5 + //go:generate sh -c "wayland-scanner client-header $(pkg-config --variable=pkgdatadir wayland-protocols)/stable/xdg-shell/xdg-shell.xml xdg-shell.h" 6 + //go:generate sh -c "wayland-scanner private-code $(pkg-config --variable=pkgdatadir wayland-protocols)/stable/xdg-shell/xdg-shell.xml xdg-shell.c" 7 + //go:generate curl -sfo wlr-data-control-unstable-v1.xml https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/raw/master/unstable/wlr-data-control-unstable-v1.xml 8 + //go:generate wayland-scanner client-header wlr-data-control-unstable-v1.xml wlr-data-control.h 9 + //go:generate wayland-scanner private-code wlr-data-control-unstable-v1.xml wlr-data-control.c 10 + //go:generate rm wlr-data-control-unstable-v1.xml
+123
internal/clipboard/wlr-data-control.c
··· 1 + /* Generated by wayland-scanner 1.24.0 */ 2 + 3 + /* 4 + * Copyright © 2018 Simon Ser 5 + * Copyright © 2019 Ivan Molodetskikh 6 + * 7 + * Permission to use, copy, modify, distribute, and sell this 8 + * software and its documentation for any purpose is hereby granted 9 + * without fee, provided that the above copyright notice appear in 10 + * all copies and that both that copyright notice and this permission 11 + * notice appear in supporting documentation, and that the name of 12 + * the copyright holders not be used in advertising or publicity 13 + * pertaining to distribution of the software without specific, 14 + * written prior permission. The copyright holders make no 15 + * representations about the suitability of this software for any 16 + * purpose. It is provided "as is" without express or implied 17 + * warranty. 18 + * 19 + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 20 + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 21 + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 23 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 24 + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 25 + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 26 + * THIS SOFTWARE. 27 + */ 28 + 29 + #include <stdbool.h> 30 + #include <stdlib.h> 31 + #include <stdint.h> 32 + #include "wayland-util.h" 33 + 34 + #ifndef __has_attribute 35 + # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 36 + #endif 37 + 38 + #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 39 + #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 40 + #else 41 + #define WL_PRIVATE 42 + #endif 43 + 44 + extern const struct wl_interface wl_seat_interface; 45 + extern const struct wl_interface zwlr_data_control_device_v1_interface; 46 + extern const struct wl_interface zwlr_data_control_offer_v1_interface; 47 + extern const struct wl_interface zwlr_data_control_source_v1_interface; 48 + 49 + static const struct wl_interface *wlr_data_control_unstable_v1_types[] = { 50 + NULL, 51 + NULL, 52 + &zwlr_data_control_source_v1_interface, 53 + &zwlr_data_control_device_v1_interface, 54 + &wl_seat_interface, 55 + &zwlr_data_control_source_v1_interface, 56 + &zwlr_data_control_source_v1_interface, 57 + &zwlr_data_control_offer_v1_interface, 58 + &zwlr_data_control_offer_v1_interface, 59 + &zwlr_data_control_offer_v1_interface, 60 + }; 61 + 62 + static const struct wl_message zwlr_data_control_manager_v1_requests[] = { 63 + { "create_data_source", "n", wlr_data_control_unstable_v1_types + 2 }, 64 + { "get_data_device", "no", wlr_data_control_unstable_v1_types + 3 }, 65 + { "destroy", "", wlr_data_control_unstable_v1_types + 0 }, 66 + }; 67 + 68 + WL_PRIVATE const struct wl_interface zwlr_data_control_manager_v1_interface = { 69 + "zwlr_data_control_manager_v1", 2, 70 + 3, zwlr_data_control_manager_v1_requests, 71 + 0, NULL, 72 + }; 73 + 74 + static const struct wl_message zwlr_data_control_device_v1_requests[] = { 75 + { "set_selection", "?o", wlr_data_control_unstable_v1_types + 5 }, 76 + { "destroy", "", wlr_data_control_unstable_v1_types + 0 }, 77 + { "set_primary_selection", "2?o", wlr_data_control_unstable_v1_types + 6 }, 78 + }; 79 + 80 + static const struct wl_message zwlr_data_control_device_v1_events[] = { 81 + { "data_offer", "n", wlr_data_control_unstable_v1_types + 7 }, 82 + { "selection", "?o", wlr_data_control_unstable_v1_types + 8 }, 83 + { "finished", "", wlr_data_control_unstable_v1_types + 0 }, 84 + { "primary_selection", "2?o", wlr_data_control_unstable_v1_types + 9 }, 85 + }; 86 + 87 + WL_PRIVATE const struct wl_interface zwlr_data_control_device_v1_interface = { 88 + "zwlr_data_control_device_v1", 2, 89 + 3, zwlr_data_control_device_v1_requests, 90 + 4, zwlr_data_control_device_v1_events, 91 + }; 92 + 93 + static const struct wl_message zwlr_data_control_source_v1_requests[] = { 94 + { "offer", "s", wlr_data_control_unstable_v1_types + 0 }, 95 + { "destroy", "", wlr_data_control_unstable_v1_types + 0 }, 96 + }; 97 + 98 + static const struct wl_message zwlr_data_control_source_v1_events[] = { 99 + { "send", "sh", wlr_data_control_unstable_v1_types + 0 }, 100 + { "cancelled", "", wlr_data_control_unstable_v1_types + 0 }, 101 + }; 102 + 103 + WL_PRIVATE const struct wl_interface zwlr_data_control_source_v1_interface = { 104 + "zwlr_data_control_source_v1", 1, 105 + 2, zwlr_data_control_source_v1_requests, 106 + 2, zwlr_data_control_source_v1_events, 107 + }; 108 + 109 + static const struct wl_message zwlr_data_control_offer_v1_requests[] = { 110 + { "receive", "sh", wlr_data_control_unstable_v1_types + 0 }, 111 + { "destroy", "", wlr_data_control_unstable_v1_types + 0 }, 112 + }; 113 + 114 + static const struct wl_message zwlr_data_control_offer_v1_events[] = { 115 + { "offer", "s", wlr_data_control_unstable_v1_types + 0 }, 116 + }; 117 + 118 + WL_PRIVATE const struct wl_interface zwlr_data_control_offer_v1_interface = { 119 + "zwlr_data_control_offer_v1", 1, 120 + 2, zwlr_data_control_offer_v1_requests, 121 + 1, zwlr_data_control_offer_v1_events, 122 + }; 123 +
+668
internal/clipboard/wlr-data-control.h
··· 1 + /* Generated by wayland-scanner 1.24.0 */ 2 + 3 + #ifndef WLR_DATA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H 4 + #define WLR_DATA_CONTROL_UNSTABLE_V1_CLIENT_PROTOCOL_H 5 + 6 + #include <stdint.h> 7 + #include <stddef.h> 8 + #include "wayland-client.h" 9 + 10 + #ifdef __cplusplus 11 + extern "C" { 12 + #endif 13 + 14 + /** 15 + * @page page_wlr_data_control_unstable_v1 The wlr_data_control_unstable_v1 protocol 16 + * control data devices 17 + * 18 + * @section page_desc_wlr_data_control_unstable_v1 Description 19 + * 20 + * This protocol allows a privileged client to control data devices. In 21 + * particular, the client will be able to manage the current selection and take 22 + * the role of a clipboard manager. 23 + * 24 + * Warning! The protocol described in this file is experimental and 25 + * backward incompatible changes may be made. Backward compatible changes 26 + * may be added together with the corresponding interface version bump. 27 + * Backward incompatible changes are done by bumping the version number in 28 + * the protocol and interface names and resetting the interface version. 29 + * Once the protocol is to be declared stable, the 'z' prefix and the 30 + * version number in the protocol and interface names are removed and the 31 + * interface version number is reset. 32 + * 33 + * Note! This protocol is deprecated and not intended for production use. 34 + * For clipboard management, use the ext-data-control-v1 protocol. 35 + * 36 + * @section page_ifaces_wlr_data_control_unstable_v1 Interfaces 37 + * - @subpage page_iface_zwlr_data_control_manager_v1 - manager to control data devices 38 + * - @subpage page_iface_zwlr_data_control_device_v1 - manage a data device for a seat 39 + * - @subpage page_iface_zwlr_data_control_source_v1 - offer to transfer data 40 + * - @subpage page_iface_zwlr_data_control_offer_v1 - offer to transfer data 41 + * @section page_copyright_wlr_data_control_unstable_v1 Copyright 42 + * <pre> 43 + * 44 + * Copyright © 2018 Simon Ser 45 + * Copyright © 2019 Ivan Molodetskikh 46 + * 47 + * Permission to use, copy, modify, distribute, and sell this 48 + * software and its documentation for any purpose is hereby granted 49 + * without fee, provided that the above copyright notice appear in 50 + * all copies and that both that copyright notice and this permission 51 + * notice appear in supporting documentation, and that the name of 52 + * the copyright holders not be used in advertising or publicity 53 + * pertaining to distribution of the software without specific, 54 + * written prior permission. The copyright holders make no 55 + * representations about the suitability of this software for any 56 + * purpose. It is provided "as is" without express or implied 57 + * warranty. 58 + * 59 + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS 60 + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND 61 + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY 62 + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 63 + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 64 + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 65 + * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF 66 + * THIS SOFTWARE. 67 + * </pre> 68 + */ 69 + struct wl_seat; 70 + struct zwlr_data_control_device_v1; 71 + struct zwlr_data_control_manager_v1; 72 + struct zwlr_data_control_offer_v1; 73 + struct zwlr_data_control_source_v1; 74 + 75 + #ifndef ZWLR_DATA_CONTROL_MANAGER_V1_INTERFACE 76 + #define ZWLR_DATA_CONTROL_MANAGER_V1_INTERFACE 77 + /** 78 + * @page page_iface_zwlr_data_control_manager_v1 zwlr_data_control_manager_v1 79 + * @section page_iface_zwlr_data_control_manager_v1_desc Description 80 + * 81 + * This interface is a manager that allows creating per-seat data device 82 + * controls. 83 + * @section page_iface_zwlr_data_control_manager_v1_api API 84 + * See @ref iface_zwlr_data_control_manager_v1. 85 + */ 86 + /** 87 + * @defgroup iface_zwlr_data_control_manager_v1 The zwlr_data_control_manager_v1 interface 88 + * 89 + * This interface is a manager that allows creating per-seat data device 90 + * controls. 91 + */ 92 + extern const struct wl_interface zwlr_data_control_manager_v1_interface; 93 + #endif 94 + #ifndef ZWLR_DATA_CONTROL_DEVICE_V1_INTERFACE 95 + #define ZWLR_DATA_CONTROL_DEVICE_V1_INTERFACE 96 + /** 97 + * @page page_iface_zwlr_data_control_device_v1 zwlr_data_control_device_v1 98 + * @section page_iface_zwlr_data_control_device_v1_desc Description 99 + * 100 + * This interface allows a client to manage a seat's selection. 101 + * 102 + * When the seat is destroyed, this object becomes inert. 103 + * @section page_iface_zwlr_data_control_device_v1_api API 104 + * See @ref iface_zwlr_data_control_device_v1. 105 + */ 106 + /** 107 + * @defgroup iface_zwlr_data_control_device_v1 The zwlr_data_control_device_v1 interface 108 + * 109 + * This interface allows a client to manage a seat's selection. 110 + * 111 + * When the seat is destroyed, this object becomes inert. 112 + */ 113 + extern const struct wl_interface zwlr_data_control_device_v1_interface; 114 + #endif 115 + #ifndef ZWLR_DATA_CONTROL_SOURCE_V1_INTERFACE 116 + #define ZWLR_DATA_CONTROL_SOURCE_V1_INTERFACE 117 + /** 118 + * @page page_iface_zwlr_data_control_source_v1 zwlr_data_control_source_v1 119 + * @section page_iface_zwlr_data_control_source_v1_desc Description 120 + * 121 + * The wlr_data_control_source object is the source side of a 122 + * wlr_data_control_offer. It is created by the source client in a data 123 + * transfer and provides a way to describe the offered data and a way to 124 + * respond to requests to transfer the data. 125 + * @section page_iface_zwlr_data_control_source_v1_api API 126 + * See @ref iface_zwlr_data_control_source_v1. 127 + */ 128 + /** 129 + * @defgroup iface_zwlr_data_control_source_v1 The zwlr_data_control_source_v1 interface 130 + * 131 + * The wlr_data_control_source object is the source side of a 132 + * wlr_data_control_offer. It is created by the source client in a data 133 + * transfer and provides a way to describe the offered data and a way to 134 + * respond to requests to transfer the data. 135 + */ 136 + extern const struct wl_interface zwlr_data_control_source_v1_interface; 137 + #endif 138 + #ifndef ZWLR_DATA_CONTROL_OFFER_V1_INTERFACE 139 + #define ZWLR_DATA_CONTROL_OFFER_V1_INTERFACE 140 + /** 141 + * @page page_iface_zwlr_data_control_offer_v1 zwlr_data_control_offer_v1 142 + * @section page_iface_zwlr_data_control_offer_v1_desc Description 143 + * 144 + * A wlr_data_control_offer represents a piece of data offered for transfer 145 + * by another client (the source client). The offer describes the different 146 + * MIME types that the data can be converted to and provides the mechanism 147 + * for transferring the data directly from the source client. 148 + * @section page_iface_zwlr_data_control_offer_v1_api API 149 + * See @ref iface_zwlr_data_control_offer_v1. 150 + */ 151 + /** 152 + * @defgroup iface_zwlr_data_control_offer_v1 The zwlr_data_control_offer_v1 interface 153 + * 154 + * A wlr_data_control_offer represents a piece of data offered for transfer 155 + * by another client (the source client). The offer describes the different 156 + * MIME types that the data can be converted to and provides the mechanism 157 + * for transferring the data directly from the source client. 158 + */ 159 + extern const struct wl_interface zwlr_data_control_offer_v1_interface; 160 + #endif 161 + 162 + #define ZWLR_DATA_CONTROL_MANAGER_V1_CREATE_DATA_SOURCE 0 163 + #define ZWLR_DATA_CONTROL_MANAGER_V1_GET_DATA_DEVICE 1 164 + #define ZWLR_DATA_CONTROL_MANAGER_V1_DESTROY 2 165 + 166 + 167 + /** 168 + * @ingroup iface_zwlr_data_control_manager_v1 169 + */ 170 + #define ZWLR_DATA_CONTROL_MANAGER_V1_CREATE_DATA_SOURCE_SINCE_VERSION 1 171 + /** 172 + * @ingroup iface_zwlr_data_control_manager_v1 173 + */ 174 + #define ZWLR_DATA_CONTROL_MANAGER_V1_GET_DATA_DEVICE_SINCE_VERSION 1 175 + /** 176 + * @ingroup iface_zwlr_data_control_manager_v1 177 + */ 178 + #define ZWLR_DATA_CONTROL_MANAGER_V1_DESTROY_SINCE_VERSION 1 179 + 180 + /** @ingroup iface_zwlr_data_control_manager_v1 */ 181 + static inline void 182 + zwlr_data_control_manager_v1_set_user_data(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1, void *user_data) 183 + { 184 + wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_manager_v1, user_data); 185 + } 186 + 187 + /** @ingroup iface_zwlr_data_control_manager_v1 */ 188 + static inline void * 189 + zwlr_data_control_manager_v1_get_user_data(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1) 190 + { 191 + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_manager_v1); 192 + } 193 + 194 + static inline uint32_t 195 + zwlr_data_control_manager_v1_get_version(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1) 196 + { 197 + return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1); 198 + } 199 + 200 + /** 201 + * @ingroup iface_zwlr_data_control_manager_v1 202 + * 203 + * Create a new data source. 204 + */ 205 + static inline struct zwlr_data_control_source_v1 * 206 + zwlr_data_control_manager_v1_create_data_source(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1) 207 + { 208 + struct wl_proxy *id; 209 + 210 + id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_manager_v1, 211 + ZWLR_DATA_CONTROL_MANAGER_V1_CREATE_DATA_SOURCE, &zwlr_data_control_source_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1), 0, NULL); 212 + 213 + return (struct zwlr_data_control_source_v1 *) id; 214 + } 215 + 216 + /** 217 + * @ingroup iface_zwlr_data_control_manager_v1 218 + * 219 + * Create a data device that can be used to manage a seat's selection. 220 + */ 221 + static inline struct zwlr_data_control_device_v1 * 222 + zwlr_data_control_manager_v1_get_data_device(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1, struct wl_seat *seat) 223 + { 224 + struct wl_proxy *id; 225 + 226 + id = wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_manager_v1, 227 + ZWLR_DATA_CONTROL_MANAGER_V1_GET_DATA_DEVICE, &zwlr_data_control_device_v1_interface, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1), 0, NULL, seat); 228 + 229 + return (struct zwlr_data_control_device_v1 *) id; 230 + } 231 + 232 + /** 233 + * @ingroup iface_zwlr_data_control_manager_v1 234 + * 235 + * All objects created by the manager will still remain valid, until their 236 + * appropriate destroy request has been called. 237 + */ 238 + static inline void 239 + zwlr_data_control_manager_v1_destroy(struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1) 240 + { 241 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_manager_v1, 242 + ZWLR_DATA_CONTROL_MANAGER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_manager_v1), WL_MARSHAL_FLAG_DESTROY); 243 + } 244 + 245 + #ifndef ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_ENUM 246 + #define ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_ENUM 247 + enum zwlr_data_control_device_v1_error { 248 + /** 249 + * source given to set_selection or set_primary_selection was already used before 250 + */ 251 + ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_USED_SOURCE = 1, 252 + }; 253 + #endif /* ZWLR_DATA_CONTROL_DEVICE_V1_ERROR_ENUM */ 254 + 255 + /** 256 + * @ingroup iface_zwlr_data_control_device_v1 257 + * @struct zwlr_data_control_device_v1_listener 258 + */ 259 + struct zwlr_data_control_device_v1_listener { 260 + /** 261 + * introduce a new wlr_data_control_offer 262 + * 263 + * The data_offer event introduces a new wlr_data_control_offer 264 + * object, which will subsequently be used in either the 265 + * wlr_data_control_device.selection event (for the regular 266 + * clipboard selections) or the 267 + * wlr_data_control_device.primary_selection event (for the primary 268 + * clipboard selections). Immediately following the 269 + * wlr_data_control_device.data_offer event, the new data_offer 270 + * object will send out wlr_data_control_offer.offer events to 271 + * describe the MIME types it offers. 272 + */ 273 + void (*data_offer)(void *data, 274 + struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, 275 + struct zwlr_data_control_offer_v1 *id); 276 + /** 277 + * advertise new selection 278 + * 279 + * The selection event is sent out to notify the client of a new 280 + * wlr_data_control_offer for the selection for this device. The 281 + * wlr_data_control_device.data_offer and the 282 + * wlr_data_control_offer.offer events are sent out immediately 283 + * before this event to introduce the data offer object. The 284 + * selection event is sent to a client when a new selection is set. 285 + * The wlr_data_control_offer is valid until a new 286 + * wlr_data_control_offer or NULL is received. The client must 287 + * destroy the previous selection wlr_data_control_offer, if any, 288 + * upon receiving this event. 289 + * 290 + * The first selection event is sent upon binding the 291 + * wlr_data_control_device object. 292 + */ 293 + void (*selection)(void *data, 294 + struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, 295 + struct zwlr_data_control_offer_v1 *id); 296 + /** 297 + * this data control is no longer valid 298 + * 299 + * This data control object is no longer valid and should be 300 + * destroyed by the client. 301 + */ 302 + void (*finished)(void *data, 303 + struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1); 304 + /** 305 + * advertise new primary selection 306 + * 307 + * The primary_selection event is sent out to notify the client 308 + * of a new wlr_data_control_offer for the primary selection for 309 + * this device. The wlr_data_control_device.data_offer and the 310 + * wlr_data_control_offer.offer events are sent out immediately 311 + * before this event to introduce the data offer object. The 312 + * primary_selection event is sent to a client when a new primary 313 + * selection is set. The wlr_data_control_offer is valid until a 314 + * new wlr_data_control_offer or NULL is received. The client must 315 + * destroy the previous primary selection wlr_data_control_offer, 316 + * if any, upon receiving this event. 317 + * 318 + * If the compositor supports primary selection, the first 319 + * primary_selection event is sent upon binding the 320 + * wlr_data_control_device object. 321 + * @since 2 322 + */ 323 + void (*primary_selection)(void *data, 324 + struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, 325 + struct zwlr_data_control_offer_v1 *id); 326 + }; 327 + 328 + /** 329 + * @ingroup iface_zwlr_data_control_device_v1 330 + */ 331 + static inline int 332 + zwlr_data_control_device_v1_add_listener(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, 333 + const struct zwlr_data_control_device_v1_listener *listener, void *data) 334 + { 335 + return wl_proxy_add_listener((struct wl_proxy *) zwlr_data_control_device_v1, 336 + (void (**)(void)) listener, data); 337 + } 338 + 339 + #define ZWLR_DATA_CONTROL_DEVICE_V1_SET_SELECTION 0 340 + #define ZWLR_DATA_CONTROL_DEVICE_V1_DESTROY 1 341 + #define ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION 2 342 + 343 + /** 344 + * @ingroup iface_zwlr_data_control_device_v1 345 + */ 346 + #define ZWLR_DATA_CONTROL_DEVICE_V1_DATA_OFFER_SINCE_VERSION 1 347 + /** 348 + * @ingroup iface_zwlr_data_control_device_v1 349 + */ 350 + #define ZWLR_DATA_CONTROL_DEVICE_V1_SELECTION_SINCE_VERSION 1 351 + /** 352 + * @ingroup iface_zwlr_data_control_device_v1 353 + */ 354 + #define ZWLR_DATA_CONTROL_DEVICE_V1_FINISHED_SINCE_VERSION 1 355 + /** 356 + * @ingroup iface_zwlr_data_control_device_v1 357 + */ 358 + #define ZWLR_DATA_CONTROL_DEVICE_V1_PRIMARY_SELECTION_SINCE_VERSION 2 359 + 360 + /** 361 + * @ingroup iface_zwlr_data_control_device_v1 362 + */ 363 + #define ZWLR_DATA_CONTROL_DEVICE_V1_SET_SELECTION_SINCE_VERSION 1 364 + /** 365 + * @ingroup iface_zwlr_data_control_device_v1 366 + */ 367 + #define ZWLR_DATA_CONTROL_DEVICE_V1_DESTROY_SINCE_VERSION 1 368 + /** 369 + * @ingroup iface_zwlr_data_control_device_v1 370 + */ 371 + #define ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION_SINCE_VERSION 2 372 + 373 + /** @ingroup iface_zwlr_data_control_device_v1 */ 374 + static inline void 375 + zwlr_data_control_device_v1_set_user_data(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, void *user_data) 376 + { 377 + wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_device_v1, user_data); 378 + } 379 + 380 + /** @ingroup iface_zwlr_data_control_device_v1 */ 381 + static inline void * 382 + zwlr_data_control_device_v1_get_user_data(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1) 383 + { 384 + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_device_v1); 385 + } 386 + 387 + static inline uint32_t 388 + zwlr_data_control_device_v1_get_version(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1) 389 + { 390 + return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1); 391 + } 392 + 393 + /** 394 + * @ingroup iface_zwlr_data_control_device_v1 395 + * 396 + * This request asks the compositor to set the selection to the data from 397 + * the source on behalf of the client. 398 + * 399 + * The given source may not be used in any further set_selection or 400 + * set_primary_selection requests. Attempting to use a previously used 401 + * source is a protocol error. 402 + * 403 + * To unset the selection, set the source to NULL. 404 + */ 405 + static inline void 406 + zwlr_data_control_device_v1_set_selection(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, struct zwlr_data_control_source_v1 *source) 407 + { 408 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_device_v1, 409 + ZWLR_DATA_CONTROL_DEVICE_V1_SET_SELECTION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1), 0, source); 410 + } 411 + 412 + /** 413 + * @ingroup iface_zwlr_data_control_device_v1 414 + * 415 + * Destroys the data device object. 416 + */ 417 + static inline void 418 + zwlr_data_control_device_v1_destroy(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1) 419 + { 420 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_device_v1, 421 + ZWLR_DATA_CONTROL_DEVICE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1), WL_MARSHAL_FLAG_DESTROY); 422 + } 423 + 424 + /** 425 + * @ingroup iface_zwlr_data_control_device_v1 426 + * 427 + * This request asks the compositor to set the primary selection to the 428 + * data from the source on behalf of the client. 429 + * 430 + * The given source may not be used in any further set_selection or 431 + * set_primary_selection requests. Attempting to use a previously used 432 + * source is a protocol error. 433 + * 434 + * To unset the primary selection, set the source to NULL. 435 + * 436 + * The compositor will ignore this request if it does not support primary 437 + * selection. 438 + */ 439 + static inline void 440 + zwlr_data_control_device_v1_set_primary_selection(struct zwlr_data_control_device_v1 *zwlr_data_control_device_v1, struct zwlr_data_control_source_v1 *source) 441 + { 442 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_device_v1, 443 + ZWLR_DATA_CONTROL_DEVICE_V1_SET_PRIMARY_SELECTION, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_device_v1), 0, source); 444 + } 445 + 446 + #ifndef ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_ENUM 447 + #define ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_ENUM 448 + enum zwlr_data_control_source_v1_error { 449 + /** 450 + * offer sent after wlr_data_control_device.set_selection 451 + */ 452 + ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_INVALID_OFFER = 1, 453 + }; 454 + #endif /* ZWLR_DATA_CONTROL_SOURCE_V1_ERROR_ENUM */ 455 + 456 + /** 457 + * @ingroup iface_zwlr_data_control_source_v1 458 + * @struct zwlr_data_control_source_v1_listener 459 + */ 460 + struct zwlr_data_control_source_v1_listener { 461 + /** 462 + * send the data 463 + * 464 + * Request for data from the client. Send the data as the 465 + * specified MIME type over the passed file descriptor, then close 466 + * it. 467 + * @param mime_type MIME type for the data 468 + * @param fd file descriptor for the data 469 + */ 470 + void (*send)(void *data, 471 + struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1, 472 + const char *mime_type, 473 + int32_t fd); 474 + /** 475 + * selection was cancelled 476 + * 477 + * This data source is no longer valid. The data source has been 478 + * replaced by another data source. 479 + * 480 + * The client should clean up and destroy this data source. 481 + */ 482 + void (*cancelled)(void *data, 483 + struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1); 484 + }; 485 + 486 + /** 487 + * @ingroup iface_zwlr_data_control_source_v1 488 + */ 489 + static inline int 490 + zwlr_data_control_source_v1_add_listener(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1, 491 + const struct zwlr_data_control_source_v1_listener *listener, void *data) 492 + { 493 + return wl_proxy_add_listener((struct wl_proxy *) zwlr_data_control_source_v1, 494 + (void (**)(void)) listener, data); 495 + } 496 + 497 + #define ZWLR_DATA_CONTROL_SOURCE_V1_OFFER 0 498 + #define ZWLR_DATA_CONTROL_SOURCE_V1_DESTROY 1 499 + 500 + /** 501 + * @ingroup iface_zwlr_data_control_source_v1 502 + */ 503 + #define ZWLR_DATA_CONTROL_SOURCE_V1_SEND_SINCE_VERSION 1 504 + /** 505 + * @ingroup iface_zwlr_data_control_source_v1 506 + */ 507 + #define ZWLR_DATA_CONTROL_SOURCE_V1_CANCELLED_SINCE_VERSION 1 508 + 509 + /** 510 + * @ingroup iface_zwlr_data_control_source_v1 511 + */ 512 + #define ZWLR_DATA_CONTROL_SOURCE_V1_OFFER_SINCE_VERSION 1 513 + /** 514 + * @ingroup iface_zwlr_data_control_source_v1 515 + */ 516 + #define ZWLR_DATA_CONTROL_SOURCE_V1_DESTROY_SINCE_VERSION 1 517 + 518 + /** @ingroup iface_zwlr_data_control_source_v1 */ 519 + static inline void 520 + zwlr_data_control_source_v1_set_user_data(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1, void *user_data) 521 + { 522 + wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_source_v1, user_data); 523 + } 524 + 525 + /** @ingroup iface_zwlr_data_control_source_v1 */ 526 + static inline void * 527 + zwlr_data_control_source_v1_get_user_data(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1) 528 + { 529 + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_source_v1); 530 + } 531 + 532 + static inline uint32_t 533 + zwlr_data_control_source_v1_get_version(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1) 534 + { 535 + return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_source_v1); 536 + } 537 + 538 + /** 539 + * @ingroup iface_zwlr_data_control_source_v1 540 + * 541 + * This request adds a MIME type to the set of MIME types advertised to 542 + * targets. Can be called several times to offer multiple types. 543 + * 544 + * Calling this after wlr_data_control_device.set_selection is a protocol 545 + * error. 546 + */ 547 + static inline void 548 + zwlr_data_control_source_v1_offer(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1, const char *mime_type) 549 + { 550 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_source_v1, 551 + ZWLR_DATA_CONTROL_SOURCE_V1_OFFER, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_source_v1), 0, mime_type); 552 + } 553 + 554 + /** 555 + * @ingroup iface_zwlr_data_control_source_v1 556 + * 557 + * Destroys the data source object. 558 + */ 559 + static inline void 560 + zwlr_data_control_source_v1_destroy(struct zwlr_data_control_source_v1 *zwlr_data_control_source_v1) 561 + { 562 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_source_v1, 563 + ZWLR_DATA_CONTROL_SOURCE_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_source_v1), WL_MARSHAL_FLAG_DESTROY); 564 + } 565 + 566 + /** 567 + * @ingroup iface_zwlr_data_control_offer_v1 568 + * @struct zwlr_data_control_offer_v1_listener 569 + */ 570 + struct zwlr_data_control_offer_v1_listener { 571 + /** 572 + * advertise offered MIME type 573 + * 574 + * Sent immediately after creating the wlr_data_control_offer 575 + * object. One event per offered MIME type. 576 + * @param mime_type offered MIME type 577 + */ 578 + void (*offer)(void *data, 579 + struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1, 580 + const char *mime_type); 581 + }; 582 + 583 + /** 584 + * @ingroup iface_zwlr_data_control_offer_v1 585 + */ 586 + static inline int 587 + zwlr_data_control_offer_v1_add_listener(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1, 588 + const struct zwlr_data_control_offer_v1_listener *listener, void *data) 589 + { 590 + return wl_proxy_add_listener((struct wl_proxy *) zwlr_data_control_offer_v1, 591 + (void (**)(void)) listener, data); 592 + } 593 + 594 + #define ZWLR_DATA_CONTROL_OFFER_V1_RECEIVE 0 595 + #define ZWLR_DATA_CONTROL_OFFER_V1_DESTROY 1 596 + 597 + /** 598 + * @ingroup iface_zwlr_data_control_offer_v1 599 + */ 600 + #define ZWLR_DATA_CONTROL_OFFER_V1_OFFER_SINCE_VERSION 1 601 + 602 + /** 603 + * @ingroup iface_zwlr_data_control_offer_v1 604 + */ 605 + #define ZWLR_DATA_CONTROL_OFFER_V1_RECEIVE_SINCE_VERSION 1 606 + /** 607 + * @ingroup iface_zwlr_data_control_offer_v1 608 + */ 609 + #define ZWLR_DATA_CONTROL_OFFER_V1_DESTROY_SINCE_VERSION 1 610 + 611 + /** @ingroup iface_zwlr_data_control_offer_v1 */ 612 + static inline void 613 + zwlr_data_control_offer_v1_set_user_data(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1, void *user_data) 614 + { 615 + wl_proxy_set_user_data((struct wl_proxy *) zwlr_data_control_offer_v1, user_data); 616 + } 617 + 618 + /** @ingroup iface_zwlr_data_control_offer_v1 */ 619 + static inline void * 620 + zwlr_data_control_offer_v1_get_user_data(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1) 621 + { 622 + return wl_proxy_get_user_data((struct wl_proxy *) zwlr_data_control_offer_v1); 623 + } 624 + 625 + static inline uint32_t 626 + zwlr_data_control_offer_v1_get_version(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1) 627 + { 628 + return wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_offer_v1); 629 + } 630 + 631 + /** 632 + * @ingroup iface_zwlr_data_control_offer_v1 633 + * 634 + * To transfer the offered data, the client issues this request and 635 + * indicates the MIME type it wants to receive. The transfer happens 636 + * through the passed file descriptor (typically created with the pipe 637 + * system call). The source client writes the data in the MIME type 638 + * representation requested and then closes the file descriptor. 639 + * 640 + * The receiving client reads from the read end of the pipe until EOF and 641 + * then closes its end, at which point the transfer is complete. 642 + * 643 + * This request may happen multiple times for different MIME types. 644 + */ 645 + static inline void 646 + zwlr_data_control_offer_v1_receive(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1, const char *mime_type, int32_t fd) 647 + { 648 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_offer_v1, 649 + ZWLR_DATA_CONTROL_OFFER_V1_RECEIVE, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_offer_v1), 0, mime_type, fd); 650 + } 651 + 652 + /** 653 + * @ingroup iface_zwlr_data_control_offer_v1 654 + * 655 + * Destroys the data offer object. 656 + */ 657 + static inline void 658 + zwlr_data_control_offer_v1_destroy(struct zwlr_data_control_offer_v1 *zwlr_data_control_offer_v1) 659 + { 660 + wl_proxy_marshal_flags((struct wl_proxy *) zwlr_data_control_offer_v1, 661 + ZWLR_DATA_CONTROL_OFFER_V1_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) zwlr_data_control_offer_v1), WL_MARSHAL_FLAG_DESTROY); 662 + } 663 + 664 + #ifdef __cplusplus 665 + } 666 + #endif 667 + 668 + #endif
+184
internal/clipboard/xdg-shell.c
··· 1 + /* Generated by wayland-scanner 1.24.0 */ 2 + 3 + /* 4 + * Copyright © 2008-2013 Kristian Høgsberg 5 + * Copyright © 2013 Rafael Antognolli 6 + * Copyright © 2013 Jasper St. Pierre 7 + * Copyright © 2010-2013 Intel Corporation 8 + * Copyright © 2015-2017 Samsung Electronics Co., Ltd 9 + * Copyright © 2015-2017 Red Hat Inc. 10 + * 11 + * Permission is hereby granted, free of charge, to any person obtaining a 12 + * copy of this software and associated documentation files (the "Software"), 13 + * to deal in the Software without restriction, including without limitation 14 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, 15 + * and/or sell copies of the Software, and to permit persons to whom the 16 + * Software is furnished to do so, subject to the following conditions: 17 + * 18 + * The above copyright notice and this permission notice (including the next 19 + * paragraph) shall be included in all copies or substantial portions of the 20 + * Software. 21 + * 22 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 25 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 + * DEALINGS IN THE SOFTWARE. 29 + */ 30 + 31 + #include <stdbool.h> 32 + #include <stdlib.h> 33 + #include <stdint.h> 34 + #include "wayland-util.h" 35 + 36 + #ifndef __has_attribute 37 + # define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */ 38 + #endif 39 + 40 + #if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4) 41 + #define WL_PRIVATE __attribute__ ((visibility("hidden"))) 42 + #else 43 + #define WL_PRIVATE 44 + #endif 45 + 46 + extern const struct wl_interface wl_output_interface; 47 + extern const struct wl_interface wl_seat_interface; 48 + extern const struct wl_interface wl_surface_interface; 49 + extern const struct wl_interface xdg_popup_interface; 50 + extern const struct wl_interface xdg_positioner_interface; 51 + extern const struct wl_interface xdg_surface_interface; 52 + extern const struct wl_interface xdg_toplevel_interface; 53 + 54 + static const struct wl_interface *xdg_shell_types[] = { 55 + NULL, 56 + NULL, 57 + NULL, 58 + NULL, 59 + &xdg_positioner_interface, 60 + &xdg_surface_interface, 61 + &wl_surface_interface, 62 + &xdg_toplevel_interface, 63 + &xdg_popup_interface, 64 + &xdg_surface_interface, 65 + &xdg_positioner_interface, 66 + &xdg_toplevel_interface, 67 + &wl_seat_interface, 68 + NULL, 69 + NULL, 70 + NULL, 71 + &wl_seat_interface, 72 + NULL, 73 + &wl_seat_interface, 74 + NULL, 75 + NULL, 76 + &wl_output_interface, 77 + &wl_seat_interface, 78 + NULL, 79 + &xdg_positioner_interface, 80 + NULL, 81 + }; 82 + 83 + static const struct wl_message xdg_wm_base_requests[] = { 84 + { "destroy", "", xdg_shell_types + 0 }, 85 + { "create_positioner", "n", xdg_shell_types + 4 }, 86 + { "get_xdg_surface", "no", xdg_shell_types + 5 }, 87 + { "pong", "u", xdg_shell_types + 0 }, 88 + }; 89 + 90 + static const struct wl_message xdg_wm_base_events[] = { 91 + { "ping", "u", xdg_shell_types + 0 }, 92 + }; 93 + 94 + WL_PRIVATE const struct wl_interface xdg_wm_base_interface = { 95 + "xdg_wm_base", 7, 96 + 4, xdg_wm_base_requests, 97 + 1, xdg_wm_base_events, 98 + }; 99 + 100 + static const struct wl_message xdg_positioner_requests[] = { 101 + { "destroy", "", xdg_shell_types + 0 }, 102 + { "set_size", "ii", xdg_shell_types + 0 }, 103 + { "set_anchor_rect", "iiii", xdg_shell_types + 0 }, 104 + { "set_anchor", "u", xdg_shell_types + 0 }, 105 + { "set_gravity", "u", xdg_shell_types + 0 }, 106 + { "set_constraint_adjustment", "u", xdg_shell_types + 0 }, 107 + { "set_offset", "ii", xdg_shell_types + 0 }, 108 + { "set_reactive", "3", xdg_shell_types + 0 }, 109 + { "set_parent_size", "3ii", xdg_shell_types + 0 }, 110 + { "set_parent_configure", "3u", xdg_shell_types + 0 }, 111 + }; 112 + 113 + WL_PRIVATE const struct wl_interface xdg_positioner_interface = { 114 + "xdg_positioner", 7, 115 + 10, xdg_positioner_requests, 116 + 0, NULL, 117 + }; 118 + 119 + static const struct wl_message xdg_surface_requests[] = { 120 + { "destroy", "", xdg_shell_types + 0 }, 121 + { "get_toplevel", "n", xdg_shell_types + 7 }, 122 + { "get_popup", "n?oo", xdg_shell_types + 8 }, 123 + { "set_window_geometry", "iiii", xdg_shell_types + 0 }, 124 + { "ack_configure", "u", xdg_shell_types + 0 }, 125 + }; 126 + 127 + static const struct wl_message xdg_surface_events[] = { 128 + { "configure", "u", xdg_shell_types + 0 }, 129 + }; 130 + 131 + WL_PRIVATE const struct wl_interface xdg_surface_interface = { 132 + "xdg_surface", 7, 133 + 5, xdg_surface_requests, 134 + 1, xdg_surface_events, 135 + }; 136 + 137 + static const struct wl_message xdg_toplevel_requests[] = { 138 + { "destroy", "", xdg_shell_types + 0 }, 139 + { "set_parent", "?o", xdg_shell_types + 11 }, 140 + { "set_title", "s", xdg_shell_types + 0 }, 141 + { "set_app_id", "s", xdg_shell_types + 0 }, 142 + { "show_window_menu", "ouii", xdg_shell_types + 12 }, 143 + { "move", "ou", xdg_shell_types + 16 }, 144 + { "resize", "ouu", xdg_shell_types + 18 }, 145 + { "set_max_size", "ii", xdg_shell_types + 0 }, 146 + { "set_min_size", "ii", xdg_shell_types + 0 }, 147 + { "set_maximized", "", xdg_shell_types + 0 }, 148 + { "unset_maximized", "", xdg_shell_types + 0 }, 149 + { "set_fullscreen", "?o", xdg_shell_types + 21 }, 150 + { "unset_fullscreen", "", xdg_shell_types + 0 }, 151 + { "set_minimized", "", xdg_shell_types + 0 }, 152 + }; 153 + 154 + static const struct wl_message xdg_toplevel_events[] = { 155 + { "configure", "iia", xdg_shell_types + 0 }, 156 + { "close", "", xdg_shell_types + 0 }, 157 + { "configure_bounds", "4ii", xdg_shell_types + 0 }, 158 + { "wm_capabilities", "5a", xdg_shell_types + 0 }, 159 + }; 160 + 161 + WL_PRIVATE const struct wl_interface xdg_toplevel_interface = { 162 + "xdg_toplevel", 7, 163 + 14, xdg_toplevel_requests, 164 + 4, xdg_toplevel_events, 165 + }; 166 + 167 + static const struct wl_message xdg_popup_requests[] = { 168 + { "destroy", "", xdg_shell_types + 0 }, 169 + { "grab", "ou", xdg_shell_types + 22 }, 170 + { "reposition", "3ou", xdg_shell_types + 24 }, 171 + }; 172 + 173 + static const struct wl_message xdg_popup_events[] = { 174 + { "configure", "iiii", xdg_shell_types + 0 }, 175 + { "popup_done", "", xdg_shell_types + 0 }, 176 + { "repositioned", "3u", xdg_shell_types + 0 }, 177 + }; 178 + 179 + WL_PRIVATE const struct wl_interface xdg_popup_interface = { 180 + "xdg_popup", 7, 181 + 3, xdg_popup_requests, 182 + 3, xdg_popup_events, 183 + }; 184 +
+2384
internal/clipboard/xdg-shell.h
··· 1 + /* Generated by wayland-scanner 1.24.0 */ 2 + 3 + #ifndef XDG_SHELL_CLIENT_PROTOCOL_H 4 + #define XDG_SHELL_CLIENT_PROTOCOL_H 5 + 6 + #include <stdint.h> 7 + #include <stddef.h> 8 + #include "wayland-client.h" 9 + 10 + #ifdef __cplusplus 11 + extern "C" { 12 + #endif 13 + 14 + /** 15 + * @page page_xdg_shell The xdg_shell protocol 16 + * @section page_ifaces_xdg_shell Interfaces 17 + * - @subpage page_iface_xdg_wm_base - create desktop-style surfaces 18 + * - @subpage page_iface_xdg_positioner - child surface positioner 19 + * - @subpage page_iface_xdg_surface - desktop user interface surface base interface 20 + * - @subpage page_iface_xdg_toplevel - toplevel surface 21 + * - @subpage page_iface_xdg_popup - short-lived, popup surfaces for menus 22 + * @section page_copyright_xdg_shell Copyright 23 + * <pre> 24 + * 25 + * Copyright © 2008-2013 Kristian Høgsberg 26 + * Copyright © 2013 Rafael Antognolli 27 + * Copyright © 2013 Jasper St. Pierre 28 + * Copyright © 2010-2013 Intel Corporation 29 + * Copyright © 2015-2017 Samsung Electronics Co., Ltd 30 + * Copyright © 2015-2017 Red Hat Inc. 31 + * 32 + * Permission is hereby granted, free of charge, to any person obtaining a 33 + * copy of this software and associated documentation files (the "Software"), 34 + * to deal in the Software without restriction, including without limitation 35 + * the rights to use, copy, modify, merge, publish, distribute, sublicense, 36 + * and/or sell copies of the Software, and to permit persons to whom the 37 + * Software is furnished to do so, subject to the following conditions: 38 + * 39 + * The above copyright notice and this permission notice (including the next 40 + * paragraph) shall be included in all copies or substantial portions of the 41 + * Software. 42 + * 43 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 45 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 46 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 47 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 48 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 49 + * DEALINGS IN THE SOFTWARE. 50 + * </pre> 51 + */ 52 + struct wl_output; 53 + struct wl_seat; 54 + struct wl_surface; 55 + struct xdg_popup; 56 + struct xdg_positioner; 57 + struct xdg_surface; 58 + struct xdg_toplevel; 59 + struct xdg_wm_base; 60 + 61 + #ifndef XDG_WM_BASE_INTERFACE 62 + #define XDG_WM_BASE_INTERFACE 63 + /** 64 + * @page page_iface_xdg_wm_base xdg_wm_base 65 + * @section page_iface_xdg_wm_base_desc Description 66 + * 67 + * The xdg_wm_base interface is exposed as a global object enabling clients 68 + * to turn their wl_surfaces into windows in a desktop environment. It 69 + * defines the basic functionality needed for clients and the compositor to 70 + * create windows that can be dragged, resized, maximized, etc, as well as 71 + * creating transient windows such as popup menus. 72 + * @section page_iface_xdg_wm_base_api API 73 + * See @ref iface_xdg_wm_base. 74 + */ 75 + /** 76 + * @defgroup iface_xdg_wm_base The xdg_wm_base interface 77 + * 78 + * The xdg_wm_base interface is exposed as a global object enabling clients 79 + * to turn their wl_surfaces into windows in a desktop environment. It 80 + * defines the basic functionality needed for clients and the compositor to 81 + * create windows that can be dragged, resized, maximized, etc, as well as 82 + * creating transient windows such as popup menus. 83 + */ 84 + extern const struct wl_interface xdg_wm_base_interface; 85 + #endif 86 + #ifndef XDG_POSITIONER_INTERFACE 87 + #define XDG_POSITIONER_INTERFACE 88 + /** 89 + * @page page_iface_xdg_positioner xdg_positioner 90 + * @section page_iface_xdg_positioner_desc Description 91 + * 92 + * The xdg_positioner provides a collection of rules for the placement of a 93 + * child surface relative to a parent surface. Rules can be defined to ensure 94 + * the child surface remains within the visible area's borders, and to 95 + * specify how the child surface changes its position, such as sliding along 96 + * an axis, or flipping around a rectangle. These positioner-created rules are 97 + * constrained by the requirement that a child surface must intersect with or 98 + * be at least partially adjacent to its parent surface. 99 + * 100 + * See the various requests for details about possible rules. 101 + * 102 + * At the time of the request, the compositor makes a copy of the rules 103 + * specified by the xdg_positioner. Thus, after the request is complete the 104 + * xdg_positioner object can be destroyed or reused; further changes to the 105 + * object will have no effect on previous usages. 106 + * 107 + * For an xdg_positioner object to be considered complete, it must have a 108 + * non-zero size set by set_size, and a non-zero anchor rectangle set by 109 + * set_anchor_rect. Passing an incomplete xdg_positioner object when 110 + * positioning a surface raises an invalid_positioner error. 111 + * @section page_iface_xdg_positioner_api API 112 + * See @ref iface_xdg_positioner. 113 + */ 114 + /** 115 + * @defgroup iface_xdg_positioner The xdg_positioner interface 116 + * 117 + * The xdg_positioner provides a collection of rules for the placement of a 118 + * child surface relative to a parent surface. Rules can be defined to ensure 119 + * the child surface remains within the visible area's borders, and to 120 + * specify how the child surface changes its position, such as sliding along 121 + * an axis, or flipping around a rectangle. These positioner-created rules are 122 + * constrained by the requirement that a child surface must intersect with or 123 + * be at least partially adjacent to its parent surface. 124 + * 125 + * See the various requests for details about possible rules. 126 + * 127 + * At the time of the request, the compositor makes a copy of the rules 128 + * specified by the xdg_positioner. Thus, after the request is complete the 129 + * xdg_positioner object can be destroyed or reused; further changes to the 130 + * object will have no effect on previous usages. 131 + * 132 + * For an xdg_positioner object to be considered complete, it must have a 133 + * non-zero size set by set_size, and a non-zero anchor rectangle set by 134 + * set_anchor_rect. Passing an incomplete xdg_positioner object when 135 + * positioning a surface raises an invalid_positioner error. 136 + */ 137 + extern const struct wl_interface xdg_positioner_interface; 138 + #endif 139 + #ifndef XDG_SURFACE_INTERFACE 140 + #define XDG_SURFACE_INTERFACE 141 + /** 142 + * @page page_iface_xdg_surface xdg_surface 143 + * @section page_iface_xdg_surface_desc Description 144 + * 145 + * An interface that may be implemented by a wl_surface, for 146 + * implementations that provide a desktop-style user interface. 147 + * 148 + * It provides a base set of functionality required to construct user 149 + * interface elements requiring management by the compositor, such as 150 + * toplevel windows, menus, etc. The types of functionality are split into 151 + * xdg_surface roles. 152 + * 153 + * Creating an xdg_surface does not set the role for a wl_surface. In order 154 + * to map an xdg_surface, the client must create a role-specific object 155 + * using, e.g., get_toplevel, get_popup. The wl_surface for any given 156 + * xdg_surface can have at most one role, and may not be assigned any role 157 + * not based on xdg_surface. 158 + * 159 + * A role must be assigned before any other requests are made to the 160 + * xdg_surface object. 161 + * 162 + * The client must call wl_surface.commit on the corresponding wl_surface 163 + * for the xdg_surface state to take effect. 164 + * 165 + * Creating an xdg_surface from a wl_surface which has a buffer attached or 166 + * committed is a client error, and any attempts by a client to attach or 167 + * manipulate a buffer prior to the first xdg_surface.configure call must 168 + * also be treated as errors. 169 + * 170 + * After creating a role-specific object and setting it up (e.g. by sending 171 + * the title, app ID, size constraints, parent, etc), the client must 172 + * perform an initial commit without any buffer attached. The compositor 173 + * will reply with initial wl_surface state such as 174 + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure 175 + * event. The client must acknowledge it and is then allowed to attach a 176 + * buffer to map the surface. 177 + * 178 + * Mapping an xdg_surface-based role surface is defined as making it 179 + * possible for the surface to be shown by the compositor. Note that 180 + * a mapped surface is not guaranteed to be visible once it is mapped. 181 + * 182 + * For an xdg_surface to be mapped by the compositor, the following 183 + * conditions must be met: 184 + * (1) the client has assigned an xdg_surface-based role to the surface 185 + * (2) the client has set and committed the xdg_surface state and the 186 + * role-dependent state to the surface 187 + * (3) the client has committed a buffer to the surface 188 + * 189 + * A newly-unmapped surface is considered to have met condition (1) out 190 + * of the 3 required conditions for mapping a surface if its role surface 191 + * has not been destroyed, i.e. the client must perform the initial commit 192 + * again before attaching a buffer. 193 + * @section page_iface_xdg_surface_api API 194 + * See @ref iface_xdg_surface. 195 + */ 196 + /** 197 + * @defgroup iface_xdg_surface The xdg_surface interface 198 + * 199 + * An interface that may be implemented by a wl_surface, for 200 + * implementations that provide a desktop-style user interface. 201 + * 202 + * It provides a base set of functionality required to construct user 203 + * interface elements requiring management by the compositor, such as 204 + * toplevel windows, menus, etc. The types of functionality are split into 205 + * xdg_surface roles. 206 + * 207 + * Creating an xdg_surface does not set the role for a wl_surface. In order 208 + * to map an xdg_surface, the client must create a role-specific object 209 + * using, e.g., get_toplevel, get_popup. The wl_surface for any given 210 + * xdg_surface can have at most one role, and may not be assigned any role 211 + * not based on xdg_surface. 212 + * 213 + * A role must be assigned before any other requests are made to the 214 + * xdg_surface object. 215 + * 216 + * The client must call wl_surface.commit on the corresponding wl_surface 217 + * for the xdg_surface state to take effect. 218 + * 219 + * Creating an xdg_surface from a wl_surface which has a buffer attached or 220 + * committed is a client error, and any attempts by a client to attach or 221 + * manipulate a buffer prior to the first xdg_surface.configure call must 222 + * also be treated as errors. 223 + * 224 + * After creating a role-specific object and setting it up (e.g. by sending 225 + * the title, app ID, size constraints, parent, etc), the client must 226 + * perform an initial commit without any buffer attached. The compositor 227 + * will reply with initial wl_surface state such as 228 + * wl_surface.preferred_buffer_scale followed by an xdg_surface.configure 229 + * event. The client must acknowledge it and is then allowed to attach a 230 + * buffer to map the surface. 231 + * 232 + * Mapping an xdg_surface-based role surface is defined as making it 233 + * possible for the surface to be shown by the compositor. Note that 234 + * a mapped surface is not guaranteed to be visible once it is mapped. 235 + * 236 + * For an xdg_surface to be mapped by the compositor, the following 237 + * conditions must be met: 238 + * (1) the client has assigned an xdg_surface-based role to the surface 239 + * (2) the client has set and committed the xdg_surface state and the 240 + * role-dependent state to the surface 241 + * (3) the client has committed a buffer to the surface 242 + * 243 + * A newly-unmapped surface is considered to have met condition (1) out 244 + * of the 3 required conditions for mapping a surface if its role surface 245 + * has not been destroyed, i.e. the client must perform the initial commit 246 + * again before attaching a buffer. 247 + */ 248 + extern const struct wl_interface xdg_surface_interface; 249 + #endif 250 + #ifndef XDG_TOPLEVEL_INTERFACE 251 + #define XDG_TOPLEVEL_INTERFACE 252 + /** 253 + * @page page_iface_xdg_toplevel xdg_toplevel 254 + * @section page_iface_xdg_toplevel_desc Description 255 + * 256 + * This interface defines an xdg_surface role which allows a surface to, 257 + * among other things, set window-like properties such as maximize, 258 + * fullscreen, and minimize, set application-specific metadata like title and 259 + * id, and well as trigger user interactive operations such as interactive 260 + * resize and move. 261 + * 262 + * A xdg_toplevel by default is responsible for providing the full intended 263 + * visual representation of the toplevel, which depending on the window 264 + * state, may mean things like a title bar, window controls and drop shadow. 265 + * 266 + * Unmapping an xdg_toplevel means that the surface cannot be shown 267 + * by the compositor until it is explicitly mapped again. 268 + * All active operations (e.g., move, resize) are canceled and all 269 + * attributes (e.g. title, state, stacking, ...) are discarded for 270 + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to 271 + * the state it had right after xdg_surface.get_toplevel. The client 272 + * can re-map the toplevel by performing a commit without any buffer 273 + * attached, waiting for a configure event and handling it as usual (see 274 + * xdg_surface description). 275 + * 276 + * Attaching a null buffer to a toplevel unmaps the surface. 277 + * @section page_iface_xdg_toplevel_api API 278 + * See @ref iface_xdg_toplevel. 279 + */ 280 + /** 281 + * @defgroup iface_xdg_toplevel The xdg_toplevel interface 282 + * 283 + * This interface defines an xdg_surface role which allows a surface to, 284 + * among other things, set window-like properties such as maximize, 285 + * fullscreen, and minimize, set application-specific metadata like title and 286 + * id, and well as trigger user interactive operations such as interactive 287 + * resize and move. 288 + * 289 + * A xdg_toplevel by default is responsible for providing the full intended 290 + * visual representation of the toplevel, which depending on the window 291 + * state, may mean things like a title bar, window controls and drop shadow. 292 + * 293 + * Unmapping an xdg_toplevel means that the surface cannot be shown 294 + * by the compositor until it is explicitly mapped again. 295 + * All active operations (e.g., move, resize) are canceled and all 296 + * attributes (e.g. title, state, stacking, ...) are discarded for 297 + * an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to 298 + * the state it had right after xdg_surface.get_toplevel. The client 299 + * can re-map the toplevel by performing a commit without any buffer 300 + * attached, waiting for a configure event and handling it as usual (see 301 + * xdg_surface description). 302 + * 303 + * Attaching a null buffer to a toplevel unmaps the surface. 304 + */ 305 + extern const struct wl_interface xdg_toplevel_interface; 306 + #endif 307 + #ifndef XDG_POPUP_INTERFACE 308 + #define XDG_POPUP_INTERFACE 309 + /** 310 + * @page page_iface_xdg_popup xdg_popup 311 + * @section page_iface_xdg_popup_desc Description 312 + * 313 + * A popup surface is a short-lived, temporary surface. It can be used to 314 + * implement for example menus, popovers, tooltips and other similar user 315 + * interface concepts. 316 + * 317 + * A popup can be made to take an explicit grab. See xdg_popup.grab for 318 + * details. 319 + * 320 + * When the popup is dismissed, a popup_done event will be sent out, and at 321 + * the same time the surface will be unmapped. See the xdg_popup.popup_done 322 + * event for details. 323 + * 324 + * Explicitly destroying the xdg_popup object will also dismiss the popup and 325 + * unmap the surface. Clients that want to dismiss the popup when another 326 + * surface of their own is clicked should dismiss the popup using the destroy 327 + * request. 328 + * 329 + * A newly created xdg_popup will be stacked on top of all previously created 330 + * xdg_popup surfaces associated with the same xdg_toplevel. 331 + * 332 + * The parent of an xdg_popup must be mapped (see the xdg_surface 333 + * description) before the xdg_popup itself. 334 + * 335 + * The client must call wl_surface.commit on the corresponding wl_surface 336 + * for the xdg_popup state to take effect. 337 + * @section page_iface_xdg_popup_api API 338 + * See @ref iface_xdg_popup. 339 + */ 340 + /** 341 + * @defgroup iface_xdg_popup The xdg_popup interface 342 + * 343 + * A popup surface is a short-lived, temporary surface. It can be used to 344 + * implement for example menus, popovers, tooltips and other similar user 345 + * interface concepts. 346 + * 347 + * A popup can be made to take an explicit grab. See xdg_popup.grab for 348 + * details. 349 + * 350 + * When the popup is dismissed, a popup_done event will be sent out, and at 351 + * the same time the surface will be unmapped. See the xdg_popup.popup_done 352 + * event for details. 353 + * 354 + * Explicitly destroying the xdg_popup object will also dismiss the popup and 355 + * unmap the surface. Clients that want to dismiss the popup when another 356 + * surface of their own is clicked should dismiss the popup using the destroy 357 + * request. 358 + * 359 + * A newly created xdg_popup will be stacked on top of all previously created 360 + * xdg_popup surfaces associated with the same xdg_toplevel. 361 + * 362 + * The parent of an xdg_popup must be mapped (see the xdg_surface 363 + * description) before the xdg_popup itself. 364 + * 365 + * The client must call wl_surface.commit on the corresponding wl_surface 366 + * for the xdg_popup state to take effect. 367 + */ 368 + extern const struct wl_interface xdg_popup_interface; 369 + #endif 370 + 371 + #ifndef XDG_WM_BASE_ERROR_ENUM 372 + #define XDG_WM_BASE_ERROR_ENUM 373 + enum xdg_wm_base_error { 374 + /** 375 + * given wl_surface has another role 376 + */ 377 + XDG_WM_BASE_ERROR_ROLE = 0, 378 + /** 379 + * xdg_wm_base was destroyed before children 380 + */ 381 + XDG_WM_BASE_ERROR_DEFUNCT_SURFACES = 1, 382 + /** 383 + * the client tried to map or destroy a non-topmost popup 384 + */ 385 + XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP = 2, 386 + /** 387 + * the client specified an invalid popup parent surface 388 + */ 389 + XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT = 3, 390 + /** 391 + * the client provided an invalid surface state 392 + */ 393 + XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE = 4, 394 + /** 395 + * the client provided an invalid positioner 396 + */ 397 + XDG_WM_BASE_ERROR_INVALID_POSITIONER = 5, 398 + /** 399 + * the client didn’t respond to a ping event in time 400 + */ 401 + XDG_WM_BASE_ERROR_UNRESPONSIVE = 6, 402 + }; 403 + #endif /* XDG_WM_BASE_ERROR_ENUM */ 404 + 405 + /** 406 + * @ingroup iface_xdg_wm_base 407 + * @struct xdg_wm_base_listener 408 + */ 409 + struct xdg_wm_base_listener { 410 + /** 411 + * check if the client is alive 412 + * 413 + * The ping event asks the client if it's still alive. Pass the 414 + * serial specified in the event back to the compositor by sending 415 + * a "pong" request back with the specified serial. See 416 + * xdg_wm_base.pong. 417 + * 418 + * Compositors can use this to determine if the client is still 419 + * alive. It's unspecified what will happen if the client doesn't 420 + * respond to the ping request, or in what timeframe. Clients 421 + * should try to respond in a reasonable amount of time. The 422 + * “unresponsive” error is provided for compositors that wish 423 + * to disconnect unresponsive clients. 424 + * 425 + * A compositor is free to ping in any way it wants, but a client 426 + * must always respond to any xdg_wm_base object it created. 427 + * @param serial pass this to the pong request 428 + */ 429 + void (*ping)(void *data, 430 + struct xdg_wm_base *xdg_wm_base, 431 + uint32_t serial); 432 + }; 433 + 434 + /** 435 + * @ingroup iface_xdg_wm_base 436 + */ 437 + static inline int 438 + xdg_wm_base_add_listener(struct xdg_wm_base *xdg_wm_base, 439 + const struct xdg_wm_base_listener *listener, void *data) 440 + { 441 + return wl_proxy_add_listener((struct wl_proxy *) xdg_wm_base, 442 + (void (**)(void)) listener, data); 443 + } 444 + 445 + #define XDG_WM_BASE_DESTROY 0 446 + #define XDG_WM_BASE_CREATE_POSITIONER 1 447 + #define XDG_WM_BASE_GET_XDG_SURFACE 2 448 + #define XDG_WM_BASE_PONG 3 449 + 450 + /** 451 + * @ingroup iface_xdg_wm_base 452 + */ 453 + #define XDG_WM_BASE_PING_SINCE_VERSION 1 454 + 455 + /** 456 + * @ingroup iface_xdg_wm_base 457 + */ 458 + #define XDG_WM_BASE_DESTROY_SINCE_VERSION 1 459 + /** 460 + * @ingroup iface_xdg_wm_base 461 + */ 462 + #define XDG_WM_BASE_CREATE_POSITIONER_SINCE_VERSION 1 463 + /** 464 + * @ingroup iface_xdg_wm_base 465 + */ 466 + #define XDG_WM_BASE_GET_XDG_SURFACE_SINCE_VERSION 1 467 + /** 468 + * @ingroup iface_xdg_wm_base 469 + */ 470 + #define XDG_WM_BASE_PONG_SINCE_VERSION 1 471 + 472 + /** @ingroup iface_xdg_wm_base */ 473 + static inline void 474 + xdg_wm_base_set_user_data(struct xdg_wm_base *xdg_wm_base, void *user_data) 475 + { 476 + wl_proxy_set_user_data((struct wl_proxy *) xdg_wm_base, user_data); 477 + } 478 + 479 + /** @ingroup iface_xdg_wm_base */ 480 + static inline void * 481 + xdg_wm_base_get_user_data(struct xdg_wm_base *xdg_wm_base) 482 + { 483 + return wl_proxy_get_user_data((struct wl_proxy *) xdg_wm_base); 484 + } 485 + 486 + static inline uint32_t 487 + xdg_wm_base_get_version(struct xdg_wm_base *xdg_wm_base) 488 + { 489 + return wl_proxy_get_version((struct wl_proxy *) xdg_wm_base); 490 + } 491 + 492 + /** 493 + * @ingroup iface_xdg_wm_base 494 + * 495 + * Destroy this xdg_wm_base object. 496 + * 497 + * Destroying a bound xdg_wm_base object while there are surfaces 498 + * still alive created by this xdg_wm_base object instance is illegal 499 + * and will result in a defunct_surfaces error. 500 + */ 501 + static inline void 502 + xdg_wm_base_destroy(struct xdg_wm_base *xdg_wm_base) 503 + { 504 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, 505 + XDG_WM_BASE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), WL_MARSHAL_FLAG_DESTROY); 506 + } 507 + 508 + /** 509 + * @ingroup iface_xdg_wm_base 510 + * 511 + * Create a positioner object. A positioner object is used to position 512 + * surfaces relative to some parent surface. See the interface description 513 + * and xdg_surface.get_popup for details. 514 + */ 515 + static inline struct xdg_positioner * 516 + xdg_wm_base_create_positioner(struct xdg_wm_base *xdg_wm_base) 517 + { 518 + struct wl_proxy *id; 519 + 520 + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, 521 + XDG_WM_BASE_CREATE_POSITIONER, &xdg_positioner_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL); 522 + 523 + return (struct xdg_positioner *) id; 524 + } 525 + 526 + /** 527 + * @ingroup iface_xdg_wm_base 528 + * 529 + * This creates an xdg_surface for the given surface. While xdg_surface 530 + * itself is not a role, the corresponding surface may only be assigned 531 + * a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is 532 + * illegal to create an xdg_surface for a wl_surface which already has an 533 + * assigned role and this will result in a role error. 534 + * 535 + * This creates an xdg_surface for the given surface. An xdg_surface is 536 + * used as basis to define a role to a given surface, such as xdg_toplevel 537 + * or xdg_popup. It also manages functionality shared between xdg_surface 538 + * based surface roles. 539 + * 540 + * See the documentation of xdg_surface for more details about what an 541 + * xdg_surface is and how it is used. 542 + */ 543 + static inline struct xdg_surface * 544 + xdg_wm_base_get_xdg_surface(struct xdg_wm_base *xdg_wm_base, struct wl_surface *surface) 545 + { 546 + struct wl_proxy *id; 547 + 548 + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, 549 + XDG_WM_BASE_GET_XDG_SURFACE, &xdg_surface_interface, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, NULL, surface); 550 + 551 + return (struct xdg_surface *) id; 552 + } 553 + 554 + /** 555 + * @ingroup iface_xdg_wm_base 556 + * 557 + * A client must respond to a ping event with a pong request or 558 + * the client may be deemed unresponsive. See xdg_wm_base.ping 559 + * and xdg_wm_base.error.unresponsive. 560 + */ 561 + static inline void 562 + xdg_wm_base_pong(struct xdg_wm_base *xdg_wm_base, uint32_t serial) 563 + { 564 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_wm_base, 565 + XDG_WM_BASE_PONG, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_wm_base), 0, serial); 566 + } 567 + 568 + #ifndef XDG_POSITIONER_ERROR_ENUM 569 + #define XDG_POSITIONER_ERROR_ENUM 570 + enum xdg_positioner_error { 571 + /** 572 + * invalid input provided 573 + */ 574 + XDG_POSITIONER_ERROR_INVALID_INPUT = 0, 575 + }; 576 + #endif /* XDG_POSITIONER_ERROR_ENUM */ 577 + 578 + #ifndef XDG_POSITIONER_ANCHOR_ENUM 579 + #define XDG_POSITIONER_ANCHOR_ENUM 580 + enum xdg_positioner_anchor { 581 + XDG_POSITIONER_ANCHOR_NONE = 0, 582 + XDG_POSITIONER_ANCHOR_TOP = 1, 583 + XDG_POSITIONER_ANCHOR_BOTTOM = 2, 584 + XDG_POSITIONER_ANCHOR_LEFT = 3, 585 + XDG_POSITIONER_ANCHOR_RIGHT = 4, 586 + XDG_POSITIONER_ANCHOR_TOP_LEFT = 5, 587 + XDG_POSITIONER_ANCHOR_BOTTOM_LEFT = 6, 588 + XDG_POSITIONER_ANCHOR_TOP_RIGHT = 7, 589 + XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT = 8, 590 + }; 591 + #endif /* XDG_POSITIONER_ANCHOR_ENUM */ 592 + 593 + #ifndef XDG_POSITIONER_GRAVITY_ENUM 594 + #define XDG_POSITIONER_GRAVITY_ENUM 595 + enum xdg_positioner_gravity { 596 + XDG_POSITIONER_GRAVITY_NONE = 0, 597 + XDG_POSITIONER_GRAVITY_TOP = 1, 598 + XDG_POSITIONER_GRAVITY_BOTTOM = 2, 599 + XDG_POSITIONER_GRAVITY_LEFT = 3, 600 + XDG_POSITIONER_GRAVITY_RIGHT = 4, 601 + XDG_POSITIONER_GRAVITY_TOP_LEFT = 5, 602 + XDG_POSITIONER_GRAVITY_BOTTOM_LEFT = 6, 603 + XDG_POSITIONER_GRAVITY_TOP_RIGHT = 7, 604 + XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT = 8, 605 + }; 606 + #endif /* XDG_POSITIONER_GRAVITY_ENUM */ 607 + 608 + #ifndef XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM 609 + #define XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM 610 + /** 611 + * @ingroup iface_xdg_positioner 612 + * constraint adjustments 613 + * 614 + * The constraint adjustment value define ways the compositor will adjust 615 + * the position of the surface, if the unadjusted position would result 616 + * in the surface being partly constrained. 617 + * 618 + * Whether a surface is considered 'constrained' is left to the compositor 619 + * to determine. For example, the surface may be partly outside the 620 + * compositor's defined 'work area', thus necessitating the child surface's 621 + * position be adjusted until it is entirely inside the work area. 622 + * 623 + * The adjustments can be combined, according to a defined precedence: 1) 624 + * Flip, 2) Slide, 3) Resize. 625 + */ 626 + enum xdg_positioner_constraint_adjustment { 627 + /** 628 + * don't move the child surface when constrained 629 + * 630 + * Don't alter the surface position even if it is constrained on 631 + * some axis, for example partially outside the edge of an output. 632 + */ 633 + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE = 0, 634 + /** 635 + * move along the x axis until unconstrained 636 + * 637 + * Slide the surface along the x axis until it is no longer 638 + * constrained. 639 + * 640 + * First try to slide towards the direction of the gravity on the x 641 + * axis until either the edge in the opposite direction of the 642 + * gravity is unconstrained or the edge in the direction of the 643 + * gravity is constrained. 644 + * 645 + * Then try to slide towards the opposite direction of the gravity 646 + * on the x axis until either the edge in the direction of the 647 + * gravity is unconstrained or the edge in the opposite direction 648 + * of the gravity is constrained. 649 + */ 650 + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1, 651 + /** 652 + * move along the y axis until unconstrained 653 + * 654 + * Slide the surface along the y axis until it is no longer 655 + * constrained. 656 + * 657 + * First try to slide towards the direction of the gravity on the y 658 + * axis until either the edge in the opposite direction of the 659 + * gravity is unconstrained or the edge in the direction of the 660 + * gravity is constrained. 661 + * 662 + * Then try to slide towards the opposite direction of the gravity 663 + * on the y axis until either the edge in the direction of the 664 + * gravity is unconstrained or the edge in the opposite direction 665 + * of the gravity is constrained. 666 + */ 667 + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 2, 668 + /** 669 + * invert the anchor and gravity on the x axis 670 + * 671 + * Invert the anchor and gravity on the x axis if the surface is 672 + * constrained on the x axis. For example, if the left edge of the 673 + * surface is constrained, the gravity is 'left' and the anchor is 674 + * 'left', change the gravity to 'right' and the anchor to 'right'. 675 + * 676 + * If the adjusted position also ends up being constrained, the 677 + * resulting position of the flip_x adjustment will be the one 678 + * before the adjustment. 679 + */ 680 + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X = 4, 681 + /** 682 + * invert the anchor and gravity on the y axis 683 + * 684 + * Invert the anchor and gravity on the y axis if the surface is 685 + * constrained on the y axis. For example, if the bottom edge of 686 + * the surface is constrained, the gravity is 'bottom' and the 687 + * anchor is 'bottom', change the gravity to 'top' and the anchor 688 + * to 'top'. 689 + * 690 + * The adjusted position is calculated given the original anchor 691 + * rectangle and offset, but with the new flipped anchor and 692 + * gravity values. 693 + * 694 + * If the adjusted position also ends up being constrained, the 695 + * resulting position of the flip_y adjustment will be the one 696 + * before the adjustment. 697 + */ 698 + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y = 8, 699 + /** 700 + * horizontally resize the surface 701 + * 702 + * Resize the surface horizontally so that it is completely 703 + * unconstrained. 704 + */ 705 + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X = 16, 706 + /** 707 + * vertically resize the surface 708 + * 709 + * Resize the surface vertically so that it is completely 710 + * unconstrained. 711 + */ 712 + XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 32, 713 + }; 714 + #endif /* XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_ENUM */ 715 + 716 + #define XDG_POSITIONER_DESTROY 0 717 + #define XDG_POSITIONER_SET_SIZE 1 718 + #define XDG_POSITIONER_SET_ANCHOR_RECT 2 719 + #define XDG_POSITIONER_SET_ANCHOR 3 720 + #define XDG_POSITIONER_SET_GRAVITY 4 721 + #define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT 5 722 + #define XDG_POSITIONER_SET_OFFSET 6 723 + #define XDG_POSITIONER_SET_REACTIVE 7 724 + #define XDG_POSITIONER_SET_PARENT_SIZE 8 725 + #define XDG_POSITIONER_SET_PARENT_CONFIGURE 9 726 + 727 + 728 + /** 729 + * @ingroup iface_xdg_positioner 730 + */ 731 + #define XDG_POSITIONER_DESTROY_SINCE_VERSION 1 732 + /** 733 + * @ingroup iface_xdg_positioner 734 + */ 735 + #define XDG_POSITIONER_SET_SIZE_SINCE_VERSION 1 736 + /** 737 + * @ingroup iface_xdg_positioner 738 + */ 739 + #define XDG_POSITIONER_SET_ANCHOR_RECT_SINCE_VERSION 1 740 + /** 741 + * @ingroup iface_xdg_positioner 742 + */ 743 + #define XDG_POSITIONER_SET_ANCHOR_SINCE_VERSION 1 744 + /** 745 + * @ingroup iface_xdg_positioner 746 + */ 747 + #define XDG_POSITIONER_SET_GRAVITY_SINCE_VERSION 1 748 + /** 749 + * @ingroup iface_xdg_positioner 750 + */ 751 + #define XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT_SINCE_VERSION 1 752 + /** 753 + * @ingroup iface_xdg_positioner 754 + */ 755 + #define XDG_POSITIONER_SET_OFFSET_SINCE_VERSION 1 756 + /** 757 + * @ingroup iface_xdg_positioner 758 + */ 759 + #define XDG_POSITIONER_SET_REACTIVE_SINCE_VERSION 3 760 + /** 761 + * @ingroup iface_xdg_positioner 762 + */ 763 + #define XDG_POSITIONER_SET_PARENT_SIZE_SINCE_VERSION 3 764 + /** 765 + * @ingroup iface_xdg_positioner 766 + */ 767 + #define XDG_POSITIONER_SET_PARENT_CONFIGURE_SINCE_VERSION 3 768 + 769 + /** @ingroup iface_xdg_positioner */ 770 + static inline void 771 + xdg_positioner_set_user_data(struct xdg_positioner *xdg_positioner, void *user_data) 772 + { 773 + wl_proxy_set_user_data((struct wl_proxy *) xdg_positioner, user_data); 774 + } 775 + 776 + /** @ingroup iface_xdg_positioner */ 777 + static inline void * 778 + xdg_positioner_get_user_data(struct xdg_positioner *xdg_positioner) 779 + { 780 + return wl_proxy_get_user_data((struct wl_proxy *) xdg_positioner); 781 + } 782 + 783 + static inline uint32_t 784 + xdg_positioner_get_version(struct xdg_positioner *xdg_positioner) 785 + { 786 + return wl_proxy_get_version((struct wl_proxy *) xdg_positioner); 787 + } 788 + 789 + /** 790 + * @ingroup iface_xdg_positioner 791 + * 792 + * Notify the compositor that the xdg_positioner will no longer be used. 793 + */ 794 + static inline void 795 + xdg_positioner_destroy(struct xdg_positioner *xdg_positioner) 796 + { 797 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 798 + XDG_POSITIONER_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), WL_MARSHAL_FLAG_DESTROY); 799 + } 800 + 801 + /** 802 + * @ingroup iface_xdg_positioner 803 + * 804 + * Set the size of the surface that is to be positioned with the positioner 805 + * object. The size is in surface-local coordinates and corresponds to the 806 + * window geometry. See xdg_surface.set_window_geometry. 807 + * 808 + * If a zero or negative size is set the invalid_input error is raised. 809 + */ 810 + static inline void 811 + xdg_positioner_set_size(struct xdg_positioner *xdg_positioner, int32_t width, int32_t height) 812 + { 813 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 814 + XDG_POSITIONER_SET_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, width, height); 815 + } 816 + 817 + /** 818 + * @ingroup iface_xdg_positioner 819 + * 820 + * Specify the anchor rectangle within the parent surface that the child 821 + * surface will be placed relative to. The rectangle is relative to the 822 + * window geometry as defined by xdg_surface.set_window_geometry of the 823 + * parent surface. 824 + * 825 + * When the xdg_positioner object is used to position a child surface, the 826 + * anchor rectangle may not extend outside the window geometry of the 827 + * positioned child's parent surface. 828 + * 829 + * If a negative size is set the invalid_input error is raised. 830 + */ 831 + static inline void 832 + xdg_positioner_set_anchor_rect(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y, int32_t width, int32_t height) 833 + { 834 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 835 + XDG_POSITIONER_SET_ANCHOR_RECT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y, width, height); 836 + } 837 + 838 + /** 839 + * @ingroup iface_xdg_positioner 840 + * 841 + * Defines the anchor point for the anchor rectangle. The specified anchor 842 + * is used derive an anchor point that the child surface will be 843 + * positioned relative to. If a corner anchor is set (e.g. 'top_left' or 844 + * 'bottom_right'), the anchor point will be at the specified corner; 845 + * otherwise, the derived anchor point will be centered on the specified 846 + * edge, or in the center of the anchor rectangle if no edge is specified. 847 + */ 848 + static inline void 849 + xdg_positioner_set_anchor(struct xdg_positioner *xdg_positioner, uint32_t anchor) 850 + { 851 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 852 + XDG_POSITIONER_SET_ANCHOR, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, anchor); 853 + } 854 + 855 + /** 856 + * @ingroup iface_xdg_positioner 857 + * 858 + * Defines in what direction a surface should be positioned, relative to 859 + * the anchor point of the parent surface. If a corner gravity is 860 + * specified (e.g. 'bottom_right' or 'top_left'), then the child surface 861 + * will be placed towards the specified gravity; otherwise, the child 862 + * surface will be centered over the anchor point on any axis that had no 863 + * gravity specified. If the gravity is not in the ‘gravity’ enum, an 864 + * invalid_input error is raised. 865 + */ 866 + static inline void 867 + xdg_positioner_set_gravity(struct xdg_positioner *xdg_positioner, uint32_t gravity) 868 + { 869 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 870 + XDG_POSITIONER_SET_GRAVITY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, gravity); 871 + } 872 + 873 + /** 874 + * @ingroup iface_xdg_positioner 875 + * 876 + * Specify how the window should be positioned if the originally intended 877 + * position caused the surface to be constrained, meaning at least 878 + * partially outside positioning boundaries set by the compositor. The 879 + * adjustment is set by constructing a bitmask describing the adjustment to 880 + * be made when the surface is constrained on that axis. 881 + * 882 + * If no bit for one axis is set, the compositor will assume that the child 883 + * surface should not change its position on that axis when constrained. 884 + * 885 + * If more than one bit for one axis is set, the order of how adjustments 886 + * are applied is specified in the corresponding adjustment descriptions. 887 + * 888 + * The default adjustment is none. 889 + */ 890 + static inline void 891 + xdg_positioner_set_constraint_adjustment(struct xdg_positioner *xdg_positioner, uint32_t constraint_adjustment) 892 + { 893 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 894 + XDG_POSITIONER_SET_CONSTRAINT_ADJUSTMENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, constraint_adjustment); 895 + } 896 + 897 + /** 898 + * @ingroup iface_xdg_positioner 899 + * 900 + * Specify the surface position offset relative to the position of the 901 + * anchor on the anchor rectangle and the anchor on the surface. For 902 + * example if the anchor of the anchor rectangle is at (x, y), the surface 903 + * has the gravity bottom|right, and the offset is (ox, oy), the calculated 904 + * surface position will be (x + ox, y + oy). The offset position of the 905 + * surface is the one used for constraint testing. See 906 + * set_constraint_adjustment. 907 + * 908 + * An example use case is placing a popup menu on top of a user interface 909 + * element, while aligning the user interface element of the parent surface 910 + * with some user interface element placed somewhere in the popup surface. 911 + */ 912 + static inline void 913 + xdg_positioner_set_offset(struct xdg_positioner *xdg_positioner, int32_t x, int32_t y) 914 + { 915 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 916 + XDG_POSITIONER_SET_OFFSET, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, x, y); 917 + } 918 + 919 + /** 920 + * @ingroup iface_xdg_positioner 921 + * 922 + * When set reactive, the surface is reconstrained if the conditions used 923 + * for constraining changed, e.g. the parent window moved. 924 + * 925 + * If the conditions changed and the popup was reconstrained, an 926 + * xdg_popup.configure event is sent with updated geometry, followed by an 927 + * xdg_surface.configure event. 928 + */ 929 + static inline void 930 + xdg_positioner_set_reactive(struct xdg_positioner *xdg_positioner) 931 + { 932 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 933 + XDG_POSITIONER_SET_REACTIVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0); 934 + } 935 + 936 + /** 937 + * @ingroup iface_xdg_positioner 938 + * 939 + * Set the parent window geometry the compositor should use when 940 + * positioning the popup. The compositor may use this information to 941 + * determine the future state the popup should be constrained using. If 942 + * this doesn't match the dimension of the parent the popup is eventually 943 + * positioned against, the behavior is undefined. 944 + * 945 + * The arguments are given in the surface-local coordinate space. 946 + */ 947 + static inline void 948 + xdg_positioner_set_parent_size(struct xdg_positioner *xdg_positioner, int32_t parent_width, int32_t parent_height) 949 + { 950 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 951 + XDG_POSITIONER_SET_PARENT_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, parent_width, parent_height); 952 + } 953 + 954 + /** 955 + * @ingroup iface_xdg_positioner 956 + * 957 + * Set the serial of an xdg_surface.configure event this positioner will be 958 + * used in response to. The compositor may use this information together 959 + * with set_parent_size to determine what future state the popup should be 960 + * constrained using. 961 + */ 962 + static inline void 963 + xdg_positioner_set_parent_configure(struct xdg_positioner *xdg_positioner, uint32_t serial) 964 + { 965 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_positioner, 966 + XDG_POSITIONER_SET_PARENT_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_positioner), 0, serial); 967 + } 968 + 969 + #ifndef XDG_SURFACE_ERROR_ENUM 970 + #define XDG_SURFACE_ERROR_ENUM 971 + enum xdg_surface_error { 972 + /** 973 + * Surface was not fully constructed 974 + */ 975 + XDG_SURFACE_ERROR_NOT_CONSTRUCTED = 1, 976 + /** 977 + * Surface was already constructed 978 + */ 979 + XDG_SURFACE_ERROR_ALREADY_CONSTRUCTED = 2, 980 + /** 981 + * Attaching a buffer to an unconfigured surface 982 + */ 983 + XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER = 3, 984 + /** 985 + * Invalid serial number when acking a configure event 986 + */ 987 + XDG_SURFACE_ERROR_INVALID_SERIAL = 4, 988 + /** 989 + * Width or height was zero or negative 990 + */ 991 + XDG_SURFACE_ERROR_INVALID_SIZE = 5, 992 + /** 993 + * Surface was destroyed before its role object 994 + */ 995 + XDG_SURFACE_ERROR_DEFUNCT_ROLE_OBJECT = 6, 996 + }; 997 + #endif /* XDG_SURFACE_ERROR_ENUM */ 998 + 999 + /** 1000 + * @ingroup iface_xdg_surface 1001 + * @struct xdg_surface_listener 1002 + */ 1003 + struct xdg_surface_listener { 1004 + /** 1005 + * suggest a surface change 1006 + * 1007 + * The configure event marks the end of a configure sequence. A 1008 + * configure sequence is a set of one or more events configuring 1009 + * the state of the xdg_surface, including the final 1010 + * xdg_surface.configure event. 1011 + * 1012 + * Where applicable, xdg_surface surface roles will during a 1013 + * configure sequence extend this event as a latched state sent as 1014 + * events before the xdg_surface.configure event. Such events 1015 + * should be considered to make up a set of atomically applied 1016 + * configuration states, where the xdg_surface.configure commits 1017 + * the accumulated state. 1018 + * 1019 + * Clients should arrange their surface for the new states, and 1020 + * then send an ack_configure request with the serial sent in this 1021 + * configure event at some point before committing the new surface. 1022 + * 1023 + * If the client receives multiple configure events before it can 1024 + * respond to one, it is free to discard all but the last event it 1025 + * received. 1026 + * @param serial serial of the configure event 1027 + */ 1028 + void (*configure)(void *data, 1029 + struct xdg_surface *xdg_surface, 1030 + uint32_t serial); 1031 + }; 1032 + 1033 + /** 1034 + * @ingroup iface_xdg_surface 1035 + */ 1036 + static inline int 1037 + xdg_surface_add_listener(struct xdg_surface *xdg_surface, 1038 + const struct xdg_surface_listener *listener, void *data) 1039 + { 1040 + return wl_proxy_add_listener((struct wl_proxy *) xdg_surface, 1041 + (void (**)(void)) listener, data); 1042 + } 1043 + 1044 + #define XDG_SURFACE_DESTROY 0 1045 + #define XDG_SURFACE_GET_TOPLEVEL 1 1046 + #define XDG_SURFACE_GET_POPUP 2 1047 + #define XDG_SURFACE_SET_WINDOW_GEOMETRY 3 1048 + #define XDG_SURFACE_ACK_CONFIGURE 4 1049 + 1050 + /** 1051 + * @ingroup iface_xdg_surface 1052 + */ 1053 + #define XDG_SURFACE_CONFIGURE_SINCE_VERSION 1 1054 + 1055 + /** 1056 + * @ingroup iface_xdg_surface 1057 + */ 1058 + #define XDG_SURFACE_DESTROY_SINCE_VERSION 1 1059 + /** 1060 + * @ingroup iface_xdg_surface 1061 + */ 1062 + #define XDG_SURFACE_GET_TOPLEVEL_SINCE_VERSION 1 1063 + /** 1064 + * @ingroup iface_xdg_surface 1065 + */ 1066 + #define XDG_SURFACE_GET_POPUP_SINCE_VERSION 1 1067 + /** 1068 + * @ingroup iface_xdg_surface 1069 + */ 1070 + #define XDG_SURFACE_SET_WINDOW_GEOMETRY_SINCE_VERSION 1 1071 + /** 1072 + * @ingroup iface_xdg_surface 1073 + */ 1074 + #define XDG_SURFACE_ACK_CONFIGURE_SINCE_VERSION 1 1075 + 1076 + /** @ingroup iface_xdg_surface */ 1077 + static inline void 1078 + xdg_surface_set_user_data(struct xdg_surface *xdg_surface, void *user_data) 1079 + { 1080 + wl_proxy_set_user_data((struct wl_proxy *) xdg_surface, user_data); 1081 + } 1082 + 1083 + /** @ingroup iface_xdg_surface */ 1084 + static inline void * 1085 + xdg_surface_get_user_data(struct xdg_surface *xdg_surface) 1086 + { 1087 + return wl_proxy_get_user_data((struct wl_proxy *) xdg_surface); 1088 + } 1089 + 1090 + static inline uint32_t 1091 + xdg_surface_get_version(struct xdg_surface *xdg_surface) 1092 + { 1093 + return wl_proxy_get_version((struct wl_proxy *) xdg_surface); 1094 + } 1095 + 1096 + /** 1097 + * @ingroup iface_xdg_surface 1098 + * 1099 + * Destroy the xdg_surface object. An xdg_surface must only be destroyed 1100 + * after its role object has been destroyed, otherwise 1101 + * a defunct_role_object error is raised. 1102 + */ 1103 + static inline void 1104 + xdg_surface_destroy(struct xdg_surface *xdg_surface) 1105 + { 1106 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, 1107 + XDG_SURFACE_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), WL_MARSHAL_FLAG_DESTROY); 1108 + } 1109 + 1110 + /** 1111 + * @ingroup iface_xdg_surface 1112 + * 1113 + * This creates an xdg_toplevel object for the given xdg_surface and gives 1114 + * the associated wl_surface the xdg_toplevel role. 1115 + * 1116 + * See the documentation of xdg_toplevel for more details about what an 1117 + * xdg_toplevel is and how it is used. 1118 + */ 1119 + static inline struct xdg_toplevel * 1120 + xdg_surface_get_toplevel(struct xdg_surface *xdg_surface) 1121 + { 1122 + struct wl_proxy *id; 1123 + 1124 + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, 1125 + XDG_SURFACE_GET_TOPLEVEL, &xdg_toplevel_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL); 1126 + 1127 + return (struct xdg_toplevel *) id; 1128 + } 1129 + 1130 + /** 1131 + * @ingroup iface_xdg_surface 1132 + * 1133 + * This creates an xdg_popup object for the given xdg_surface and gives 1134 + * the associated wl_surface the xdg_popup role. 1135 + * 1136 + * If null is passed as a parent, a parent surface must be specified using 1137 + * some other protocol, before committing the initial state. 1138 + * 1139 + * See the documentation of xdg_popup for more details about what an 1140 + * xdg_popup is and how it is used. 1141 + */ 1142 + static inline struct xdg_popup * 1143 + xdg_surface_get_popup(struct xdg_surface *xdg_surface, struct xdg_surface *parent, struct xdg_positioner *positioner) 1144 + { 1145 + struct wl_proxy *id; 1146 + 1147 + id = wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, 1148 + XDG_SURFACE_GET_POPUP, &xdg_popup_interface, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, NULL, parent, positioner); 1149 + 1150 + return (struct xdg_popup *) id; 1151 + } 1152 + 1153 + /** 1154 + * @ingroup iface_xdg_surface 1155 + * 1156 + * The window geometry of a surface is its "visible bounds" from the 1157 + * user's perspective. Client-side decorations often have invisible 1158 + * portions like drop-shadows which should be ignored for the 1159 + * purposes of aligning, placing and constraining windows. Note that 1160 + * in some situations, compositors may clip rendering to the window 1161 + * geometry, so the client should avoid putting functional elements 1162 + * outside of it. 1163 + * 1164 + * The window geometry is double-buffered state, see wl_surface.commit. 1165 + * 1166 + * When maintaining a position, the compositor should treat the (x, y) 1167 + * coordinate of the window geometry as the top left corner of the window. 1168 + * A client changing the (x, y) window geometry coordinate should in 1169 + * general not alter the position of the window. 1170 + * 1171 + * Once the window geometry of the surface is set, it is not possible to 1172 + * unset it, and it will remain the same until set_window_geometry is 1173 + * called again, even if a new subsurface or buffer is attached. 1174 + * 1175 + * If never set, the value is the full bounds of the surface, 1176 + * including any subsurfaces. This updates dynamically on every 1177 + * commit. This unset is meant for extremely simple clients. 1178 + * 1179 + * The arguments are given in the surface-local coordinate space of 1180 + * the wl_surface associated with this xdg_surface, and may extend outside 1181 + * of the wl_surface itself to mark parts of the subsurface tree as part of 1182 + * the window geometry. 1183 + * 1184 + * When applied, the effective window geometry will be the set window 1185 + * geometry clamped to the bounding rectangle of the combined 1186 + * geometry of the surface of the xdg_surface and the associated 1187 + * subsurfaces. 1188 + * 1189 + * The effective geometry will not be recalculated unless a new call to 1190 + * set_window_geometry is done and the new pending surface state is 1191 + * subsequently applied. 1192 + * 1193 + * The width and height of the effective window geometry must be 1194 + * greater than zero. Setting an invalid size will raise an 1195 + * invalid_size error. 1196 + */ 1197 + static inline void 1198 + xdg_surface_set_window_geometry(struct xdg_surface *xdg_surface, int32_t x, int32_t y, int32_t width, int32_t height) 1199 + { 1200 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, 1201 + XDG_SURFACE_SET_WINDOW_GEOMETRY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, x, y, width, height); 1202 + } 1203 + 1204 + /** 1205 + * @ingroup iface_xdg_surface 1206 + * 1207 + * When a configure event is received, if a client commits the 1208 + * surface in response to the configure event, then the client 1209 + * must make an ack_configure request sometime before the commit 1210 + * request, passing along the serial of the configure event. 1211 + * 1212 + * For instance, for toplevel surfaces the compositor might use this 1213 + * information to move a surface to the top left only when the client has 1214 + * drawn itself for the maximized or fullscreen state. 1215 + * 1216 + * If the client receives multiple configure events before it 1217 + * can respond to one, it only has to ack the last configure event. 1218 + * Acking a configure event that was never sent raises an invalid_serial 1219 + * error. 1220 + * 1221 + * A client is not required to commit immediately after sending 1222 + * an ack_configure request - it may even ack_configure several times 1223 + * before its next surface commit. 1224 + * 1225 + * A client may send multiple ack_configure requests before committing, but 1226 + * only the last request sent before a commit indicates which configure 1227 + * event the client really is responding to. 1228 + * 1229 + * Sending an ack_configure request consumes the serial number sent with 1230 + * the request, as well as serial numbers sent by all configure events 1231 + * sent on this xdg_surface prior to the configure event referenced by 1232 + * the committed serial. 1233 + * 1234 + * It is an error to issue multiple ack_configure requests referencing a 1235 + * serial from the same configure event, or to issue an ack_configure 1236 + * request referencing a serial from a configure event issued before the 1237 + * event identified by the last ack_configure request for the same 1238 + * xdg_surface. Doing so will raise an invalid_serial error. 1239 + */ 1240 + static inline void 1241 + xdg_surface_ack_configure(struct xdg_surface *xdg_surface, uint32_t serial) 1242 + { 1243 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_surface, 1244 + XDG_SURFACE_ACK_CONFIGURE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_surface), 0, serial); 1245 + } 1246 + 1247 + #ifndef XDG_TOPLEVEL_ERROR_ENUM 1248 + #define XDG_TOPLEVEL_ERROR_ENUM 1249 + enum xdg_toplevel_error { 1250 + /** 1251 + * provided value is not a valid variant of the resize_edge enum 1252 + */ 1253 + XDG_TOPLEVEL_ERROR_INVALID_RESIZE_EDGE = 0, 1254 + /** 1255 + * invalid parent toplevel 1256 + */ 1257 + XDG_TOPLEVEL_ERROR_INVALID_PARENT = 1, 1258 + /** 1259 + * client provided an invalid min or max size 1260 + */ 1261 + XDG_TOPLEVEL_ERROR_INVALID_SIZE = 2, 1262 + }; 1263 + #endif /* XDG_TOPLEVEL_ERROR_ENUM */ 1264 + 1265 + #ifndef XDG_TOPLEVEL_RESIZE_EDGE_ENUM 1266 + #define XDG_TOPLEVEL_RESIZE_EDGE_ENUM 1267 + /** 1268 + * @ingroup iface_xdg_toplevel 1269 + * edge values for resizing 1270 + * 1271 + * These values are used to indicate which edge of a surface 1272 + * is being dragged in a resize operation. 1273 + */ 1274 + enum xdg_toplevel_resize_edge { 1275 + XDG_TOPLEVEL_RESIZE_EDGE_NONE = 0, 1276 + XDG_TOPLEVEL_RESIZE_EDGE_TOP = 1, 1277 + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM = 2, 1278 + XDG_TOPLEVEL_RESIZE_EDGE_LEFT = 4, 1279 + XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT = 5, 1280 + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT = 6, 1281 + XDG_TOPLEVEL_RESIZE_EDGE_RIGHT = 8, 1282 + XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT = 9, 1283 + XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT = 10, 1284 + }; 1285 + #endif /* XDG_TOPLEVEL_RESIZE_EDGE_ENUM */ 1286 + 1287 + #ifndef XDG_TOPLEVEL_STATE_ENUM 1288 + #define XDG_TOPLEVEL_STATE_ENUM 1289 + /** 1290 + * @ingroup iface_xdg_toplevel 1291 + * types of state on the surface 1292 + * 1293 + * The different state values used on the surface. This is designed for 1294 + * state values like maximized, fullscreen. It is paired with the 1295 + * configure event to ensure that both the client and the compositor 1296 + * setting the state can be synchronized. 1297 + * 1298 + * States set in this way are double-buffered, see wl_surface.commit. 1299 + */ 1300 + enum xdg_toplevel_state { 1301 + /** 1302 + * the surface is maximized 1303 + * the surface is maximized 1304 + * 1305 + * The surface is maximized. The window geometry specified in the 1306 + * configure event must be obeyed by the client, or the 1307 + * xdg_wm_base.invalid_surface_state error is raised. 1308 + * 1309 + * The client should draw without shadow or other decoration 1310 + * outside of the window geometry. 1311 + */ 1312 + XDG_TOPLEVEL_STATE_MAXIMIZED = 1, 1313 + /** 1314 + * the surface is fullscreen 1315 + * the surface is fullscreen 1316 + * 1317 + * The surface is fullscreen. The window geometry specified in 1318 + * the configure event is a maximum; the client cannot resize 1319 + * beyond it. For a surface to cover the whole fullscreened area, 1320 + * the geometry dimensions must be obeyed by the client. For more 1321 + * details, see xdg_toplevel.set_fullscreen. 1322 + */ 1323 + XDG_TOPLEVEL_STATE_FULLSCREEN = 2, 1324 + /** 1325 + * the surface is being resized 1326 + * the surface is being resized 1327 + * 1328 + * The surface is being resized. The window geometry specified in 1329 + * the configure event is a maximum; the client cannot resize 1330 + * beyond it. Clients that have aspect ratio or cell sizing 1331 + * configuration can use a smaller size, however. 1332 + */ 1333 + XDG_TOPLEVEL_STATE_RESIZING = 3, 1334 + /** 1335 + * the surface is now activated 1336 + * the surface is now activated 1337 + * 1338 + * Client window decorations should be painted as if the window 1339 + * is active. Do not assume this means that the window actually has 1340 + * keyboard or pointer focus. 1341 + */ 1342 + XDG_TOPLEVEL_STATE_ACTIVATED = 4, 1343 + /** 1344 + * the surface’s left edge is tiled 1345 + * 1346 + * The window is currently in a tiled layout and the left edge is 1347 + * considered to be adjacent to another part of the tiling grid. 1348 + * 1349 + * The client should draw without shadow or other decoration 1350 + * outside of the window geometry on the left edge. 1351 + * @since 2 1352 + */ 1353 + XDG_TOPLEVEL_STATE_TILED_LEFT = 5, 1354 + /** 1355 + * the surface’s right edge is tiled 1356 + * 1357 + * The window is currently in a tiled layout and the right edge 1358 + * is considered to be adjacent to another part of the tiling grid. 1359 + * 1360 + * The client should draw without shadow or other decoration 1361 + * outside of the window geometry on the right edge. 1362 + * @since 2 1363 + */ 1364 + XDG_TOPLEVEL_STATE_TILED_RIGHT = 6, 1365 + /** 1366 + * the surface’s top edge is tiled 1367 + * 1368 + * The window is currently in a tiled layout and the top edge is 1369 + * considered to be adjacent to another part of the tiling grid. 1370 + * 1371 + * The client should draw without shadow or other decoration 1372 + * outside of the window geometry on the top edge. 1373 + * @since 2 1374 + */ 1375 + XDG_TOPLEVEL_STATE_TILED_TOP = 7, 1376 + /** 1377 + * the surface’s bottom edge is tiled 1378 + * 1379 + * The window is currently in a tiled layout and the bottom edge 1380 + * is considered to be adjacent to another part of the tiling grid. 1381 + * 1382 + * The client should draw without shadow or other decoration 1383 + * outside of the window geometry on the bottom edge. 1384 + * @since 2 1385 + */ 1386 + XDG_TOPLEVEL_STATE_TILED_BOTTOM = 8, 1387 + /** 1388 + * surface repaint is suspended 1389 + * 1390 + * The surface is currently not ordinarily being repainted; for 1391 + * example because its content is occluded by another window, or 1392 + * its outputs are switched off due to screen locking. 1393 + * @since 6 1394 + */ 1395 + XDG_TOPLEVEL_STATE_SUSPENDED = 9, 1396 + /** 1397 + * the surface’s left edge is constrained 1398 + * 1399 + * The left edge of the window is currently constrained, meaning 1400 + * it shouldn't attempt to resize from that edge. It can for 1401 + * example mean it's tiled next to a monitor edge on the 1402 + * constrained side of the window. 1403 + * @since 7 1404 + */ 1405 + XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT = 10, 1406 + /** 1407 + * the surface’s right edge is constrained 1408 + * 1409 + * The right edge of the window is currently constrained, meaning 1410 + * it shouldn't attempt to resize from that edge. It can for 1411 + * example mean it's tiled next to a monitor edge on the 1412 + * constrained side of the window. 1413 + * @since 7 1414 + */ 1415 + XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT = 11, 1416 + /** 1417 + * the surface’s top edge is constrained 1418 + * 1419 + * The top edge of the window is currently constrained, meaning 1420 + * it shouldn't attempt to resize from that edge. It can for 1421 + * example mean it's tiled next to a monitor edge on the 1422 + * constrained side of the window. 1423 + * @since 7 1424 + */ 1425 + XDG_TOPLEVEL_STATE_CONSTRAINED_TOP = 12, 1426 + /** 1427 + * the surface’s bottom edge is constrained 1428 + * 1429 + * The bottom edge of the window is currently constrained, 1430 + * meaning it shouldn't attempt to resize from that edge. It can 1431 + * for example mean it's tiled next to a monitor edge on the 1432 + * constrained side of the window. 1433 + * @since 7 1434 + */ 1435 + XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM = 13, 1436 + }; 1437 + /** 1438 + * @ingroup iface_xdg_toplevel 1439 + */ 1440 + #define XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION 2 1441 + /** 1442 + * @ingroup iface_xdg_toplevel 1443 + */ 1444 + #define XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION 2 1445 + /** 1446 + * @ingroup iface_xdg_toplevel 1447 + */ 1448 + #define XDG_TOPLEVEL_STATE_TILED_TOP_SINCE_VERSION 2 1449 + /** 1450 + * @ingroup iface_xdg_toplevel 1451 + */ 1452 + #define XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION 2 1453 + /** 1454 + * @ingroup iface_xdg_toplevel 1455 + */ 1456 + #define XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION 6 1457 + /** 1458 + * @ingroup iface_xdg_toplevel 1459 + */ 1460 + #define XDG_TOPLEVEL_STATE_CONSTRAINED_LEFT_SINCE_VERSION 7 1461 + /** 1462 + * @ingroup iface_xdg_toplevel 1463 + */ 1464 + #define XDG_TOPLEVEL_STATE_CONSTRAINED_RIGHT_SINCE_VERSION 7 1465 + /** 1466 + * @ingroup iface_xdg_toplevel 1467 + */ 1468 + #define XDG_TOPLEVEL_STATE_CONSTRAINED_TOP_SINCE_VERSION 7 1469 + /** 1470 + * @ingroup iface_xdg_toplevel 1471 + */ 1472 + #define XDG_TOPLEVEL_STATE_CONSTRAINED_BOTTOM_SINCE_VERSION 7 1473 + #endif /* XDG_TOPLEVEL_STATE_ENUM */ 1474 + 1475 + #ifndef XDG_TOPLEVEL_WM_CAPABILITIES_ENUM 1476 + #define XDG_TOPLEVEL_WM_CAPABILITIES_ENUM 1477 + enum xdg_toplevel_wm_capabilities { 1478 + /** 1479 + * show_window_menu is available 1480 + */ 1481 + XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU = 1, 1482 + /** 1483 + * set_maximized and unset_maximized are available 1484 + */ 1485 + XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE = 2, 1486 + /** 1487 + * set_fullscreen and unset_fullscreen are available 1488 + */ 1489 + XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN = 3, 1490 + /** 1491 + * set_minimized is available 1492 + */ 1493 + XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE = 4, 1494 + }; 1495 + #endif /* XDG_TOPLEVEL_WM_CAPABILITIES_ENUM */ 1496 + 1497 + /** 1498 + * @ingroup iface_xdg_toplevel 1499 + * @struct xdg_toplevel_listener 1500 + */ 1501 + struct xdg_toplevel_listener { 1502 + /** 1503 + * suggest a surface change 1504 + * 1505 + * This configure event asks the client to resize its toplevel 1506 + * surface or to change its state. The configured state should not 1507 + * be applied immediately. See xdg_surface.configure for details. 1508 + * 1509 + * The width and height arguments specify a hint to the window 1510 + * about how its surface should be resized in window geometry 1511 + * coordinates. See set_window_geometry. 1512 + * 1513 + * If the width or height arguments are zero, it means the client 1514 + * should decide its own window dimension. This may happen when the 1515 + * compositor needs to configure the state of the surface but 1516 + * doesn't have any information about any previous or expected 1517 + * dimension. 1518 + * 1519 + * The states listed in the event specify how the width/height 1520 + * arguments should be interpreted, and possibly how it should be 1521 + * drawn. 1522 + * 1523 + * Clients must send an ack_configure in response to this event. 1524 + * See xdg_surface.configure and xdg_surface.ack_configure for 1525 + * details. 1526 + */ 1527 + void (*configure)(void *data, 1528 + struct xdg_toplevel *xdg_toplevel, 1529 + int32_t width, 1530 + int32_t height, 1531 + struct wl_array *states); 1532 + /** 1533 + * surface wants to be closed 1534 + * 1535 + * The close event is sent by the compositor when the user wants 1536 + * the surface to be closed. This should be equivalent to the user 1537 + * clicking the close button in client-side decorations, if your 1538 + * application has any. 1539 + * 1540 + * This is only a request that the user intends to close the 1541 + * window. The client may choose to ignore this request, or show a 1542 + * dialog to ask the user to save their data, etc. 1543 + */ 1544 + void (*close)(void *data, 1545 + struct xdg_toplevel *xdg_toplevel); 1546 + /** 1547 + * recommended window geometry bounds 1548 + * 1549 + * The configure_bounds event may be sent prior to a 1550 + * xdg_toplevel.configure event to communicate the bounds a window 1551 + * geometry size is recommended to constrain to. 1552 + * 1553 + * The passed width and height are in surface coordinate space. If 1554 + * width and height are 0, it means bounds is unknown and 1555 + * equivalent to as if no configure_bounds event was ever sent for 1556 + * this surface. 1557 + * 1558 + * The bounds can for example correspond to the size of a monitor 1559 + * excluding any panels or other shell components, so that a 1560 + * surface isn't created in a way that it cannot fit. 1561 + * 1562 + * The bounds may change at any point, and in such a case, a new 1563 + * xdg_toplevel.configure_bounds will be sent, followed by 1564 + * xdg_toplevel.configure and xdg_surface.configure. 1565 + * @since 4 1566 + */ 1567 + void (*configure_bounds)(void *data, 1568 + struct xdg_toplevel *xdg_toplevel, 1569 + int32_t width, 1570 + int32_t height); 1571 + /** 1572 + * compositor capabilities 1573 + * 1574 + * This event advertises the capabilities supported by the 1575 + * compositor. If a capability isn't supported, clients should hide 1576 + * or disable the UI elements that expose this functionality. For 1577 + * instance, if the compositor doesn't advertise support for 1578 + * minimized toplevels, a button triggering the set_minimized 1579 + * request should not be displayed. 1580 + * 1581 + * The compositor will ignore requests it doesn't support. For 1582 + * instance, a compositor which doesn't advertise support for 1583 + * minimized will ignore set_minimized requests. 1584 + * 1585 + * Compositors must send this event once before the first 1586 + * xdg_surface.configure event. When the capabilities change, 1587 + * compositors must send this event again and then send an 1588 + * xdg_surface.configure event. 1589 + * 1590 + * The configured state should not be applied immediately. See 1591 + * xdg_surface.configure for details. 1592 + * 1593 + * The capabilities are sent as an array of 32-bit unsigned 1594 + * integers in native endianness. 1595 + * @param capabilities array of 32-bit capabilities 1596 + * @since 5 1597 + */ 1598 + void (*wm_capabilities)(void *data, 1599 + struct xdg_toplevel *xdg_toplevel, 1600 + struct wl_array *capabilities); 1601 + }; 1602 + 1603 + /** 1604 + * @ingroup iface_xdg_toplevel 1605 + */ 1606 + static inline int 1607 + xdg_toplevel_add_listener(struct xdg_toplevel *xdg_toplevel, 1608 + const struct xdg_toplevel_listener *listener, void *data) 1609 + { 1610 + return wl_proxy_add_listener((struct wl_proxy *) xdg_toplevel, 1611 + (void (**)(void)) listener, data); 1612 + } 1613 + 1614 + #define XDG_TOPLEVEL_DESTROY 0 1615 + #define XDG_TOPLEVEL_SET_PARENT 1 1616 + #define XDG_TOPLEVEL_SET_TITLE 2 1617 + #define XDG_TOPLEVEL_SET_APP_ID 3 1618 + #define XDG_TOPLEVEL_SHOW_WINDOW_MENU 4 1619 + #define XDG_TOPLEVEL_MOVE 5 1620 + #define XDG_TOPLEVEL_RESIZE 6 1621 + #define XDG_TOPLEVEL_SET_MAX_SIZE 7 1622 + #define XDG_TOPLEVEL_SET_MIN_SIZE 8 1623 + #define XDG_TOPLEVEL_SET_MAXIMIZED 9 1624 + #define XDG_TOPLEVEL_UNSET_MAXIMIZED 10 1625 + #define XDG_TOPLEVEL_SET_FULLSCREEN 11 1626 + #define XDG_TOPLEVEL_UNSET_FULLSCREEN 12 1627 + #define XDG_TOPLEVEL_SET_MINIMIZED 13 1628 + 1629 + /** 1630 + * @ingroup iface_xdg_toplevel 1631 + */ 1632 + #define XDG_TOPLEVEL_CONFIGURE_SINCE_VERSION 1 1633 + /** 1634 + * @ingroup iface_xdg_toplevel 1635 + */ 1636 + #define XDG_TOPLEVEL_CLOSE_SINCE_VERSION 1 1637 + /** 1638 + * @ingroup iface_xdg_toplevel 1639 + */ 1640 + #define XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION 4 1641 + /** 1642 + * @ingroup iface_xdg_toplevel 1643 + */ 1644 + #define XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION 5 1645 + 1646 + /** 1647 + * @ingroup iface_xdg_toplevel 1648 + */ 1649 + #define XDG_TOPLEVEL_DESTROY_SINCE_VERSION 1 1650 + /** 1651 + * @ingroup iface_xdg_toplevel 1652 + */ 1653 + #define XDG_TOPLEVEL_SET_PARENT_SINCE_VERSION 1 1654 + /** 1655 + * @ingroup iface_xdg_toplevel 1656 + */ 1657 + #define XDG_TOPLEVEL_SET_TITLE_SINCE_VERSION 1 1658 + /** 1659 + * @ingroup iface_xdg_toplevel 1660 + */ 1661 + #define XDG_TOPLEVEL_SET_APP_ID_SINCE_VERSION 1 1662 + /** 1663 + * @ingroup iface_xdg_toplevel 1664 + */ 1665 + #define XDG_TOPLEVEL_SHOW_WINDOW_MENU_SINCE_VERSION 1 1666 + /** 1667 + * @ingroup iface_xdg_toplevel 1668 + */ 1669 + #define XDG_TOPLEVEL_MOVE_SINCE_VERSION 1 1670 + /** 1671 + * @ingroup iface_xdg_toplevel 1672 + */ 1673 + #define XDG_TOPLEVEL_RESIZE_SINCE_VERSION 1 1674 + /** 1675 + * @ingroup iface_xdg_toplevel 1676 + */ 1677 + #define XDG_TOPLEVEL_SET_MAX_SIZE_SINCE_VERSION 1 1678 + /** 1679 + * @ingroup iface_xdg_toplevel 1680 + */ 1681 + #define XDG_TOPLEVEL_SET_MIN_SIZE_SINCE_VERSION 1 1682 + /** 1683 + * @ingroup iface_xdg_toplevel 1684 + */ 1685 + #define XDG_TOPLEVEL_SET_MAXIMIZED_SINCE_VERSION 1 1686 + /** 1687 + * @ingroup iface_xdg_toplevel 1688 + */ 1689 + #define XDG_TOPLEVEL_UNSET_MAXIMIZED_SINCE_VERSION 1 1690 + /** 1691 + * @ingroup iface_xdg_toplevel 1692 + */ 1693 + #define XDG_TOPLEVEL_SET_FULLSCREEN_SINCE_VERSION 1 1694 + /** 1695 + * @ingroup iface_xdg_toplevel 1696 + */ 1697 + #define XDG_TOPLEVEL_UNSET_FULLSCREEN_SINCE_VERSION 1 1698 + /** 1699 + * @ingroup iface_xdg_toplevel 1700 + */ 1701 + #define XDG_TOPLEVEL_SET_MINIMIZED_SINCE_VERSION 1 1702 + 1703 + /** @ingroup iface_xdg_toplevel */ 1704 + static inline void 1705 + xdg_toplevel_set_user_data(struct xdg_toplevel *xdg_toplevel, void *user_data) 1706 + { 1707 + wl_proxy_set_user_data((struct wl_proxy *) xdg_toplevel, user_data); 1708 + } 1709 + 1710 + /** @ingroup iface_xdg_toplevel */ 1711 + static inline void * 1712 + xdg_toplevel_get_user_data(struct xdg_toplevel *xdg_toplevel) 1713 + { 1714 + return wl_proxy_get_user_data((struct wl_proxy *) xdg_toplevel); 1715 + } 1716 + 1717 + static inline uint32_t 1718 + xdg_toplevel_get_version(struct xdg_toplevel *xdg_toplevel) 1719 + { 1720 + return wl_proxy_get_version((struct wl_proxy *) xdg_toplevel); 1721 + } 1722 + 1723 + /** 1724 + * @ingroup iface_xdg_toplevel 1725 + * 1726 + * This request destroys the role surface and unmaps the surface; 1727 + * see "Unmapping" behavior in interface section for details. 1728 + */ 1729 + static inline void 1730 + xdg_toplevel_destroy(struct xdg_toplevel *xdg_toplevel) 1731 + { 1732 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1733 + XDG_TOPLEVEL_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), WL_MARSHAL_FLAG_DESTROY); 1734 + } 1735 + 1736 + /** 1737 + * @ingroup iface_xdg_toplevel 1738 + * 1739 + * Set the "parent" of this surface. This surface should be stacked 1740 + * above the parent surface and all other ancestor surfaces. 1741 + * 1742 + * Parent surfaces should be set on dialogs, toolboxes, or other 1743 + * "auxiliary" surfaces, so that the parent is raised when the dialog 1744 + * is raised. 1745 + * 1746 + * Setting a null parent for a child surface unsets its parent. Setting 1747 + * a null parent for a surface which currently has no parent is a no-op. 1748 + * 1749 + * Only mapped surfaces can have child surfaces. Setting a parent which 1750 + * is not mapped is equivalent to setting a null parent. If a surface 1751 + * becomes unmapped, its children's parent is set to the parent of 1752 + * the now-unmapped surface. If the now-unmapped surface has no parent, 1753 + * its children's parent is unset. If the now-unmapped surface becomes 1754 + * mapped again, its parent-child relationship is not restored. 1755 + * 1756 + * The parent toplevel must not be one of the child toplevel's 1757 + * descendants, and the parent must be different from the child toplevel, 1758 + * otherwise the invalid_parent protocol error is raised. 1759 + */ 1760 + static inline void 1761 + xdg_toplevel_set_parent(struct xdg_toplevel *xdg_toplevel, struct xdg_toplevel *parent) 1762 + { 1763 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1764 + XDG_TOPLEVEL_SET_PARENT, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, parent); 1765 + } 1766 + 1767 + /** 1768 + * @ingroup iface_xdg_toplevel 1769 + * 1770 + * Set a short title for the surface. 1771 + * 1772 + * This string may be used to identify the surface in a task bar, 1773 + * window list, or other user interface elements provided by the 1774 + * compositor. 1775 + * 1776 + * The string must be encoded in UTF-8. 1777 + */ 1778 + static inline void 1779 + xdg_toplevel_set_title(struct xdg_toplevel *xdg_toplevel, const char *title) 1780 + { 1781 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1782 + XDG_TOPLEVEL_SET_TITLE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, title); 1783 + } 1784 + 1785 + /** 1786 + * @ingroup iface_xdg_toplevel 1787 + * 1788 + * Set an application identifier for the surface. 1789 + * 1790 + * The app ID identifies the general class of applications to which 1791 + * the surface belongs. The compositor can use this to group multiple 1792 + * surfaces together, or to determine how to launch a new application. 1793 + * 1794 + * For D-Bus activatable applications, the app ID is used as the D-Bus 1795 + * service name. 1796 + * 1797 + * The compositor shell will try to group application surfaces together 1798 + * by their app ID. As a best practice, it is suggested to select app 1799 + * ID's that match the basename of the application's .desktop file. 1800 + * For example, "org.freedesktop.FooViewer" where the .desktop file is 1801 + * "org.freedesktop.FooViewer.desktop". 1802 + * 1803 + * Like other properties, a set_app_id request can be sent after the 1804 + * xdg_toplevel has been mapped to update the property. 1805 + * 1806 + * See the desktop-entry specification [0] for more details on 1807 + * application identifiers and how they relate to well-known D-Bus 1808 + * names and .desktop files. 1809 + * 1810 + * [0] https://standards.freedesktop.org/desktop-entry-spec/ 1811 + */ 1812 + static inline void 1813 + xdg_toplevel_set_app_id(struct xdg_toplevel *xdg_toplevel, const char *app_id) 1814 + { 1815 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1816 + XDG_TOPLEVEL_SET_APP_ID, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, app_id); 1817 + } 1818 + 1819 + /** 1820 + * @ingroup iface_xdg_toplevel 1821 + * 1822 + * Clients implementing client-side decorations might want to show 1823 + * a context menu when right-clicking on the decorations, giving the 1824 + * user a menu that they can use to maximize or minimize the window. 1825 + * 1826 + * This request asks the compositor to pop up such a window menu at 1827 + * the given position, relative to the local surface coordinates of 1828 + * the parent surface. There are no guarantees as to what menu items 1829 + * the window menu contains, or even if a window menu will be drawn 1830 + * at all. 1831 + * 1832 + * This request must be used in response to some sort of user action 1833 + * like a button press, key press, or touch down event. 1834 + */ 1835 + static inline void 1836 + xdg_toplevel_show_window_menu(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, int32_t x, int32_t y) 1837 + { 1838 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1839 + XDG_TOPLEVEL_SHOW_WINDOW_MENU, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, x, y); 1840 + } 1841 + 1842 + /** 1843 + * @ingroup iface_xdg_toplevel 1844 + * 1845 + * Start an interactive, user-driven move of the surface. 1846 + * 1847 + * This request must be used in response to some sort of user action 1848 + * like a button press, key press, or touch down event. The passed 1849 + * serial is used to determine the type of interactive move (touch, 1850 + * pointer, etc). 1851 + * 1852 + * The server may ignore move requests depending on the state of 1853 + * the surface (e.g. fullscreen or maximized), or if the passed serial 1854 + * is no longer valid. 1855 + * 1856 + * If triggered, the surface will lose the focus of the device 1857 + * (wl_pointer, wl_touch, etc) used for the move. It is up to the 1858 + * compositor to visually indicate that the move is taking place, such as 1859 + * updating a pointer cursor, during the move. There is no guarantee 1860 + * that the device focus will return when the move is completed. 1861 + */ 1862 + static inline void 1863 + xdg_toplevel_move(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial) 1864 + { 1865 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1866 + XDG_TOPLEVEL_MOVE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial); 1867 + } 1868 + 1869 + /** 1870 + * @ingroup iface_xdg_toplevel 1871 + * 1872 + * Start a user-driven, interactive resize of the surface. 1873 + * 1874 + * This request must be used in response to some sort of user action 1875 + * like a button press, key press, or touch down event. The passed 1876 + * serial is used to determine the type of interactive resize (touch, 1877 + * pointer, etc). 1878 + * 1879 + * The server may ignore resize requests depending on the state of 1880 + * the surface (e.g. fullscreen or maximized). 1881 + * 1882 + * If triggered, the client will receive configure events with the 1883 + * "resize" state enum value and the expected sizes. See the "resize" 1884 + * enum value for more details about what is required. The client 1885 + * must also acknowledge configure events using "ack_configure". After 1886 + * the resize is completed, the client will receive another "configure" 1887 + * event without the resize state. 1888 + * 1889 + * If triggered, the surface also will lose the focus of the device 1890 + * (wl_pointer, wl_touch, etc) used for the resize. It is up to the 1891 + * compositor to visually indicate that the resize is taking place, 1892 + * such as updating a pointer cursor, during the resize. There is no 1893 + * guarantee that the device focus will return when the resize is 1894 + * completed. 1895 + * 1896 + * The edges parameter specifies how the surface should be resized, and 1897 + * is one of the values of the resize_edge enum. Values not matching 1898 + * a variant of the enum will cause the invalid_resize_edge protocol error. 1899 + * The compositor may use this information to update the surface position 1900 + * for example when dragging the top left corner. The compositor may also 1901 + * use this information to adapt its behavior, e.g. choose an appropriate 1902 + * cursor image. 1903 + */ 1904 + static inline void 1905 + xdg_toplevel_resize(struct xdg_toplevel *xdg_toplevel, struct wl_seat *seat, uint32_t serial, uint32_t edges) 1906 + { 1907 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1908 + XDG_TOPLEVEL_RESIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, seat, serial, edges); 1909 + } 1910 + 1911 + /** 1912 + * @ingroup iface_xdg_toplevel 1913 + * 1914 + * Set a maximum size for the window. 1915 + * 1916 + * The client can specify a maximum size so that the compositor does 1917 + * not try to configure the window beyond this size. 1918 + * 1919 + * The width and height arguments are in window geometry coordinates. 1920 + * See xdg_surface.set_window_geometry. 1921 + * 1922 + * Values set in this way are double-buffered, see wl_surface.commit. 1923 + * 1924 + * The compositor can use this information to allow or disallow 1925 + * different states like maximize or fullscreen and draw accurate 1926 + * animations. 1927 + * 1928 + * Similarly, a tiling window manager may use this information to 1929 + * place and resize client windows in a more effective way. 1930 + * 1931 + * The client should not rely on the compositor to obey the maximum 1932 + * size. The compositor may decide to ignore the values set by the 1933 + * client and request a larger size. 1934 + * 1935 + * If never set, or a value of zero in the request, means that the 1936 + * client has no expected maximum size in the given dimension. 1937 + * As a result, a client wishing to reset the maximum size 1938 + * to an unspecified state can use zero for width and height in the 1939 + * request. 1940 + * 1941 + * Requesting a maximum size to be smaller than the minimum size of 1942 + * a surface is illegal and will result in an invalid_size error. 1943 + * 1944 + * The width and height must be greater than or equal to zero. Using 1945 + * strictly negative values for width or height will result in a 1946 + * invalid_size error. 1947 + */ 1948 + static inline void 1949 + xdg_toplevel_set_max_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) 1950 + { 1951 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1952 + XDG_TOPLEVEL_SET_MAX_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); 1953 + } 1954 + 1955 + /** 1956 + * @ingroup iface_xdg_toplevel 1957 + * 1958 + * Set a minimum size for the window. 1959 + * 1960 + * The client can specify a minimum size so that the compositor does 1961 + * not try to configure the window below this size. 1962 + * 1963 + * The width and height arguments are in window geometry coordinates. 1964 + * See xdg_surface.set_window_geometry. 1965 + * 1966 + * Values set in this way are double-buffered, see wl_surface.commit. 1967 + * 1968 + * The compositor can use this information to allow or disallow 1969 + * different states like maximize or fullscreen and draw accurate 1970 + * animations. 1971 + * 1972 + * Similarly, a tiling window manager may use this information to 1973 + * place and resize client windows in a more effective way. 1974 + * 1975 + * The client should not rely on the compositor to obey the minimum 1976 + * size. The compositor may decide to ignore the values set by the 1977 + * client and request a smaller size. 1978 + * 1979 + * If never set, or a value of zero in the request, means that the 1980 + * client has no expected minimum size in the given dimension. 1981 + * As a result, a client wishing to reset the minimum size 1982 + * to an unspecified state can use zero for width and height in the 1983 + * request. 1984 + * 1985 + * Requesting a minimum size to be larger than the maximum size of 1986 + * a surface is illegal and will result in an invalid_size error. 1987 + * 1988 + * The width and height must be greater than or equal to zero. Using 1989 + * strictly negative values for width and height will result in a 1990 + * invalid_size error. 1991 + */ 1992 + static inline void 1993 + xdg_toplevel_set_min_size(struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height) 1994 + { 1995 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 1996 + XDG_TOPLEVEL_SET_MIN_SIZE, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, width, height); 1997 + } 1998 + 1999 + /** 2000 + * @ingroup iface_xdg_toplevel 2001 + * 2002 + * Maximize the surface. 2003 + * 2004 + * After requesting that the surface should be maximized, the compositor 2005 + * will respond by emitting a configure event. Whether this configure 2006 + * actually sets the window maximized is subject to compositor policies. 2007 + * The client must then update its content, drawing in the configured 2008 + * state. The client must also acknowledge the configure when committing 2009 + * the new content (see ack_configure). 2010 + * 2011 + * It is up to the compositor to decide how and where to maximize the 2012 + * surface, for example which output and what region of the screen should 2013 + * be used. 2014 + * 2015 + * If the surface was already maximized, the compositor will still emit 2016 + * a configure event with the "maximized" state. 2017 + * 2018 + * If the surface is in a fullscreen state, this request has no direct 2019 + * effect. It may alter the state the surface is returned to when 2020 + * unmaximized unless overridden by the compositor. 2021 + */ 2022 + static inline void 2023 + xdg_toplevel_set_maximized(struct xdg_toplevel *xdg_toplevel) 2024 + { 2025 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 2026 + XDG_TOPLEVEL_SET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); 2027 + } 2028 + 2029 + /** 2030 + * @ingroup iface_xdg_toplevel 2031 + * 2032 + * Unmaximize the surface. 2033 + * 2034 + * After requesting that the surface should be unmaximized, the compositor 2035 + * will respond by emitting a configure event. Whether this actually 2036 + * un-maximizes the window is subject to compositor policies. 2037 + * If available and applicable, the compositor will include the window 2038 + * geometry dimensions the window had prior to being maximized in the 2039 + * configure event. The client must then update its content, drawing it in 2040 + * the configured state. The client must also acknowledge the configure 2041 + * when committing the new content (see ack_configure). 2042 + * 2043 + * It is up to the compositor to position the surface after it was 2044 + * unmaximized; usually the position the surface had before maximizing, if 2045 + * applicable. 2046 + * 2047 + * If the surface was already not maximized, the compositor will still 2048 + * emit a configure event without the "maximized" state. 2049 + * 2050 + * If the surface is in a fullscreen state, this request has no direct 2051 + * effect. It may alter the state the surface is returned to when 2052 + * unmaximized unless overridden by the compositor. 2053 + */ 2054 + static inline void 2055 + xdg_toplevel_unset_maximized(struct xdg_toplevel *xdg_toplevel) 2056 + { 2057 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 2058 + XDG_TOPLEVEL_UNSET_MAXIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); 2059 + } 2060 + 2061 + /** 2062 + * @ingroup iface_xdg_toplevel 2063 + * 2064 + * Make the surface fullscreen. 2065 + * 2066 + * After requesting that the surface should be fullscreened, the 2067 + * compositor will respond by emitting a configure event. Whether the 2068 + * client is actually put into a fullscreen state is subject to compositor 2069 + * policies. The client must also acknowledge the configure when 2070 + * committing the new content (see ack_configure). 2071 + * 2072 + * The output passed by the request indicates the client's preference as 2073 + * to which display it should be set fullscreen on. If this value is NULL, 2074 + * it's up to the compositor to choose which display will be used to map 2075 + * this surface. 2076 + * 2077 + * If the surface doesn't cover the whole output, the compositor will 2078 + * position the surface in the center of the output and compensate with 2079 + * with border fill covering the rest of the output. The content of the 2080 + * border fill is undefined, but should be assumed to be in some way that 2081 + * attempts to blend into the surrounding area (e.g. solid black). 2082 + * 2083 + * If the fullscreened surface is not opaque, the compositor must make 2084 + * sure that other screen content not part of the same surface tree (made 2085 + * up of subsurfaces, popups or similarly coupled surfaces) are not 2086 + * visible below the fullscreened surface. 2087 + */ 2088 + static inline void 2089 + xdg_toplevel_set_fullscreen(struct xdg_toplevel *xdg_toplevel, struct wl_output *output) 2090 + { 2091 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 2092 + XDG_TOPLEVEL_SET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0, output); 2093 + } 2094 + 2095 + /** 2096 + * @ingroup iface_xdg_toplevel 2097 + * 2098 + * Make the surface no longer fullscreen. 2099 + * 2100 + * After requesting that the surface should be unfullscreened, the 2101 + * compositor will respond by emitting a configure event. 2102 + * Whether this actually removes the fullscreen state of the client is 2103 + * subject to compositor policies. 2104 + * 2105 + * Making a surface unfullscreen sets states for the surface based on the following: 2106 + * * the state(s) it may have had before becoming fullscreen 2107 + * * any state(s) decided by the compositor 2108 + * * any state(s) requested by the client while the surface was fullscreen 2109 + * 2110 + * The compositor may include the previous window geometry dimensions in 2111 + * the configure event, if applicable. 2112 + * 2113 + * The client must also acknowledge the configure when committing the new 2114 + * content (see ack_configure). 2115 + */ 2116 + static inline void 2117 + xdg_toplevel_unset_fullscreen(struct xdg_toplevel *xdg_toplevel) 2118 + { 2119 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 2120 + XDG_TOPLEVEL_UNSET_FULLSCREEN, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); 2121 + } 2122 + 2123 + /** 2124 + * @ingroup iface_xdg_toplevel 2125 + * 2126 + * Request that the compositor minimize your surface. There is no 2127 + * way to know if the surface is currently minimized, nor is there 2128 + * any way to unset minimization on this surface. 2129 + * 2130 + * If you are looking to throttle redrawing when minimized, please 2131 + * instead use the wl_surface.frame event for this, as this will 2132 + * also work with live previews on windows in Alt-Tab, Expose or 2133 + * similar compositor features. 2134 + */ 2135 + static inline void 2136 + xdg_toplevel_set_minimized(struct xdg_toplevel *xdg_toplevel) 2137 + { 2138 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_toplevel, 2139 + XDG_TOPLEVEL_SET_MINIMIZED, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_toplevel), 0); 2140 + } 2141 + 2142 + #ifndef XDG_POPUP_ERROR_ENUM 2143 + #define XDG_POPUP_ERROR_ENUM 2144 + enum xdg_popup_error { 2145 + /** 2146 + * tried to grab after being mapped 2147 + */ 2148 + XDG_POPUP_ERROR_INVALID_GRAB = 0, 2149 + }; 2150 + #endif /* XDG_POPUP_ERROR_ENUM */ 2151 + 2152 + /** 2153 + * @ingroup iface_xdg_popup 2154 + * @struct xdg_popup_listener 2155 + */ 2156 + struct xdg_popup_listener { 2157 + /** 2158 + * configure the popup surface 2159 + * 2160 + * This event asks the popup surface to configure itself given 2161 + * the configuration. The configured state should not be applied 2162 + * immediately. See xdg_surface.configure for details. 2163 + * 2164 + * The x and y arguments represent the position the popup was 2165 + * placed at given the xdg_positioner rule, relative to the upper 2166 + * left corner of the window geometry of the parent surface. 2167 + * 2168 + * For version 2 or older, the configure event for an xdg_popup is 2169 + * only ever sent once for the initial configuration. Starting with 2170 + * version 3, it may be sent again if the popup is setup with an 2171 + * xdg_positioner with set_reactive requested, or in response to 2172 + * xdg_popup.reposition requests. 2173 + * @param x x position relative to parent surface window geometry 2174 + * @param y y position relative to parent surface window geometry 2175 + * @param width window geometry width 2176 + * @param height window geometry height 2177 + */ 2178 + void (*configure)(void *data, 2179 + struct xdg_popup *xdg_popup, 2180 + int32_t x, 2181 + int32_t y, 2182 + int32_t width, 2183 + int32_t height); 2184 + /** 2185 + * popup interaction is done 2186 + * 2187 + * The popup_done event is sent out when a popup is dismissed by 2188 + * the compositor. The client should destroy the xdg_popup object 2189 + * at this point. 2190 + */ 2191 + void (*popup_done)(void *data, 2192 + struct xdg_popup *xdg_popup); 2193 + /** 2194 + * signal the completion of a repositioned request 2195 + * 2196 + * The repositioned event is sent as part of a popup 2197 + * configuration sequence, together with xdg_popup.configure and 2198 + * lastly xdg_surface.configure to notify the completion of a 2199 + * reposition request. 2200 + * 2201 + * The repositioned event is to notify about the completion of a 2202 + * xdg_popup.reposition request. The token argument is the token 2203 + * passed in the xdg_popup.reposition request. 2204 + * 2205 + * Immediately after this event is emitted, xdg_popup.configure and 2206 + * xdg_surface.configure will be sent with the updated size and 2207 + * position, as well as a new configure serial. 2208 + * 2209 + * The client should optionally update the content of the popup, 2210 + * but must acknowledge the new popup configuration for the new 2211 + * position to take effect. See xdg_surface.ack_configure for 2212 + * details. 2213 + * @param token reposition request token 2214 + * @since 3 2215 + */ 2216 + void (*repositioned)(void *data, 2217 + struct xdg_popup *xdg_popup, 2218 + uint32_t token); 2219 + }; 2220 + 2221 + /** 2222 + * @ingroup iface_xdg_popup 2223 + */ 2224 + static inline int 2225 + xdg_popup_add_listener(struct xdg_popup *xdg_popup, 2226 + const struct xdg_popup_listener *listener, void *data) 2227 + { 2228 + return wl_proxy_add_listener((struct wl_proxy *) xdg_popup, 2229 + (void (**)(void)) listener, data); 2230 + } 2231 + 2232 + #define XDG_POPUP_DESTROY 0 2233 + #define XDG_POPUP_GRAB 1 2234 + #define XDG_POPUP_REPOSITION 2 2235 + 2236 + /** 2237 + * @ingroup iface_xdg_popup 2238 + */ 2239 + #define XDG_POPUP_CONFIGURE_SINCE_VERSION 1 2240 + /** 2241 + * @ingroup iface_xdg_popup 2242 + */ 2243 + #define XDG_POPUP_POPUP_DONE_SINCE_VERSION 1 2244 + /** 2245 + * @ingroup iface_xdg_popup 2246 + */ 2247 + #define XDG_POPUP_REPOSITIONED_SINCE_VERSION 3 2248 + 2249 + /** 2250 + * @ingroup iface_xdg_popup 2251 + */ 2252 + #define XDG_POPUP_DESTROY_SINCE_VERSION 1 2253 + /** 2254 + * @ingroup iface_xdg_popup 2255 + */ 2256 + #define XDG_POPUP_GRAB_SINCE_VERSION 1 2257 + /** 2258 + * @ingroup iface_xdg_popup 2259 + */ 2260 + #define XDG_POPUP_REPOSITION_SINCE_VERSION 3 2261 + 2262 + /** @ingroup iface_xdg_popup */ 2263 + static inline void 2264 + xdg_popup_set_user_data(struct xdg_popup *xdg_popup, void *user_data) 2265 + { 2266 + wl_proxy_set_user_data((struct wl_proxy *) xdg_popup, user_data); 2267 + } 2268 + 2269 + /** @ingroup iface_xdg_popup */ 2270 + static inline void * 2271 + xdg_popup_get_user_data(struct xdg_popup *xdg_popup) 2272 + { 2273 + return wl_proxy_get_user_data((struct wl_proxy *) xdg_popup); 2274 + } 2275 + 2276 + static inline uint32_t 2277 + xdg_popup_get_version(struct xdg_popup *xdg_popup) 2278 + { 2279 + return wl_proxy_get_version((struct wl_proxy *) xdg_popup); 2280 + } 2281 + 2282 + /** 2283 + * @ingroup iface_xdg_popup 2284 + * 2285 + * This destroys the popup. Explicitly destroying the xdg_popup 2286 + * object will also dismiss the popup, and unmap the surface. 2287 + * 2288 + * If this xdg_popup is not the "topmost" popup, the 2289 + * xdg_wm_base.not_the_topmost_popup protocol error will be sent. 2290 + */ 2291 + static inline void 2292 + xdg_popup_destroy(struct xdg_popup *xdg_popup) 2293 + { 2294 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, 2295 + XDG_POPUP_DESTROY, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), WL_MARSHAL_FLAG_DESTROY); 2296 + } 2297 + 2298 + /** 2299 + * @ingroup iface_xdg_popup 2300 + * 2301 + * This request makes the created popup take an explicit grab. An explicit 2302 + * grab will be dismissed when the user dismisses the popup, or when the 2303 + * client destroys the xdg_popup. This can be done by the user clicking 2304 + * outside the surface, using the keyboard, or even locking the screen 2305 + * through closing the lid or a timeout. 2306 + * 2307 + * If the compositor denies the grab, the popup will be immediately 2308 + * dismissed. 2309 + * 2310 + * This request must be used in response to some sort of user action like a 2311 + * button press, key press, or touch down event. The serial number of the 2312 + * event should be passed as 'serial'. 2313 + * 2314 + * The parent of a grabbing popup must either be an xdg_toplevel surface or 2315 + * another xdg_popup with an explicit grab. If the parent is another 2316 + * xdg_popup it means that the popups are nested, with this popup now being 2317 + * the topmost popup. 2318 + * 2319 + * Nested popups must be destroyed in the reverse order they were created 2320 + * in, e.g. the only popup you are allowed to destroy at all times is the 2321 + * topmost one. 2322 + * 2323 + * When compositors choose to dismiss a popup, they may dismiss every 2324 + * nested grabbing popup as well. When a compositor dismisses popups, it 2325 + * will follow the same dismissing order as required from the client. 2326 + * 2327 + * If the topmost grabbing popup is destroyed, the grab will be returned to 2328 + * the parent of the popup, if that parent previously had an explicit grab. 2329 + * 2330 + * If the parent is a grabbing popup which has already been dismissed, this 2331 + * popup will be immediately dismissed. If the parent is a popup that did 2332 + * not take an explicit grab, an error will be raised. 2333 + * 2334 + * During a popup grab, the client owning the grab will receive pointer 2335 + * and touch events for all their surfaces as normal (similar to an 2336 + * "owner-events" grab in X11 parlance), while the top most grabbing popup 2337 + * will always have keyboard focus. 2338 + */ 2339 + static inline void 2340 + xdg_popup_grab(struct xdg_popup *xdg_popup, struct wl_seat *seat, uint32_t serial) 2341 + { 2342 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, 2343 + XDG_POPUP_GRAB, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, seat, serial); 2344 + } 2345 + 2346 + /** 2347 + * @ingroup iface_xdg_popup 2348 + * 2349 + * Reposition an already-mapped popup. The popup will be placed given the 2350 + * details in the passed xdg_positioner object, and a 2351 + * xdg_popup.repositioned followed by xdg_popup.configure and 2352 + * xdg_surface.configure will be emitted in response. Any parameters set 2353 + * by the previous positioner will be discarded. 2354 + * 2355 + * The passed token will be sent in the corresponding 2356 + * xdg_popup.repositioned event. The new popup position will not take 2357 + * effect until the corresponding configure event is acknowledged by the 2358 + * client. See xdg_popup.repositioned for details. The token itself is 2359 + * opaque, and has no other special meaning. 2360 + * 2361 + * If multiple reposition requests are sent, the compositor may skip all 2362 + * but the last one. 2363 + * 2364 + * If the popup is repositioned in response to a configure event for its 2365 + * parent, the client should send an xdg_positioner.set_parent_configure 2366 + * and possibly an xdg_positioner.set_parent_size request to allow the 2367 + * compositor to properly constrain the popup. 2368 + * 2369 + * If the popup is repositioned together with a parent that is being 2370 + * resized, but not in response to a configure event, the client should 2371 + * send an xdg_positioner.set_parent_size request. 2372 + */ 2373 + static inline void 2374 + xdg_popup_reposition(struct xdg_popup *xdg_popup, struct xdg_positioner *positioner, uint32_t token) 2375 + { 2376 + wl_proxy_marshal_flags((struct wl_proxy *) xdg_popup, 2377 + XDG_POPUP_REPOSITION, NULL, wl_proxy_get_version((struct wl_proxy *) xdg_popup), 0, positioner, token); 2378 + } 2379 + 2380 + #ifdef __cplusplus 2381 + } 2382 + #endif 2383 + 2384 + #endif
+139
internal/screenshot/portal.go
··· 1 + package screenshot 2 + 3 + import ( 4 + "fmt" 5 + "net/url" 6 + "strings" 7 + "time" 8 + 9 + "github.com/godbus/dbus/v5" 10 + ) 11 + 12 + const ( 13 + portalService = "org.freedesktop.portal.Desktop" 14 + portalPath = "/org/freedesktop/portal/desktop" 15 + portalInterface = "org.freedesktop.portal.Screenshot" 16 + requestIface = "org.freedesktop.portal.Request" 17 + ) 18 + 19 + // CaptureScreenshot triggers the XDG Desktop Portal screenshot dialog. 20 + // Returns the path to the captured image file. 21 + // The interactive parameter controls whether the user can select a region (true) 22 + // or if it captures the entire screen immediately (false). 23 + func CaptureScreenshot(interactive bool) (string, error) { 24 + conn, err := dbus.ConnectSessionBus() 25 + if err != nil { 26 + return "", fmt.Errorf("failed to connect to session bus: %w", err) 27 + } 28 + defer conn.Close() 29 + 30 + // Generate unique handle token 31 + token := fmt.Sprintf("blup_%d", time.Now().UnixNano()) 32 + 33 + // Build the expected request path 34 + // The path format is: /org/freedesktop/portal/desktop/request/{sender}/{token} 35 + // where sender is the unique bus name with ':' replaced by '_' and '.' replaced by '_' 36 + sender := conn.Names()[0] 37 + sender = strings.ReplaceAll(sender[1:], ".", "_") // Remove leading ':' and replace dots 38 + handlePath := dbus.ObjectPath(fmt.Sprintf("/org/freedesktop/portal/desktop/request/%s/%s", sender, token)) 39 + 40 + // Subscribe to Response signal before making request 41 + ch := make(chan *dbus.Signal, 10) 42 + conn.Signal(ch) 43 + 44 + // Use AddMatchSignal for more reliable matching 45 + if err := conn.AddMatchSignal( 46 + dbus.WithMatchObjectPath(handlePath), 47 + dbus.WithMatchInterface(requestIface), 48 + dbus.WithMatchMember("Response"), 49 + ); err != nil { 50 + return "", fmt.Errorf("failed to add signal match: %w", err) 51 + } 52 + defer conn.RemoveMatchSignal( 53 + dbus.WithMatchObjectPath(handlePath), 54 + dbus.WithMatchInterface(requestIface), 55 + dbus.WithMatchMember("Response"), 56 + ) 57 + 58 + // Call Screenshot method 59 + obj := conn.Object(portalService, dbus.ObjectPath(portalPath)) 60 + options := map[string]dbus.Variant{ 61 + "handle_token": dbus.MakeVariant(token), 62 + "interactive": dbus.MakeVariant(interactive), 63 + } 64 + 65 + var requestPath dbus.ObjectPath 66 + err = obj.Call(portalInterface+".Screenshot", 0, "", options).Store(&requestPath) 67 + if err != nil { 68 + return "", fmt.Errorf("failed to call Screenshot: %w", err) 69 + } 70 + 71 + // Wait for Response signal with timeout 72 + timeout := time.After(120 * time.Second) 73 + for { 74 + select { 75 + case signal := <-ch: 76 + // Check if this is the signal we're waiting for 77 + if signal.Path != handlePath { 78 + continue 79 + } 80 + if signal.Name != requestIface+".Response" { 81 + continue 82 + } 83 + 84 + if len(signal.Body) < 2 { 85 + return "", fmt.Errorf("invalid response signal: body has %d elements, expected at least 2", len(signal.Body)) 86 + } 87 + 88 + responseCode, ok := signal.Body[0].(uint32) 89 + if !ok { 90 + return "", fmt.Errorf("invalid response code type: %T", signal.Body[0]) 91 + } 92 + 93 + if responseCode != 0 { 94 + if responseCode == 1 { 95 + return "", fmt.Errorf("screenshot cancelled by user") 96 + } 97 + return "", fmt.Errorf("screenshot failed with code: %d", responseCode) 98 + } 99 + 100 + results, ok := signal.Body[1].(map[string]dbus.Variant) 101 + if !ok { 102 + return "", fmt.Errorf("invalid results type: %T", signal.Body[1]) 103 + } 104 + 105 + uriVariant, ok := results["uri"] 106 + if !ok { 107 + return "", fmt.Errorf("no uri in response") 108 + } 109 + 110 + uri, ok := uriVariant.Value().(string) 111 + if !ok { 112 + return "", fmt.Errorf("invalid uri type") 113 + } 114 + 115 + // Convert file:// URI to path 116 + filePath, err := uriToPath(uri) 117 + if err != nil { 118 + return "", fmt.Errorf("failed to parse URI: %w", err) 119 + } 120 + 121 + return filePath, nil 122 + 123 + case <-timeout: 124 + return "", fmt.Errorf("timeout waiting for screenshot response") 125 + } 126 + } 127 + } 128 + 129 + // uriToPath converts a file:// URI to a filesystem path 130 + func uriToPath(uri string) (string, error) { 131 + u, err := url.Parse(uri) 132 + if err != nil { 133 + return "", err 134 + } 135 + if u.Scheme != "file" { 136 + return "", fmt.Errorf("expected file:// URI, got %s", u.Scheme) 137 + } 138 + return u.Path, nil 139 + }
+62 -63
internal/util/util.go
··· 1 1 package util 2 2 3 3 import ( 4 - "fmt" 5 - "math/big" 6 - "strings" 7 - "github.com/ipfs/go-cid" 4 + "fmt" 5 + "github.com/ipfs/go-cid" 6 + "math/big" 7 + "strings" 8 8 ) 9 9 10 10 const base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 11 11 12 12 // ConvertCIDBase32ToBase62 converts an IPFS CID from base32 to base62 13 13 func ConvertCIDBase32ToBase62(cidBase32 string) (string, error) { 14 - c, err := cid.Decode(cidBase32) 15 - if err != nil { 16 - return "", err 17 - } 18 - return encodeBase62(c.Bytes()), nil 14 + c, err := cid.Decode(cidBase32) 15 + if err != nil { 16 + return "", err 17 + } 18 + return encodeBase62(c.Bytes()), nil 19 19 } 20 20 21 21 // ConvertCIDBase62ToBase32 converts an IPFS CID from base62 to base32 22 22 func ConvertCIDBase62ToBase32(cidBase62 string) (string, error) { 23 - decodedBytes, err := decodeBase62(cidBase62) 24 - if err != nil { 25 - return "", fmt.Errorf("failed to decode base62: %w", err) 26 - } 27 - c, err := cid.Cast(decodedBytes) 28 - if err != nil { 29 - return "", err 30 - } 23 + decodedBytes, err := decodeBase62(cidBase62) 24 + if err != nil { 25 + return "", fmt.Errorf("failed to decode base62: %w", err) 26 + } 27 + c, err := cid.Cast(decodedBytes) 28 + if err != nil { 29 + return "", err 30 + } 31 31 32 - return c.String(), nil 32 + return c.String(), nil 33 33 } 34 34 35 35 func encodeBase62(data []byte) string { 36 - if len(data) == 0 { 37 - return "" 38 - } 36 + if len(data) == 0 { 37 + return "" 38 + } 39 39 40 - num := new(big.Int).SetBytes(data) 41 - if num.Cmp(big.NewInt(0)) == 0 { 42 - return "0" 43 - } 40 + num := new(big.Int).SetBytes(data) 41 + if num.Cmp(big.NewInt(0)) == 0 { 42 + return "0" 43 + } 44 44 45 - var result []byte 46 - base := big.NewInt(62) 47 - zero := big.NewInt(0) 45 + var result []byte 46 + base := big.NewInt(62) 47 + zero := big.NewInt(0) 48 48 49 - 50 - for num.Cmp(zero) > 0 { 51 - remainder := new(big.Int) 52 - num.DivMod(num, base, remainder) 53 - result = append(result, base62Alphabet[remainder.Int64()]) 54 - } 49 + for num.Cmp(zero) > 0 { 50 + remainder := new(big.Int) 51 + num.DivMod(num, base, remainder) 52 + result = append(result, base62Alphabet[remainder.Int64()]) 53 + } 55 54 56 - // Reverse the result 57 - for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { 58 - result[i], result[j] = result[j], result[i] 59 - } 55 + // Reverse the result 56 + for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { 57 + result[i], result[j] = result[j], result[i] 58 + } 60 59 61 - return string(result) 60 + return string(result) 62 61 } 63 62 64 63 func decodeBase62(encoded string) ([]byte, error) { 65 - if encoded == "" { 66 - return []byte{}, nil 67 - } 64 + if encoded == "" { 65 + return []byte{}, nil 66 + } 68 67 69 - num := big.NewInt(0) 70 - base := big.NewInt(62) 68 + num := big.NewInt(0) 69 + base := big.NewInt(62) 71 70 72 - for _, char := range encoded { 73 - index := strings.IndexRune(base62Alphabet, char) 74 - if index == -1 { 75 - return nil, fmt.Errorf("invalid character in base62 string: %c", char) 76 - } 71 + for _, char := range encoded { 72 + index := strings.IndexRune(base62Alphabet, char) 73 + if index == -1 { 74 + return nil, fmt.Errorf("invalid character in base62 string: %c", char) 75 + } 77 76 78 - num.Mul(num, base) 79 - num.Add(num, big.NewInt(int64(index))) 80 - } 77 + num.Mul(num, base) 78 + num.Add(num, big.NewInt(int64(index))) 79 + } 81 80 82 - return num.Bytes(), nil 81 + return num.Bytes(), nil 83 82 } 84 83 85 84 func IsBase32(str string) bool { 86 - if str == "" { 87 - return false 88 - } 89 - // Base32 uses A-Z, 2-7 (case insensitive) 90 - for _, char := range strings.ToUpper(str) { 91 - if !((char >= 'A' && char <= 'Z') || (char >= '2' && char <= '7') || char == '=') { 92 - return false 93 - } 94 - } 95 - return len(str) > 0 85 + if str == "" { 86 + return false 87 + } 88 + // Base32 uses A-Z, 2-7 (case insensitive) 89 + for _, char := range strings.ToUpper(str) { 90 + if !((char >= 'A' && char <= 'Z') || (char >= '2' && char <= '7') || char == '=') { 91 + return false 92 + } 93 + } 94 + return len(str) > 0 96 95 }
+219 -219
internal/util/util_test.go
··· 8 8 9 9 // Real IPFS CIDs for testing 10 10 var validCIDs = []string{ 11 - "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi", 12 - "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm", 13 - "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha", 11 + "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi", 12 + "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm", 13 + "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha", 14 14 } 15 15 16 16 func TestConvertCIDBase32ToBase62(t *testing.T) { 17 - tests := []struct { 18 - name string 19 - input string 20 - wantErr bool 21 - }{ 22 - { 23 - name: "valid IPFS CID 1", 24 - input: "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi", 25 - wantErr: false, 26 - }, 27 - { 28 - name: "valid IPFS CID 2", 29 - input: "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm", 30 - wantErr: false, 31 - }, 32 - { 33 - name: "valid IPFS CID 3", 34 - input: "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha", 35 - wantErr: false, 36 - }, 37 - { 38 - name: "empty string", 39 - input: "", 40 - wantErr: true, 41 - }, 42 - { 43 - name: "invalid characters", 44 - input: "invalid123!@#", 45 - wantErr: true, 46 - }, 47 - { 48 - name: "only prefix", 49 - input: "bafkrei", 50 - wantErr: true, 51 - }, 52 - } 17 + tests := []struct { 18 + name string 19 + input string 20 + wantErr bool 21 + }{ 22 + { 23 + name: "valid IPFS CID 1", 24 + input: "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi", 25 + wantErr: false, 26 + }, 27 + { 28 + name: "valid IPFS CID 2", 29 + input: "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm", 30 + wantErr: false, 31 + }, 32 + { 33 + name: "valid IPFS CID 3", 34 + input: "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha", 35 + wantErr: false, 36 + }, 37 + { 38 + name: "empty string", 39 + input: "", 40 + wantErr: true, 41 + }, 42 + { 43 + name: "invalid characters", 44 + input: "invalid123!@#", 45 + wantErr: true, 46 + }, 47 + { 48 + name: "only prefix", 49 + input: "bafkrei", 50 + wantErr: true, 51 + }, 52 + } 53 53 54 - for _, tt := range tests { 55 - t.Run(tt.name, func(t *testing.T) { 56 - result, err := ConvertCIDBase32ToBase62(tt.input) 54 + for _, tt := range tests { 55 + t.Run(tt.name, func(t *testing.T) { 56 + result, err := ConvertCIDBase32ToBase62(tt.input) 57 57 58 - if tt.wantErr { 59 - if err == nil { 60 - t.Errorf("ConvertCIDBase32ToBase62() expected error but got %v", result) 61 - } 62 - return 63 - } 58 + if tt.wantErr { 59 + if err == nil { 60 + t.Errorf("ConvertCIDBase32ToBase62() expected error but got %v", result) 61 + } 62 + return 63 + } 64 64 65 - if err != nil { 66 - t.Errorf("ConvertCIDBase32ToBase62() unexpected error: %v", err) 67 - return 68 - } 65 + if err != nil { 66 + t.Errorf("ConvertCIDBase32ToBase62() unexpected error: %v", err) 67 + return 68 + } 69 69 70 - if result == "" { 71 - t.Errorf("ConvertCIDBase32ToBase62() returned empty result") 72 - } 70 + if result == "" { 71 + t.Errorf("ConvertCIDBase32ToBase62() returned empty result") 72 + } 73 73 74 - t.Logf("Converted %s to %s", tt.input, result) 75 - }) 76 - } 74 + t.Logf("Converted %s to %s", tt.input, result) 75 + }) 76 + } 77 77 } 78 78 79 79 func TestRoundTripConversionWithRealCIDs(t *testing.T) { 80 - for i, originalCID := range validCIDs { 81 - t.Run(fmt.Sprintf("real_cid_%d", i+1), func(t *testing.T) { 82 - // Convert to base62 83 - base62Result, err := ConvertCIDBase32ToBase62(originalCID) 84 - if err != nil { 85 - t.Fatalf("Failed to convert to base62: %v", err) 86 - } 80 + for i, originalCID := range validCIDs { 81 + t.Run(fmt.Sprintf("real_cid_%d", i+1), func(t *testing.T) { 82 + // Convert to base62 83 + base62Result, err := ConvertCIDBase32ToBase62(originalCID) 84 + if err != nil { 85 + t.Fatalf("Failed to convert to base62: %v", err) 86 + } 87 87 88 - t.Logf("Original CID: %s", originalCID) 89 - t.Logf("Base62: %s", base62Result) 90 - t.Logf("Length reduction: %d -> %d chars (%.1f%% reduction)", 91 - len(originalCID), len(base62Result), 92 - float64(len(originalCID)-len(base62Result))/float64(len(originalCID))*100) 88 + t.Logf("Original CID: %s", originalCID) 89 + t.Logf("Base62: %s", base62Result) 90 + t.Logf("Length reduction: %d -> %d chars (%.1f%% reduction)", 91 + len(originalCID), len(base62Result), 92 + float64(len(originalCID)-len(base62Result))/float64(len(originalCID))*100) 93 93 94 - // Convert back to base32 95 - base32Result, err := ConvertCIDBase62ToBase32(base62Result) 96 - if err != nil { 97 - t.Fatalf("Failed to convert back to base32: %v", err) 98 - } 94 + // Convert back to base32 95 + base32Result, err := ConvertCIDBase62ToBase32(base62Result) 96 + if err != nil { 97 + t.Fatalf("Failed to convert back to base32: %v", err) 98 + } 99 99 100 - t.Logf("Restored: %s", base32Result) 100 + t.Logf("Restored: %s", base32Result) 101 101 102 - // Compare (case insensitive since base32 can be uppercase/lowercase) 103 - if !strings.EqualFold(originalCID, base32Result) { 104 - t.Errorf("Round trip failed:\nOriginal: %s\nBase62: %s\nRestored: %s", 105 - originalCID, base62Result, base32Result) 106 - } 107 - }) 108 - } 102 + // Compare (case insensitive since base32 can be uppercase/lowercase) 103 + if !strings.EqualFold(originalCID, base32Result) { 104 + t.Errorf("Round trip failed:\nOriginal: %s\nBase62: %s\nRestored: %s", 105 + originalCID, base62Result, base32Result) 106 + } 107 + }) 108 + } 109 109 } 110 110 111 111 func TestIsBase32WithRealCIDs(t *testing.T) { 112 - tests := []struct { 113 - name string 114 - input string 115 - expected bool 116 - }{ 117 - { 118 - name: "real CID 1", 119 - input: "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi", 120 - expected: true, 121 - }, 122 - { 123 - name: "real CID 2", 124 - input: "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm", 125 - expected: true, 126 - }, 127 - { 128 - name: "real CID 3", 129 - input: "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha", 130 - expected: true, 131 - }, 132 - { 133 - name: "real CID uppercase", 134 - input: strings.ToUpper("bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi"), 135 - expected: true, 136 - }, 137 - { 138 - name: "empty string", 139 - input: "", 140 - expected: false, 141 - }, 142 - { 143 - name: "invalid characters", 144 - input: "bafkrei123invalid!@#", 145 - expected: false, 146 - }, 147 - { 148 - name: "contains 0 and 1 (invalid in base32)", 149 - input: "bafkrei01234567890", 150 - expected: false, 151 - }, 152 - { 153 - name: "contains 8 and 9 (invalid in base32)", 154 - input: "bafkrei89abcdef", 155 - expected: false, 156 - }, 157 - } 112 + tests := []struct { 113 + name string 114 + input string 115 + expected bool 116 + }{ 117 + { 118 + name: "real CID 1", 119 + input: "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi", 120 + expected: true, 121 + }, 122 + { 123 + name: "real CID 2", 124 + input: "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm", 125 + expected: true, 126 + }, 127 + { 128 + name: "real CID 3", 129 + input: "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha", 130 + expected: true, 131 + }, 132 + { 133 + name: "real CID uppercase", 134 + input: strings.ToUpper("bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi"), 135 + expected: true, 136 + }, 137 + { 138 + name: "empty string", 139 + input: "", 140 + expected: false, 141 + }, 142 + { 143 + name: "invalid characters", 144 + input: "bafkrei123invalid!@#", 145 + expected: false, 146 + }, 147 + { 148 + name: "contains 0 and 1 (invalid in base32)", 149 + input: "bafkrei01234567890", 150 + expected: false, 151 + }, 152 + { 153 + name: "contains 8 and 9 (invalid in base32)", 154 + input: "bafkrei89abcdef", 155 + expected: false, 156 + }, 157 + } 158 158 159 - for _, tt := range tests { 160 - t.Run(tt.name, func(t *testing.T) { 161 - result := IsBase32(tt.input) 162 - if result != tt.expected { 163 - t.Errorf("IsBase32(%q) = %v, expected %v", tt.input, result, tt.expected) 164 - } 165 - }) 166 - } 159 + for _, tt := range tests { 160 + t.Run(tt.name, func(t *testing.T) { 161 + result := IsBase32(tt.input) 162 + if result != tt.expected { 163 + t.Errorf("IsBase32(%q) = %v, expected %v", tt.input, result, tt.expected) 164 + } 165 + }) 166 + } 167 167 } 168 168 169 169 func TestCompressionRatio(t *testing.T) { 170 - t.Log("Testing compression ratios with real CIDs:") 170 + t.Log("Testing compression ratios with real CIDs:") 171 171 172 - var totalOriginalLength, totalBase62Length int 172 + var totalOriginalLength, totalBase62Length int 173 173 174 - for i, cid := range validCIDs { 175 - base62, err := ConvertCIDBase32ToBase62(cid) 176 - if err != nil { 177 - t.Fatalf("Failed to convert CID %d: %v", i+1, err) 178 - } 174 + for i, cid := range validCIDs { 175 + base62, err := ConvertCIDBase32ToBase62(cid) 176 + if err != nil { 177 + t.Fatalf("Failed to convert CID %d: %v", i+1, err) 178 + } 179 179 180 - originalLen := len(cid) 181 - base62Len := len(base62) 182 - reduction := float64(originalLen-base62Len) / float64(originalLen) * 100 180 + originalLen := len(cid) 181 + base62Len := len(base62) 182 + reduction := float64(originalLen-base62Len) / float64(originalLen) * 100 183 183 184 - totalOriginalLength += originalLen 185 - totalBase62Length += base62Len 184 + totalOriginalLength += originalLen 185 + totalBase62Length += base62Len 186 186 187 - t.Logf("CID %d: %d -> %d chars (%.1f%% reduction)", 188 - i+1, originalLen, base62Len, reduction) 189 - } 187 + t.Logf("CID %d: %d -> %d chars (%.1f%% reduction)", 188 + i+1, originalLen, base62Len, reduction) 189 + } 190 190 191 - overallReduction := float64(totalOriginalLength-totalBase62Length) / float64(totalOriginalLength) * 100 192 - t.Logf("Overall: %d -> %d chars (%.1f%% reduction)", 193 - totalOriginalLength, totalBase62Length, overallReduction) 191 + overallReduction := float64(totalOriginalLength-totalBase62Length) / float64(totalOriginalLength) * 100 192 + t.Logf("Overall: %d -> %d chars (%.1f%% reduction)", 193 + totalOriginalLength, totalBase62Length, overallReduction) 194 194 } 195 195 196 196 func TestSpecificCIDConversions(t *testing.T) { 197 - // Test each CID individually with detailed logging 198 - testCases := []struct { 199 - name string 200 - cid string 201 - }{ 202 - {"CID with 'igns2g4c'", "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi"}, 203 - {"CID with 'igms7evf'", "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm"}, 204 - {"CID with 'ieedz3pv'", "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha"}, 205 - } 197 + // Test each CID individually with detailed logging 198 + testCases := []struct { 199 + name string 200 + cid string 201 + }{ 202 + {"CID with 'igns2g4c'", "bafkreigns2g4c46vrdvj6wlv7jka2binxftkadssvv5mxakqz4vulbfroi"}, 203 + {"CID with 'igms7evf'", "bafkreigms7evfvvxtsb2azgx64wfqv3xt62elwwt5zjtv2dcgd4idu3erm"}, 204 + {"CID with 'ieedz3pv'", "bafkreieedz3pv6onkm5ombpwbx43ytwzff3qgzing5ym45xthlnvku2wha"}, 205 + } 206 206 207 - for _, tc := range testCases { 208 - t.Run(tc.name, func(t *testing.T) { 209 - // Test IsBase32 210 - if !IsBase32(tc.cid) { 211 - t.Errorf("IsBase32 failed for %s", tc.cid) 212 - } 207 + for _, tc := range testCases { 208 + t.Run(tc.name, func(t *testing.T) { 209 + // Test IsBase32 210 + if !IsBase32(tc.cid) { 211 + t.Errorf("IsBase32 failed for %s", tc.cid) 212 + } 213 213 214 - // Test conversion to base62 215 - base62, err := ConvertCIDBase32ToBase62(tc.cid) 216 - if err != nil { 217 - t.Fatalf("ConvertCIDBase32ToBase62 failed: %v", err) 218 - } 214 + // Test conversion to base62 215 + base62, err := ConvertCIDBase32ToBase62(tc.cid) 216 + if err != nil { 217 + t.Fatalf("ConvertCIDBase32ToBase62 failed: %v", err) 218 + } 219 219 220 - // Test conversion back to base32 221 - restored, err := ConvertCIDBase62ToBase32(base62) 222 - if err != nil { 223 - t.Fatalf("ConvertCIDBase62ToBase32 failed: %v", err) 224 - } 220 + // Test conversion back to base32 221 + restored, err := ConvertCIDBase62ToBase32(base62) 222 + if err != nil { 223 + t.Fatalf("ConvertCIDBase62ToBase32 failed: %v", err) 224 + } 225 225 226 - // Verify round trip 227 - if !strings.EqualFold(tc.cid, restored) { 228 - t.Errorf("Round trip failed for %s", tc.cid) 229 - t.Errorf(" Original: %s", tc.cid) 230 - t.Errorf(" Base62: %s", base62) 231 - t.Errorf(" Restored: %s", restored) 232 - } else { 233 - t.Logf("✓ Round trip successful") 234 - t.Logf(" Original: %s", tc.cid) 235 - t.Logf(" Base62: %s", base62) 236 - t.Logf(" Restored: %s", restored) 237 - } 238 - }) 239 - } 226 + // Verify round trip 227 + if !strings.EqualFold(tc.cid, restored) { 228 + t.Errorf("Round trip failed for %s", tc.cid) 229 + t.Errorf(" Original: %s", tc.cid) 230 + t.Errorf(" Base62: %s", base62) 231 + t.Errorf(" Restored: %s", restored) 232 + } else { 233 + t.Logf("✓ Round trip successful") 234 + t.Logf(" Original: %s", tc.cid) 235 + t.Logf(" Base62: %s", base62) 236 + t.Logf(" Restored: %s", restored) 237 + } 238 + }) 239 + } 240 240 } 241 241 242 242 // Benchmark with real CIDs 243 243 func BenchmarkConvertRealCIDBase32ToBase62(b *testing.B) { 244 - cid := validCIDs[0] // Use first real CID 244 + cid := validCIDs[0] // Use first real CID 245 245 246 - b.ResetTimer() 247 - for i := 0; i < b.N; i++ { 248 - _, err := ConvertCIDBase32ToBase62(cid) 249 - if err != nil { 250 - b.Fatal(err) 251 - } 252 - } 246 + b.ResetTimer() 247 + for i := 0; i < b.N; i++ { 248 + _, err := ConvertCIDBase32ToBase62(cid) 249 + if err != nil { 250 + b.Fatal(err) 251 + } 252 + } 253 253 } 254 254 255 255 func BenchmarkConvertRealCIDBase62ToBase32(b *testing.B) { 256 - // Convert real CID to base62 first 257 - base62CID, err := ConvertCIDBase32ToBase62(validCIDs[0]) 258 - if err != nil { 259 - b.Fatal(err) 260 - } 256 + // Convert real CID to base62 first 257 + base62CID, err := ConvertCIDBase32ToBase62(validCIDs[0]) 258 + if err != nil { 259 + b.Fatal(err) 260 + } 261 261 262 - b.ResetTimer() 263 - for i := 0; i < b.N; i++ { 264 - _, err := ConvertCIDBase62ToBase32(base62CID) 265 - if err != nil { 266 - b.Fatal(err) 267 - } 268 - } 262 + b.ResetTimer() 263 + for i := 0; i < b.N; i++ { 264 + _, err := ConvertCIDBase62ToBase32(base62CID) 265 + if err != nil { 266 + b.Fatal(err) 267 + } 268 + } 269 269 } 270 270 271 271 func BenchmarkIsBase32RealCID(b *testing.B) { 272 - cid := validCIDs[0] 272 + cid := validCIDs[0] 273 273 274 - b.ResetTimer() 275 - for i := 0; i < b.N; i++ { 276 - IsBase32(cid) 277 - } 274 + b.ResetTimer() 275 + for i := 0; i < b.N; i++ { 276 + IsBase32(cid) 277 + } 278 278 }