├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Makefile ├── README.md ├── clipboard-sync.service ├── control ├── default.nix ├── flake.lock ├── flake.nix └── src ├── clipboard.rs ├── error.rs ├── log.rs ├── main.rs ├── mustatex.rs ├── sync.rs └── zombies.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled files and executables 2 | /target/ 3 | /result 4 | 5 | # distributable packages 6 | /dist/ 7 | 8 | # These are backup files generated by rustfmt 9 | **/*.rs.bk 10 | 11 | .vscode 12 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.17.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "adler32" 22 | version = "1.2.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" 25 | 26 | [[package]] 27 | name = "ansi_term" 28 | version = "0.12.1" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 31 | dependencies = [ 32 | "winapi", 33 | ] 34 | 35 | [[package]] 36 | name = "anyhow" 37 | version = "1.0.56" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27" 40 | 41 | [[package]] 42 | name = "arboard" 43 | version = "2.1.0" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "6045ca509e4abacde2b884ac4618a51d0c017b5d85a3ee84a7226eb33b3154a9" 46 | dependencies = [ 47 | "clipboard-win", 48 | "core-graphics", 49 | "image", 50 | "log", 51 | "objc", 52 | "objc-foundation", 53 | "objc_id", 54 | "once_cell", 55 | "parking_lot 0.12.0", 56 | "thiserror", 57 | "winapi", 58 | "x11rb", 59 | ] 60 | 61 | [[package]] 62 | name = "atty" 63 | version = "0.2.11" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 66 | dependencies = [ 67 | "libc", 68 | "termion", 69 | "winapi", 70 | ] 71 | 72 | [[package]] 73 | name = "autocfg" 74 | version = "1.1.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 77 | 78 | [[package]] 79 | name = "backtrace" 80 | version = "0.3.64" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" 83 | dependencies = [ 84 | "addr2line", 85 | "cc", 86 | "cfg-if 1.0.0", 87 | "libc", 88 | "miniz_oxide 0.4.4", 89 | "object", 90 | "rustc-demangle", 91 | ] 92 | 93 | [[package]] 94 | name = "bitflags" 95 | version = "1.3.2" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 98 | 99 | [[package]] 100 | name = "block" 101 | version = "0.1.6" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" 104 | 105 | [[package]] 106 | name = "bytecount" 107 | version = "0.6.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" 110 | 111 | [[package]] 112 | name = "bytemuck" 113 | version = "1.9.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "ee1e0e2125faccb856bf10b0a9dfa89c4c718d05ef85580dfefbdf1c422ef801" 116 | 117 | [[package]] 118 | name = "byteorder" 119 | version = "1.4.3" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 122 | 123 | [[package]] 124 | name = "cc" 125 | version = "1.0.73" 126 | source = "registry+https://github.com/rust-lang/crates.io-index" 127 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" 128 | 129 | [[package]] 130 | name = "cfg-if" 131 | version = "0.1.10" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 134 | 135 | [[package]] 136 | name = "cfg-if" 137 | version = "1.0.0" 138 | source = "registry+https://github.com/rust-lang/crates.io-index" 139 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 140 | 141 | [[package]] 142 | name = "chrono" 143 | version = "0.4.19" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" 146 | dependencies = [ 147 | "libc", 148 | "num-integer", 149 | "num-traits", 150 | "time", 151 | "winapi", 152 | ] 153 | 154 | [[package]] 155 | name = "clap" 156 | version = "2.34.0" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 159 | dependencies = [ 160 | "ansi_term", 161 | "atty", 162 | "bitflags", 163 | "strsim 0.8.0", 164 | "term_size", 165 | "textwrap", 166 | "unicode-width", 167 | "vec_map", 168 | ] 169 | 170 | [[package]] 171 | name = "clap" 172 | version = "4.0.29" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" 175 | dependencies = [ 176 | "bitflags", 177 | "clap_derive", 178 | "clap_lex", 179 | "is-terminal", 180 | "once_cell", 181 | "strsim 0.10.0", 182 | "termcolor", 183 | "terminal_size", 184 | ] 185 | 186 | [[package]] 187 | name = "clap_derive" 188 | version = "4.0.21" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "0177313f9f02afc995627906bbd8967e2be069f5261954222dac78290c2b9014" 191 | dependencies = [ 192 | "heck 0.4.0", 193 | "proc-macro-error", 194 | "proc-macro2", 195 | "quote", 196 | "syn", 197 | ] 198 | 199 | [[package]] 200 | name = "clap_lex" 201 | version = "0.3.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "0d4198f73e42b4936b35b5bb248d81d2b595ecb170da0bac7655c54eedfa8da8" 204 | dependencies = [ 205 | "os_str_bytes", 206 | ] 207 | 208 | [[package]] 209 | name = "cli-clipboard" 210 | version = "0.2.1" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "40debc19e73992ef303534cce81b963a3706e54ae2f27ae2c136cdc6fe735232" 213 | dependencies = [ 214 | "anyhow", 215 | "clipboard-win", 216 | "failure", 217 | "objc", 218 | "objc-foundation", 219 | "objc_id", 220 | "wl-clipboard-rs 0.4.1", 221 | "x11-clipboard", 222 | ] 223 | 224 | [[package]] 225 | name = "clipboard-sync" 226 | version = "0.2.0" 227 | dependencies = [ 228 | "anyhow", 229 | "arboard", 230 | "chrono", 231 | "clap 4.0.29", 232 | "cli-clipboard", 233 | "gag", 234 | "itertools", 235 | "nix 0.23.1", 236 | "terminal-clipboard", 237 | "thiserror", 238 | "wayland-client 0.29.4", 239 | "wl-clipboard-rs 0.7.0", 240 | "x11-clipboard", 241 | ] 242 | 243 | [[package]] 244 | name = "clipboard-win" 245 | version = "4.4.1" 246 | source = "registry+https://github.com/rust-lang/crates.io-index" 247 | checksum = "2f3e1238132dc01f081e1cbb9dace14e5ef4c3a51ee244bd982275fb514605db" 248 | dependencies = [ 249 | "error-code", 250 | "str-buf", 251 | "winapi", 252 | ] 253 | 254 | [[package]] 255 | name = "cloudabi" 256 | version = "0.0.3" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" 259 | dependencies = [ 260 | "bitflags", 261 | ] 262 | 263 | [[package]] 264 | name = "color_quant" 265 | version = "1.1.0" 266 | source = "registry+https://github.com/rust-lang/crates.io-index" 267 | checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" 268 | 269 | [[package]] 270 | name = "convert_case" 271 | version = "0.4.0" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 274 | 275 | [[package]] 276 | name = "core-foundation" 277 | version = "0.9.3" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" 280 | dependencies = [ 281 | "core-foundation-sys", 282 | "libc", 283 | ] 284 | 285 | [[package]] 286 | name = "core-foundation-sys" 287 | version = "0.8.3" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 290 | 291 | [[package]] 292 | name = "core-graphics" 293 | version = "0.22.3" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" 296 | dependencies = [ 297 | "bitflags", 298 | "core-foundation", 299 | "core-graphics-types", 300 | "foreign-types", 301 | "libc", 302 | ] 303 | 304 | [[package]] 305 | name = "core-graphics-types" 306 | version = "0.1.1" 307 | source = "registry+https://github.com/rust-lang/crates.io-index" 308 | checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" 309 | dependencies = [ 310 | "bitflags", 311 | "core-foundation", 312 | "foreign-types", 313 | "libc", 314 | ] 315 | 316 | [[package]] 317 | name = "crc32fast" 318 | version = "1.3.2" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 321 | dependencies = [ 322 | "cfg-if 1.0.0", 323 | ] 324 | 325 | [[package]] 326 | name = "deflate" 327 | version = "0.8.6" 328 | source = "registry+https://github.com/rust-lang/crates.io-index" 329 | checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" 330 | dependencies = [ 331 | "adler32", 332 | "byteorder", 333 | ] 334 | 335 | [[package]] 336 | name = "derive-new" 337 | version = "0.5.9" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" 340 | dependencies = [ 341 | "proc-macro2", 342 | "quote", 343 | "syn", 344 | ] 345 | 346 | [[package]] 347 | name = "derive_more" 348 | version = "0.99.17" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 351 | dependencies = [ 352 | "convert_case", 353 | "proc-macro2", 354 | "quote", 355 | "rustc_version", 356 | "syn", 357 | ] 358 | 359 | [[package]] 360 | name = "downcast-rs" 361 | version = "1.2.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 364 | 365 | [[package]] 366 | name = "either" 367 | version = "1.7.0" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" 370 | 371 | [[package]] 372 | name = "errno" 373 | version = "0.2.8" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 376 | dependencies = [ 377 | "errno-dragonfly", 378 | "libc", 379 | "winapi", 380 | ] 381 | 382 | [[package]] 383 | name = "errno-dragonfly" 384 | version = "0.1.2" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 387 | dependencies = [ 388 | "cc", 389 | "libc", 390 | ] 391 | 392 | [[package]] 393 | name = "error-code" 394 | version = "2.3.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "64f18991e7bf11e7ffee451b5318b5c1a73c52d0d0ada6e5a3017c8c1ced6a21" 397 | dependencies = [ 398 | "libc", 399 | "str-buf", 400 | ] 401 | 402 | [[package]] 403 | name = "exitfailure" 404 | version = "0.5.1" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "2ff5bd832af37f366c6c194d813a11cd90ac484f124f079294f28e357ae40515" 407 | dependencies = [ 408 | "failure", 409 | ] 410 | 411 | [[package]] 412 | name = "failure" 413 | version = "0.1.8" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" 416 | dependencies = [ 417 | "backtrace", 418 | "failure_derive", 419 | ] 420 | 421 | [[package]] 422 | name = "failure_derive" 423 | version = "0.1.8" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" 426 | dependencies = [ 427 | "proc-macro2", 428 | "quote", 429 | "syn", 430 | "synstructure", 431 | ] 432 | 433 | [[package]] 434 | name = "fastrand" 435 | version = "1.7.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 438 | dependencies = [ 439 | "instant", 440 | ] 441 | 442 | [[package]] 443 | name = "filedescriptor" 444 | version = "0.8.2" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" 447 | dependencies = [ 448 | "libc", 449 | "thiserror", 450 | "winapi", 451 | ] 452 | 453 | [[package]] 454 | name = "fixedbitset" 455 | version = "0.2.0" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "37ab347416e802de484e4d03c7316c48f1ecb56574dfd4a46a80f173ce1de04d" 458 | 459 | [[package]] 460 | name = "fixedbitset" 461 | version = "0.4.1" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e" 464 | 465 | [[package]] 466 | name = "fnv" 467 | version = "1.0.7" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 470 | 471 | [[package]] 472 | name = "foreign-types" 473 | version = "0.3.2" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 476 | dependencies = [ 477 | "foreign-types-shared", 478 | ] 479 | 480 | [[package]] 481 | name = "foreign-types-shared" 482 | version = "0.1.1" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 485 | 486 | [[package]] 487 | name = "gag" 488 | version = "1.0.0" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "a713bee13966e9fbffdf7193af71d54a6b35a0bb34997cd6c9519ebeb5005972" 491 | dependencies = [ 492 | "filedescriptor", 493 | "tempfile", 494 | ] 495 | 496 | [[package]] 497 | name = "gethostname" 498 | version = "0.2.3" 499 | source = "registry+https://github.com/rust-lang/crates.io-index" 500 | checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e" 501 | dependencies = [ 502 | "libc", 503 | "winapi", 504 | ] 505 | 506 | [[package]] 507 | name = "gimli" 508 | version = "0.26.1" 509 | source = "registry+https://github.com/rust-lang/crates.io-index" 510 | checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" 511 | 512 | [[package]] 513 | name = "hashbrown" 514 | version = "0.11.2" 515 | source = "registry+https://github.com/rust-lang/crates.io-index" 516 | checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" 517 | 518 | [[package]] 519 | name = "heck" 520 | version = "0.3.3" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 523 | dependencies = [ 524 | "unicode-segmentation", 525 | ] 526 | 527 | [[package]] 528 | name = "heck" 529 | version = "0.4.0" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" 532 | 533 | [[package]] 534 | name = "hermit-abi" 535 | version = "0.2.6" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 538 | dependencies = [ 539 | "libc", 540 | ] 541 | 542 | [[package]] 543 | name = "image" 544 | version = "0.23.14" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" 547 | dependencies = [ 548 | "bytemuck", 549 | "byteorder", 550 | "color_quant", 551 | "num-iter", 552 | "num-rational", 553 | "num-traits", 554 | "png", 555 | "tiff", 556 | ] 557 | 558 | [[package]] 559 | name = "indexmap" 560 | version = "1.8.1" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" 563 | dependencies = [ 564 | "autocfg", 565 | "hashbrown", 566 | ] 567 | 568 | [[package]] 569 | name = "instant" 570 | version = "0.1.12" 571 | source = "registry+https://github.com/rust-lang/crates.io-index" 572 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 573 | dependencies = [ 574 | "cfg-if 1.0.0", 575 | ] 576 | 577 | [[package]] 578 | name = "io-lifetimes" 579 | version = "1.0.3" 580 | source = "registry+https://github.com/rust-lang/crates.io-index" 581 | checksum = "46112a93252b123d31a119a8d1a1ac19deac4fac6e0e8b0df58f0d4e5870e63c" 582 | dependencies = [ 583 | "libc", 584 | "windows-sys 0.42.0", 585 | ] 586 | 587 | [[package]] 588 | name = "is-terminal" 589 | version = "0.4.1" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" 592 | dependencies = [ 593 | "hermit-abi", 594 | "io-lifetimes", 595 | "rustix", 596 | "windows-sys 0.42.0", 597 | ] 598 | 599 | [[package]] 600 | name = "itertools" 601 | version = "0.10.3" 602 | source = "registry+https://github.com/rust-lang/crates.io-index" 603 | checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" 604 | dependencies = [ 605 | "either", 606 | ] 607 | 608 | [[package]] 609 | name = "jpeg-decoder" 610 | version = "0.1.22" 611 | source = "registry+https://github.com/rust-lang/crates.io-index" 612 | checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" 613 | 614 | [[package]] 615 | name = "lazy_static" 616 | version = "0.2.11" 617 | source = "registry+https://github.com/rust-lang/crates.io-index" 618 | checksum = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" 619 | 620 | [[package]] 621 | name = "lazy_static" 622 | version = "1.4.0" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 625 | 626 | [[package]] 627 | name = "libc" 628 | version = "0.2.138" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8" 631 | 632 | [[package]] 633 | name = "linux-raw-sys" 634 | version = "0.1.3" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "8f9f08d8963a6c613f4b1a78f4f4a4dbfadf8e6545b2d72861731e4858b8b47f" 637 | 638 | [[package]] 639 | name = "lock_api" 640 | version = "0.3.4" 641 | source = "registry+https://github.com/rust-lang/crates.io-index" 642 | checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" 643 | dependencies = [ 644 | "scopeguard", 645 | ] 646 | 647 | [[package]] 648 | name = "lock_api" 649 | version = "0.4.7" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" 652 | dependencies = [ 653 | "autocfg", 654 | "scopeguard", 655 | ] 656 | 657 | [[package]] 658 | name = "log" 659 | version = "0.4.16" 660 | source = "registry+https://github.com/rust-lang/crates.io-index" 661 | checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" 662 | dependencies = [ 663 | "cfg-if 1.0.0", 664 | ] 665 | 666 | [[package]] 667 | name = "malloc_buf" 668 | version = "0.0.6" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" 671 | dependencies = [ 672 | "libc", 673 | ] 674 | 675 | [[package]] 676 | name = "memchr" 677 | version = "1.0.2" 678 | source = "registry+https://github.com/rust-lang/crates.io-index" 679 | checksum = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" 680 | dependencies = [ 681 | "libc", 682 | ] 683 | 684 | [[package]] 685 | name = "memchr" 686 | version = "2.4.1" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 689 | 690 | [[package]] 691 | name = "memoffset" 692 | version = "0.6.5" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 695 | dependencies = [ 696 | "autocfg", 697 | ] 698 | 699 | [[package]] 700 | name = "mime" 701 | version = "0.3.16" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" 704 | 705 | [[package]] 706 | name = "mime_guess" 707 | version = "2.0.4" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" 710 | dependencies = [ 711 | "mime", 712 | "unicase", 713 | ] 714 | 715 | [[package]] 716 | name = "minimal-lexical" 717 | version = "0.2.1" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 720 | 721 | [[package]] 722 | name = "miniz_oxide" 723 | version = "0.3.7" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" 726 | dependencies = [ 727 | "adler32", 728 | ] 729 | 730 | [[package]] 731 | name = "miniz_oxide" 732 | version = "0.4.4" 733 | source = "registry+https://github.com/rust-lang/crates.io-index" 734 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 735 | dependencies = [ 736 | "adler", 737 | "autocfg", 738 | ] 739 | 740 | [[package]] 741 | name = "nix" 742 | version = "0.17.0" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" 745 | dependencies = [ 746 | "bitflags", 747 | "cc", 748 | "cfg-if 0.1.10", 749 | "libc", 750 | "void", 751 | ] 752 | 753 | [[package]] 754 | name = "nix" 755 | version = "0.18.0" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" 758 | dependencies = [ 759 | "bitflags", 760 | "cc", 761 | "cfg-if 0.1.10", 762 | "libc", 763 | ] 764 | 765 | [[package]] 766 | name = "nix" 767 | version = "0.22.3" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" 770 | dependencies = [ 771 | "bitflags", 772 | "cc", 773 | "cfg-if 1.0.0", 774 | "libc", 775 | "memoffset", 776 | ] 777 | 778 | [[package]] 779 | name = "nix" 780 | version = "0.23.1" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" 783 | dependencies = [ 784 | "bitflags", 785 | "cc", 786 | "cfg-if 1.0.0", 787 | "libc", 788 | "memoffset", 789 | ] 790 | 791 | [[package]] 792 | name = "nix" 793 | version = "0.24.2" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" 796 | dependencies = [ 797 | "bitflags", 798 | "cfg-if 1.0.0", 799 | "libc", 800 | "memoffset", 801 | ] 802 | 803 | [[package]] 804 | name = "nom" 805 | version = "3.2.1" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b" 808 | dependencies = [ 809 | "memchr 1.0.2", 810 | ] 811 | 812 | [[package]] 813 | name = "nom" 814 | version = "7.1.1" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" 817 | dependencies = [ 818 | "memchr 2.4.1", 819 | "minimal-lexical", 820 | ] 821 | 822 | [[package]] 823 | name = "num-integer" 824 | version = "0.1.44" 825 | source = "registry+https://github.com/rust-lang/crates.io-index" 826 | checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" 827 | dependencies = [ 828 | "autocfg", 829 | "num-traits", 830 | ] 831 | 832 | [[package]] 833 | name = "num-iter" 834 | version = "0.1.42" 835 | source = "registry+https://github.com/rust-lang/crates.io-index" 836 | checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" 837 | dependencies = [ 838 | "autocfg", 839 | "num-integer", 840 | "num-traits", 841 | ] 842 | 843 | [[package]] 844 | name = "num-rational" 845 | version = "0.3.2" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" 848 | dependencies = [ 849 | "autocfg", 850 | "num-integer", 851 | "num-traits", 852 | ] 853 | 854 | [[package]] 855 | name = "num-traits" 856 | version = "0.2.14" 857 | source = "registry+https://github.com/rust-lang/crates.io-index" 858 | checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" 859 | dependencies = [ 860 | "autocfg", 861 | ] 862 | 863 | [[package]] 864 | name = "numtoa" 865 | version = "0.1.0" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" 868 | 869 | [[package]] 870 | name = "objc" 871 | version = "0.2.7" 872 | source = "registry+https://github.com/rust-lang/crates.io-index" 873 | checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" 874 | dependencies = [ 875 | "malloc_buf", 876 | ] 877 | 878 | [[package]] 879 | name = "objc-foundation" 880 | version = "0.1.1" 881 | source = "registry+https://github.com/rust-lang/crates.io-index" 882 | checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" 883 | dependencies = [ 884 | "block", 885 | "objc", 886 | "objc_id", 887 | ] 888 | 889 | [[package]] 890 | name = "objc_id" 891 | version = "0.1.1" 892 | source = "registry+https://github.com/rust-lang/crates.io-index" 893 | checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" 894 | dependencies = [ 895 | "objc", 896 | ] 897 | 898 | [[package]] 899 | name = "object" 900 | version = "0.27.1" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" 903 | dependencies = [ 904 | "memchr 2.4.1", 905 | ] 906 | 907 | [[package]] 908 | name = "once_cell" 909 | version = "1.16.0" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" 912 | 913 | [[package]] 914 | name = "os_pipe" 915 | version = "0.9.2" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" 918 | dependencies = [ 919 | "libc", 920 | "winapi", 921 | ] 922 | 923 | [[package]] 924 | name = "os_pipe" 925 | version = "1.0.1" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "2c92f2b54f081d635c77e7120862d48db8e91f7f21cef23ab1b4fe9971c59f55" 928 | dependencies = [ 929 | "libc", 930 | "winapi", 931 | ] 932 | 933 | [[package]] 934 | name = "os_str_bytes" 935 | version = "6.4.1" 936 | source = "registry+https://github.com/rust-lang/crates.io-index" 937 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 938 | 939 | [[package]] 940 | name = "parking_lot" 941 | version = "0.10.2" 942 | source = "registry+https://github.com/rust-lang/crates.io-index" 943 | checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" 944 | dependencies = [ 945 | "lock_api 0.3.4", 946 | "parking_lot_core 0.7.2", 947 | ] 948 | 949 | [[package]] 950 | name = "parking_lot" 951 | version = "0.12.0" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58" 954 | dependencies = [ 955 | "lock_api 0.4.7", 956 | "parking_lot_core 0.9.2", 957 | ] 958 | 959 | [[package]] 960 | name = "parking_lot_core" 961 | version = "0.7.2" 962 | source = "registry+https://github.com/rust-lang/crates.io-index" 963 | checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" 964 | dependencies = [ 965 | "cfg-if 0.1.10", 966 | "cloudabi", 967 | "libc", 968 | "redox_syscall 0.1.57", 969 | "smallvec", 970 | "winapi", 971 | ] 972 | 973 | [[package]] 974 | name = "parking_lot_core" 975 | version = "0.9.2" 976 | source = "registry+https://github.com/rust-lang/crates.io-index" 977 | checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37" 978 | dependencies = [ 979 | "cfg-if 1.0.0", 980 | "libc", 981 | "redox_syscall 0.2.13", 982 | "smallvec", 983 | "windows-sys 0.34.0", 984 | ] 985 | 986 | [[package]] 987 | name = "petgraph" 988 | version = "0.5.1" 989 | source = "registry+https://github.com/rust-lang/crates.io-index" 990 | checksum = "467d164a6de56270bd7c4d070df81d07beace25012d5103ced4e9ff08d6afdb7" 991 | dependencies = [ 992 | "fixedbitset 0.2.0", 993 | "indexmap", 994 | ] 995 | 996 | [[package]] 997 | name = "petgraph" 998 | version = "0.6.0" 999 | source = "registry+https://github.com/rust-lang/crates.io-index" 1000 | checksum = "4a13a2fa9d0b63e5f22328828741e523766fff0ee9e779316902290dff3f824f" 1001 | dependencies = [ 1002 | "fixedbitset 0.4.1", 1003 | "indexmap", 1004 | ] 1005 | 1006 | [[package]] 1007 | name = "pkg-config" 1008 | version = "0.3.24" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 1011 | 1012 | [[package]] 1013 | name = "png" 1014 | version = "0.16.8" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" 1017 | dependencies = [ 1018 | "bitflags", 1019 | "crc32fast", 1020 | "deflate", 1021 | "miniz_oxide 0.3.7", 1022 | ] 1023 | 1024 | [[package]] 1025 | name = "proc-macro-error" 1026 | version = "1.0.4" 1027 | source = "registry+https://github.com/rust-lang/crates.io-index" 1028 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 1029 | dependencies = [ 1030 | "proc-macro-error-attr", 1031 | "proc-macro2", 1032 | "quote", 1033 | "syn", 1034 | "version_check", 1035 | ] 1036 | 1037 | [[package]] 1038 | name = "proc-macro-error-attr" 1039 | version = "1.0.4" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 1042 | dependencies = [ 1043 | "proc-macro2", 1044 | "quote", 1045 | "version_check", 1046 | ] 1047 | 1048 | [[package]] 1049 | name = "proc-macro2" 1050 | version = "1.0.47" 1051 | source = "registry+https://github.com/rust-lang/crates.io-index" 1052 | checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725" 1053 | dependencies = [ 1054 | "unicode-ident", 1055 | ] 1056 | 1057 | [[package]] 1058 | name = "quick-xml" 1059 | version = "0.22.0" 1060 | source = "registry+https://github.com/rust-lang/crates.io-index" 1061 | checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" 1062 | dependencies = [ 1063 | "memchr 2.4.1", 1064 | ] 1065 | 1066 | [[package]] 1067 | name = "quote" 1068 | version = "1.0.17" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58" 1071 | dependencies = [ 1072 | "proc-macro2", 1073 | ] 1074 | 1075 | [[package]] 1076 | name = "redox_syscall" 1077 | version = "0.1.57" 1078 | source = "registry+https://github.com/rust-lang/crates.io-index" 1079 | checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" 1080 | 1081 | [[package]] 1082 | name = "redox_syscall" 1083 | version = "0.2.13" 1084 | source = "registry+https://github.com/rust-lang/crates.io-index" 1085 | checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" 1086 | dependencies = [ 1087 | "bitflags", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "redox_termios" 1092 | version = "0.1.2" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" 1095 | dependencies = [ 1096 | "redox_syscall 0.2.13", 1097 | ] 1098 | 1099 | [[package]] 1100 | name = "remove_dir_all" 1101 | version = "0.5.3" 1102 | source = "registry+https://github.com/rust-lang/crates.io-index" 1103 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 1104 | dependencies = [ 1105 | "winapi", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "rustc-demangle" 1110 | version = "0.1.21" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" 1113 | 1114 | [[package]] 1115 | name = "rustc_version" 1116 | version = "0.4.0" 1117 | source = "registry+https://github.com/rust-lang/crates.io-index" 1118 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 1119 | dependencies = [ 1120 | "semver", 1121 | ] 1122 | 1123 | [[package]] 1124 | name = "rustix" 1125 | version = "0.36.5" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" 1128 | dependencies = [ 1129 | "bitflags", 1130 | "errno", 1131 | "io-lifetimes", 1132 | "libc", 1133 | "linux-raw-sys", 1134 | "windows-sys 0.42.0", 1135 | ] 1136 | 1137 | [[package]] 1138 | name = "scopeguard" 1139 | version = "1.1.0" 1140 | source = "registry+https://github.com/rust-lang/crates.io-index" 1141 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 1142 | 1143 | [[package]] 1144 | name = "semver" 1145 | version = "1.0.7" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" 1148 | 1149 | [[package]] 1150 | name = "smallvec" 1151 | version = "1.8.0" 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" 1153 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 1154 | 1155 | [[package]] 1156 | name = "stderrlog" 1157 | version = "0.4.3" 1158 | source = "registry+https://github.com/rust-lang/crates.io-index" 1159 | checksum = "32e5ee9b90a5452c570a0b0ac1c99ae9498db7e56e33d74366de7f2a7add7f25" 1160 | dependencies = [ 1161 | "atty", 1162 | "chrono", 1163 | "log", 1164 | "termcolor", 1165 | "thread_local", 1166 | ] 1167 | 1168 | [[package]] 1169 | name = "str-buf" 1170 | version = "1.0.5" 1171 | source = "registry+https://github.com/rust-lang/crates.io-index" 1172 | checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" 1173 | 1174 | [[package]] 1175 | name = "strsim" 1176 | version = "0.8.0" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 1179 | 1180 | [[package]] 1181 | name = "strsim" 1182 | version = "0.10.0" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1185 | 1186 | [[package]] 1187 | name = "structopt" 1188 | version = "0.3.26" 1189 | source = "registry+https://github.com/rust-lang/crates.io-index" 1190 | checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" 1191 | dependencies = [ 1192 | "clap 2.34.0", 1193 | "lazy_static 1.4.0", 1194 | "structopt-derive", 1195 | ] 1196 | 1197 | [[package]] 1198 | name = "structopt-derive" 1199 | version = "0.4.18" 1200 | source = "registry+https://github.com/rust-lang/crates.io-index" 1201 | checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" 1202 | dependencies = [ 1203 | "heck 0.3.3", 1204 | "proc-macro-error", 1205 | "proc-macro2", 1206 | "quote", 1207 | "syn", 1208 | ] 1209 | 1210 | [[package]] 1211 | name = "syn" 1212 | version = "1.0.90" 1213 | source = "registry+https://github.com/rust-lang/crates.io-index" 1214 | checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f" 1215 | dependencies = [ 1216 | "proc-macro2", 1217 | "quote", 1218 | "unicode-xid", 1219 | ] 1220 | 1221 | [[package]] 1222 | name = "synstructure" 1223 | version = "0.12.6" 1224 | source = "registry+https://github.com/rust-lang/crates.io-index" 1225 | checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" 1226 | dependencies = [ 1227 | "proc-macro2", 1228 | "quote", 1229 | "syn", 1230 | "unicode-xid", 1231 | ] 1232 | 1233 | [[package]] 1234 | name = "tempfile" 1235 | version = "3.3.0" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 1238 | dependencies = [ 1239 | "cfg-if 1.0.0", 1240 | "fastrand", 1241 | "libc", 1242 | "redox_syscall 0.2.13", 1243 | "remove_dir_all", 1244 | "winapi", 1245 | ] 1246 | 1247 | [[package]] 1248 | name = "term_size" 1249 | version = "0.3.2" 1250 | source = "registry+https://github.com/rust-lang/crates.io-index" 1251 | checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" 1252 | dependencies = [ 1253 | "libc", 1254 | "winapi", 1255 | ] 1256 | 1257 | [[package]] 1258 | name = "termcolor" 1259 | version = "1.1.3" 1260 | source = "registry+https://github.com/rust-lang/crates.io-index" 1261 | checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" 1262 | dependencies = [ 1263 | "winapi-util", 1264 | ] 1265 | 1266 | [[package]] 1267 | name = "terminal-clipboard" 1268 | version = "0.3.1" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "58a7bd0c28e9221173c7d32e774bc3c5432fe788f91984dd1b78c501b5e7d1df" 1271 | dependencies = [ 1272 | "clipboard-win", 1273 | "once_cell", 1274 | "termux-clipboard", 1275 | "x11-clipboard", 1276 | ] 1277 | 1278 | [[package]] 1279 | name = "terminal_size" 1280 | version = "0.2.3" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "cb20089a8ba2b69debd491f8d2d023761cbf196e999218c591fa1e7e15a21907" 1283 | dependencies = [ 1284 | "rustix", 1285 | "windows-sys 0.42.0", 1286 | ] 1287 | 1288 | [[package]] 1289 | name = "termion" 1290 | version = "1.5.6" 1291 | source = "registry+https://github.com/rust-lang/crates.io-index" 1292 | checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e" 1293 | dependencies = [ 1294 | "libc", 1295 | "numtoa", 1296 | "redox_syscall 0.2.13", 1297 | "redox_termios", 1298 | ] 1299 | 1300 | [[package]] 1301 | name = "termux-clipboard" 1302 | version = "0.1.0" 1303 | source = "registry+https://github.com/rust-lang/crates.io-index" 1304 | checksum = "9f6aff13ca3293315b94f6dbd9c69e1c958fe421c294681e2ffda80c9858e36f" 1305 | 1306 | [[package]] 1307 | name = "textwrap" 1308 | version = "0.11.0" 1309 | source = "registry+https://github.com/rust-lang/crates.io-index" 1310 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 1311 | dependencies = [ 1312 | "term_size", 1313 | "unicode-width", 1314 | ] 1315 | 1316 | [[package]] 1317 | name = "thiserror" 1318 | version = "1.0.30" 1319 | source = "registry+https://github.com/rust-lang/crates.io-index" 1320 | checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" 1321 | dependencies = [ 1322 | "thiserror-impl", 1323 | ] 1324 | 1325 | [[package]] 1326 | name = "thiserror-impl" 1327 | version = "1.0.30" 1328 | source = "registry+https://github.com/rust-lang/crates.io-index" 1329 | checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" 1330 | dependencies = [ 1331 | "proc-macro2", 1332 | "quote", 1333 | "syn", 1334 | ] 1335 | 1336 | [[package]] 1337 | name = "thread_local" 1338 | version = "0.3.4" 1339 | source = "registry+https://github.com/rust-lang/crates.io-index" 1340 | checksum = "1697c4b57aeeb7a536b647165a2825faddffb1d3bad386d507709bd51a90bb14" 1341 | dependencies = [ 1342 | "lazy_static 0.2.11", 1343 | "unreachable", 1344 | ] 1345 | 1346 | [[package]] 1347 | name = "tiff" 1348 | version = "0.6.1" 1349 | source = "registry+https://github.com/rust-lang/crates.io-index" 1350 | checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437" 1351 | dependencies = [ 1352 | "jpeg-decoder", 1353 | "miniz_oxide 0.4.4", 1354 | "weezl", 1355 | ] 1356 | 1357 | [[package]] 1358 | name = "time" 1359 | version = "0.1.44" 1360 | source = "registry+https://github.com/rust-lang/crates.io-index" 1361 | checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" 1362 | dependencies = [ 1363 | "libc", 1364 | "wasi", 1365 | "winapi", 1366 | ] 1367 | 1368 | [[package]] 1369 | name = "tree_magic" 1370 | version = "0.2.3" 1371 | source = "registry+https://github.com/rust-lang/crates.io-index" 1372 | checksum = "b1d99367ce3e553a84738f73bd626ccca541ef90ae757fdcdc4cbe728e6cb629" 1373 | dependencies = [ 1374 | "fnv", 1375 | "lazy_static 1.4.0", 1376 | "nom 3.2.1", 1377 | "parking_lot 0.10.2", 1378 | "petgraph 0.5.1", 1379 | ] 1380 | 1381 | [[package]] 1382 | name = "tree_magic_mini" 1383 | version = "3.0.3" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "91adfd0607cacf6e4babdb870e9bec4037c1c4b151cfd279ccefc5e0c7feaa6d" 1386 | dependencies = [ 1387 | "bytecount", 1388 | "fnv", 1389 | "lazy_static 1.4.0", 1390 | "nom 7.1.1", 1391 | "once_cell", 1392 | "petgraph 0.6.0", 1393 | ] 1394 | 1395 | [[package]] 1396 | name = "unicase" 1397 | version = "2.6.0" 1398 | source = "registry+https://github.com/rust-lang/crates.io-index" 1399 | checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" 1400 | dependencies = [ 1401 | "version_check", 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "unicode-ident" 1406 | version = "1.0.5" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" 1409 | 1410 | [[package]] 1411 | name = "unicode-segmentation" 1412 | version = "1.9.0" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" 1415 | 1416 | [[package]] 1417 | name = "unicode-width" 1418 | version = "0.1.9" 1419 | source = "registry+https://github.com/rust-lang/crates.io-index" 1420 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 1421 | 1422 | [[package]] 1423 | name = "unicode-xid" 1424 | version = "0.2.2" 1425 | source = "registry+https://github.com/rust-lang/crates.io-index" 1426 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 1427 | 1428 | [[package]] 1429 | name = "unreachable" 1430 | version = "1.0.0" 1431 | source = "registry+https://github.com/rust-lang/crates.io-index" 1432 | checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" 1433 | dependencies = [ 1434 | "void", 1435 | ] 1436 | 1437 | [[package]] 1438 | name = "vec_map" 1439 | version = "0.8.2" 1440 | source = "registry+https://github.com/rust-lang/crates.io-index" 1441 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 1442 | 1443 | [[package]] 1444 | name = "version_check" 1445 | version = "0.9.4" 1446 | source = "registry+https://github.com/rust-lang/crates.io-index" 1447 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1448 | 1449 | [[package]] 1450 | name = "void" 1451 | version = "1.0.2" 1452 | source = "registry+https://github.com/rust-lang/crates.io-index" 1453 | checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" 1454 | 1455 | [[package]] 1456 | name = "wasi" 1457 | version = "0.10.0+wasi-snapshot-preview1" 1458 | source = "registry+https://github.com/rust-lang/crates.io-index" 1459 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 1460 | 1461 | [[package]] 1462 | name = "wayland-client" 1463 | version = "0.27.0" 1464 | source = "registry+https://github.com/rust-lang/crates.io-index" 1465 | checksum = "ab702fefbcd6d6f67fb5816e3a89a3b5a42a94290abbc015311c9a30d1068ae4" 1466 | dependencies = [ 1467 | "bitflags", 1468 | "downcast-rs", 1469 | "libc", 1470 | "nix 0.17.0", 1471 | "wayland-commons 0.27.0", 1472 | "wayland-scanner 0.27.0", 1473 | "wayland-sys 0.27.0", 1474 | ] 1475 | 1476 | [[package]] 1477 | name = "wayland-client" 1478 | version = "0.29.4" 1479 | source = "registry+https://github.com/rust-lang/crates.io-index" 1480 | checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" 1481 | dependencies = [ 1482 | "bitflags", 1483 | "downcast-rs", 1484 | "libc", 1485 | "nix 0.22.3", 1486 | "wayland-commons 0.29.4", 1487 | "wayland-scanner 0.29.4", 1488 | "wayland-sys 0.29.4", 1489 | ] 1490 | 1491 | [[package]] 1492 | name = "wayland-commons" 1493 | version = "0.27.0" 1494 | source = "registry+https://github.com/rust-lang/crates.io-index" 1495 | checksum = "e972e9336ad5a9dd861b4e21ff35ad71d3e5c6b4803d65c39913612f851b95f1" 1496 | dependencies = [ 1497 | "nix 0.17.0", 1498 | "once_cell", 1499 | "smallvec", 1500 | "wayland-sys 0.27.0", 1501 | ] 1502 | 1503 | [[package]] 1504 | name = "wayland-commons" 1505 | version = "0.29.4" 1506 | source = "registry+https://github.com/rust-lang/crates.io-index" 1507 | checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" 1508 | dependencies = [ 1509 | "nix 0.22.3", 1510 | "once_cell", 1511 | "smallvec", 1512 | "wayland-sys 0.29.4", 1513 | ] 1514 | 1515 | [[package]] 1516 | name = "wayland-protocols" 1517 | version = "0.27.0" 1518 | source = "registry+https://github.com/rust-lang/crates.io-index" 1519 | checksum = "f3d6fc54b17b98b5083bc21ae3a30e6d75cb4b01647360e4c3a04648bcf8781d" 1520 | dependencies = [ 1521 | "bitflags", 1522 | "wayland-client 0.27.0", 1523 | "wayland-commons 0.27.0", 1524 | "wayland-scanner 0.27.0", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "wayland-protocols" 1529 | version = "0.29.4" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" 1532 | dependencies = [ 1533 | "bitflags", 1534 | "wayland-client 0.29.4", 1535 | "wayland-commons 0.29.4", 1536 | "wayland-scanner 0.29.4", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "wayland-scanner" 1541 | version = "0.27.0" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "030f56009d932bd9400bb472764fea8109be1b0fc482d9cd75496c943ac30328" 1544 | dependencies = [ 1545 | "proc-macro2", 1546 | "quote", 1547 | "xml-rs", 1548 | ] 1549 | 1550 | [[package]] 1551 | name = "wayland-scanner" 1552 | version = "0.29.4" 1553 | source = "registry+https://github.com/rust-lang/crates.io-index" 1554 | checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" 1555 | dependencies = [ 1556 | "proc-macro2", 1557 | "quote", 1558 | "xml-rs", 1559 | ] 1560 | 1561 | [[package]] 1562 | name = "wayland-sys" 1563 | version = "0.27.0" 1564 | source = "registry+https://github.com/rust-lang/crates.io-index" 1565 | checksum = "8bdeffbbb474477dfa2acb45ac7479e5fe8f741c64ab032c5d11b94d07edc269" 1566 | dependencies = [ 1567 | "pkg-config", 1568 | ] 1569 | 1570 | [[package]] 1571 | name = "wayland-sys" 1572 | version = "0.29.4" 1573 | source = "registry+https://github.com/rust-lang/crates.io-index" 1574 | checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" 1575 | dependencies = [ 1576 | "pkg-config", 1577 | ] 1578 | 1579 | [[package]] 1580 | name = "weezl" 1581 | version = "0.1.5" 1582 | source = "registry+https://github.com/rust-lang/crates.io-index" 1583 | checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e" 1584 | 1585 | [[package]] 1586 | name = "winapi" 1587 | version = "0.3.9" 1588 | source = "registry+https://github.com/rust-lang/crates.io-index" 1589 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1590 | dependencies = [ 1591 | "winapi-i686-pc-windows-gnu", 1592 | "winapi-x86_64-pc-windows-gnu", 1593 | ] 1594 | 1595 | [[package]] 1596 | name = "winapi-i686-pc-windows-gnu" 1597 | version = "0.4.0" 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" 1599 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1600 | 1601 | [[package]] 1602 | name = "winapi-util" 1603 | version = "0.1.5" 1604 | source = "registry+https://github.com/rust-lang/crates.io-index" 1605 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 1606 | dependencies = [ 1607 | "winapi", 1608 | ] 1609 | 1610 | [[package]] 1611 | name = "winapi-wsapoll" 1612 | version = "0.1.1" 1613 | source = "registry+https://github.com/rust-lang/crates.io-index" 1614 | checksum = "44c17110f57155602a80dca10be03852116403c9ff3cd25b079d666f2aa3df6e" 1615 | dependencies = [ 1616 | "winapi", 1617 | ] 1618 | 1619 | [[package]] 1620 | name = "winapi-x86_64-pc-windows-gnu" 1621 | version = "0.4.0" 1622 | source = "registry+https://github.com/rust-lang/crates.io-index" 1623 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1624 | 1625 | [[package]] 1626 | name = "windows-sys" 1627 | version = "0.34.0" 1628 | source = "registry+https://github.com/rust-lang/crates.io-index" 1629 | checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825" 1630 | dependencies = [ 1631 | "windows_aarch64_msvc 0.34.0", 1632 | "windows_i686_gnu 0.34.0", 1633 | "windows_i686_msvc 0.34.0", 1634 | "windows_x86_64_gnu 0.34.0", 1635 | "windows_x86_64_msvc 0.34.0", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "windows-sys" 1640 | version = "0.42.0" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 1643 | dependencies = [ 1644 | "windows_aarch64_gnullvm", 1645 | "windows_aarch64_msvc 0.42.0", 1646 | "windows_i686_gnu 0.42.0", 1647 | "windows_i686_msvc 0.42.0", 1648 | "windows_x86_64_gnu 0.42.0", 1649 | "windows_x86_64_gnullvm", 1650 | "windows_x86_64_msvc 0.42.0", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "windows_aarch64_gnullvm" 1655 | version = "0.42.0" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" 1658 | 1659 | [[package]] 1660 | name = "windows_aarch64_msvc" 1661 | version = "0.34.0" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d" 1664 | 1665 | [[package]] 1666 | name = "windows_aarch64_msvc" 1667 | version = "0.42.0" 1668 | source = "registry+https://github.com/rust-lang/crates.io-index" 1669 | checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" 1670 | 1671 | [[package]] 1672 | name = "windows_i686_gnu" 1673 | version = "0.34.0" 1674 | source = "registry+https://github.com/rust-lang/crates.io-index" 1675 | checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed" 1676 | 1677 | [[package]] 1678 | name = "windows_i686_gnu" 1679 | version = "0.42.0" 1680 | source = "registry+https://github.com/rust-lang/crates.io-index" 1681 | checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" 1682 | 1683 | [[package]] 1684 | name = "windows_i686_msvc" 1685 | version = "0.34.0" 1686 | source = "registry+https://github.com/rust-lang/crates.io-index" 1687 | checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956" 1688 | 1689 | [[package]] 1690 | name = "windows_i686_msvc" 1691 | version = "0.42.0" 1692 | source = "registry+https://github.com/rust-lang/crates.io-index" 1693 | checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" 1694 | 1695 | [[package]] 1696 | name = "windows_x86_64_gnu" 1697 | version = "0.34.0" 1698 | source = "registry+https://github.com/rust-lang/crates.io-index" 1699 | checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4" 1700 | 1701 | [[package]] 1702 | name = "windows_x86_64_gnu" 1703 | version = "0.42.0" 1704 | source = "registry+https://github.com/rust-lang/crates.io-index" 1705 | checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" 1706 | 1707 | [[package]] 1708 | name = "windows_x86_64_gnullvm" 1709 | version = "0.42.0" 1710 | source = "registry+https://github.com/rust-lang/crates.io-index" 1711 | checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" 1712 | 1713 | [[package]] 1714 | name = "windows_x86_64_msvc" 1715 | version = "0.34.0" 1716 | source = "registry+https://github.com/rust-lang/crates.io-index" 1717 | checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9" 1718 | 1719 | [[package]] 1720 | name = "windows_x86_64_msvc" 1721 | version = "0.42.0" 1722 | source = "registry+https://github.com/rust-lang/crates.io-index" 1723 | checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" 1724 | 1725 | [[package]] 1726 | name = "wl-clipboard-rs" 1727 | version = "0.4.1" 1728 | source = "registry+https://github.com/rust-lang/crates.io-index" 1729 | checksum = "23d00076f424455f5988a18c7d7433fc7f919d6f4c685f663439cf7b4c65a288" 1730 | dependencies = [ 1731 | "derive-new", 1732 | "derive_more", 1733 | "exitfailure", 1734 | "failure", 1735 | "libc", 1736 | "log", 1737 | "mime_guess", 1738 | "nix 0.18.0", 1739 | "os_pipe 0.9.2", 1740 | "stderrlog", 1741 | "structopt", 1742 | "tempfile", 1743 | "tree_magic", 1744 | "wayland-client 0.27.0", 1745 | "wayland-protocols 0.27.0", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "wl-clipboard-rs" 1750 | version = "0.7.0" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "981a303dfbb75d659f6612d05a14b2e363c103d24f676a2d44a00d18507a1ad9" 1753 | dependencies = [ 1754 | "derive-new", 1755 | "libc", 1756 | "log", 1757 | "nix 0.24.2", 1758 | "os_pipe 1.0.1", 1759 | "tempfile", 1760 | "thiserror", 1761 | "tree_magic_mini", 1762 | "wayland-client 0.29.4", 1763 | "wayland-protocols 0.29.4", 1764 | ] 1765 | 1766 | [[package]] 1767 | name = "x11-clipboard" 1768 | version = "0.5.3" 1769 | source = "registry+https://github.com/rust-lang/crates.io-index" 1770 | checksum = "473068b7b80ac86a18328824f1054e5e007898c47b5bbc281bd7abe32bc3653c" 1771 | dependencies = [ 1772 | "xcb", 1773 | ] 1774 | 1775 | [[package]] 1776 | name = "x11rb" 1777 | version = "0.9.0" 1778 | source = "registry+https://github.com/rust-lang/crates.io-index" 1779 | checksum = "6e99be55648b3ae2a52342f9a870c0e138709a3493261ce9b469afe6e4df6d8a" 1780 | dependencies = [ 1781 | "gethostname", 1782 | "nix 0.22.3", 1783 | "winapi", 1784 | "winapi-wsapoll", 1785 | ] 1786 | 1787 | [[package]] 1788 | name = "xcb" 1789 | version = "0.10.1" 1790 | source = "registry+https://github.com/rust-lang/crates.io-index" 1791 | checksum = "771e2b996df720cd1c6dd9ff90f62d91698fd3610cc078388d0564bdd6622a9c" 1792 | dependencies = [ 1793 | "libc", 1794 | "log", 1795 | "quick-xml", 1796 | ] 1797 | 1798 | [[package]] 1799 | name = "xml-rs" 1800 | version = "0.8.4" 1801 | source = "registry+https://github.com/rust-lang/crates.io-index" 1802 | checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" 1803 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "clipboard-sync" 3 | description = " Synchronizes the clipboard across multiple X11 and wayland instances running on the same machine. " 4 | version = "0.2.0" 5 | edition = "2021" 6 | license = "MIT OR Apache-2.0" 7 | 8 | [features] 9 | backtrace = [] 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | wayland-client = "0.29.4" 15 | wl-clipboard-rs = "0.7.0" 16 | cli-clipboard = "0.2.0" 17 | arboard = "2.0.0" 18 | x11-clipboard = "0.5.3" 19 | terminal-clipboard = "0.3.1" 20 | nix = "0.23.1" 21 | anyhow = "1.0.44" 22 | thiserror = "1.0" 23 | chrono = "0.4" 24 | itertools = "0.10" 25 | gag = "1.0" 26 | clap = { version = "4.0.29", features = ["derive", "wrap_help"] } 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | prefix := "/usr/local" 2 | bin := "bin" 3 | systemd := "lib/systemd" 4 | profile := "release" 5 | deb_version := $(shell grep Version control | sed 's/Version: *//g') 6 | 7 | build: 8 | cargo build --profile $(profile) 9 | 10 | install: 11 | install -Dm755 target/$(profile)/clipboard-sync "$(prefix)/$(bin)/clipboard-sync" 12 | install -Dm644 clipboard-sync.service "$(prefix)/$(systemd)/user/clipboard-sync.service" 13 | 14 | uninstall: 15 | rm -f "$(prefix)/$(bin)/clipboard-sync" 16 | rm -f "$(prefix)/$(systemd)/user/clipboard-sync.service" 17 | 18 | user-%: 19 | $(MAKE) $* prefix="${HOME}" bin=.bin systemd=.config/systemd 20 | 21 | deb: 22 | rm -rf dist/deb 23 | mkdir -p dist/deb/clipboard-sync_$(deb_version)/DEBIAN 24 | $(MAKE) install prefix=dist/deb/clipboard-sync_$(deb_version) 25 | cp control dist/deb/clipboard-sync_$(deb_version)/DEBIAN/control 26 | dpkg-deb --build dist/deb/clipboard-sync_$(deb_version) 27 | 28 | deblint: 29 | lintian dist/deb/clipboard-sync_$(deb_version).deb 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clipboard Sync 2 | 3 | Synchronizes the clipboard across multiple X11 and wayland instances running on the same machine. 4 | 5 | To use clipboard sync, you only need to install it and start the service. It identifies and synchronizes your clipboards automatically. 6 | 7 | Example use cases: 8 | 9 | - **Improve Wayland compatibility**: You have already enabled support for wayland in your system, but your computer does not synchronize the clipboard between X11 and wayland windows. clipboard-sync can solve this problem. [more details](https://github.com/dnut/clipboard-sync/issues/9#issuecomment-1502368133) 10 | - **VNC**: You run a VNC server and would like all host and client logins from the same user to share the same clipboard. 11 | - **Multiple displays**: You run two or more desktop environments or window managers in separate ttys, switching between desktops using ctrl-alt-F*. 12 | - **Nested Wayland**: You run a wayland compositor within a window. examples: 13 | - you primarily use kde, but run sway in a window to consolidate all your messenger apps into a single tiled/tabbed window. 14 | - you use gnome and develop extensions for gnome, so you run a nested gnome environment for testing. 15 | 16 | Table of Contents: 17 | - [Installation](#installation) 18 | - [Usage](#usage) 19 | - [Build From Source](#build-from-source) 20 | 21 | # Installation 22 | Install clipboard-sync with your system's package manager. If your system is not supported, please vote on [the appropriate issue](https://github.com/dnut/clipboard-sync/issues?q=is%3Aissue+label%3Adistribution), or create one if it does not exist. 23 | 24 | ### Arch Linux 25 | [clipboard-sync](https://aur.archlinux.org/packages/clipboard-sync) is available in the Arch User Repository. 26 | 27 | ### Ubuntu & Debian 28 | Install from the official repository: 29 | ```bash 30 | sudo wget -P /etc/apt/sources.list.d/ https://raw.githubusercontent.com/dnut/deb/master/dnut.list 31 | sudo apt update && sudo apt install clipboard-sync 32 | ``` 33 | 34 | ## Advanced Installation 35 | If your system is not supported, you have two other options: 36 | - [Generic Linux](#generic-linux) 37 | - automatically and cleanly installs or uninstalls the entire package. 38 | - requires extra steps to acquire the source code. 39 | - [Cargo](#cargo) 40 | - only installs the executable, which needs to be run manually. 41 | - requires extra steps to manually edit and install a systemd service if desired. 42 | - If you use [cargo-update](https://crates.io/crates/cargo-update), it can make updates to the executable easier. 43 | 44 | ### Generic Linux 45 | [Build from Source](#build-from-source), then install ***either*** system-wide ***or*** for only the current user: 46 | ```bash 47 | sudo make install # system 48 | make user-install # user 49 | ``` 50 | It can be easily uninstalled: 51 | ```bash 52 | sudo make uninstall # system 53 | make user-uninstall # user 54 | ``` 55 | 56 | ### Cargo 57 | [clipboard-sync](https://crates.io/crates/clipboard-sync) is published to crates.io, so it can be installed as a normal binary crate. 58 | 59 | 1. Install rust: https://www.rust-lang.org/tools/install 60 | 2. Install clipboard-sync 61 | ```bash 62 | cargo install clipboard-sync 63 | ``` 64 | 3. If you want it to run in the background, you can use tmux, or you can manually download the systemd service file and copy it to a systemd folder. You can download it to the correct path using this command, after which you may need to manually edit the file to point the correct binary location: 65 | ```bash 66 | wget -P "$HOME/.config/systemd/user/" https://raw.githubusercontent.com/dnut/clipboard-sync/master/clipboard-sync.service 67 | ``` 68 | It can be easily uninstalled: 69 | ```bash 70 | cargo uninstall clipboard-sync 71 | rm -r "$HOME/.config/systemd/clipboard-sync.service" 72 | ``` 73 | 74 | ### Ubuntu & Debian (advanced) 75 | In addition to installing from the official repository, you can also build and install the deb package from source. Follow the instructions to [Build from Source](#build-from-source), then create a deb file and install it with: 76 | ```bash 77 | make deb && sudo apt install ./dist/deb/clipboard-sync_*.deb 78 | ``` 79 | 80 | ### NixOS 81 | Add this repo to your flake inputs: 82 | ```nix 83 | clipboard-sync.url = "github:dnut/clipboard-sync"; 84 | ``` 85 | 86 | Put `clipboard-sync.nixosModules.default` into flake modules. 87 | 88 | To enable the systemd service, add `services.clipboard-sync.enable = true;` into the `configuration.nix`. 89 | 90 | # Usage 91 | The typical set-and-forget approach is to enable to service: 92 | ```bash 93 | systemctl --user enable --now clipboard-sync 94 | ``` 95 | 96 | If you don't want it to run constantly, only on-demand, don't use systemd. Directly call the binary as needed: 97 | ```bash 98 | clipboard-sync 99 | ``` 100 | 101 | You can also daemonize clipboard-sync using tmux instead of systemd. ~/.bashrc aliases may be handy for these commands. 102 | ```bash 103 | tmux new-session -ds clipboard-sync clipboard-sync # start in background 104 | tmux attach -t clipboard-sync # view status 105 | ctrl-b, d # while viewing status, send back to background 106 | ctrl-c # while viewing status, terminate the process 107 | ``` 108 | 109 | # Build from Source 110 | 111 | 1. Ensure you have the build dependencies: rust make gcc libc libxcb 112 | - install rust using rustup: https://www.rust-lang.org/tools/install 113 | - For the rest: 114 | - arch linux: `sudo pacman -Syu make gcc libxcb` 115 | - debian/ubuntu: `sudo apt install make gcc libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev` 116 | 117 | 2. Download the source from [the releases page](https://github.com/dnut/clipboard-sync/releases/) and unzip it. Alternatively, select *one* of these commands to download the source: 118 | ```bash 119 | wget -O- https://github.com/dnut/clipboard-sync/archive/refs/tags/0.2.0.tar.gz | tar xvz 120 | curl -L https://github.com/dnut/clipboard-sync/archive/refs/tags/0.2.0.tar.gz | tar xvz 121 | git clone https://github.com/dnut/clipboard-sync.git --branch stable 122 | ``` 123 | 124 | 3. Compile the program 125 | ```bash 126 | cd clipboard-sync* 127 | make 128 | ``` 129 | 130 | The executable is here: 131 | ```bash 132 | ./target/release/clipboard-sync 133 | ``` 134 | -------------------------------------------------------------------------------- /clipboard-sync.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Synchronize clipboards across all displays 3 | Documentation=https://github.com/dnut/clipboard-sync/ 4 | PartOf=graphical-session.target 5 | After=graphical-session.target 6 | Requisite=graphical-session.target 7 | 8 | [Service] 9 | ExecStart=/usr/bin/env clipboard-sync --hide-timestamp --log-level debug 10 | Restart=on-failure 11 | 12 | [Install] 13 | WantedBy=graphical-session.target 14 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: clipboard-sync 2 | Version: 0.2.0-1 3 | Section: clipboard 4 | Priority: optional 5 | Architecture: amd64 6 | Depends: libxcb1, libxcb-render0, libxcb-shape0, libxcb-xfixes0 7 | Maintainer: Drew Nutter 8 | Description: Synchronizes the clipboard across multiple X11 and wayland instances running on the same machine. 9 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | { 2 | pkgs ? import { }, 3 | }: 4 | pkgs.rustPlatform.buildRustPackage rec { 5 | buildInputs = with pkgs; [ xorg.libxcb ]; 6 | 7 | pname = "clipboard-sync"; 8 | version = "0.2.0"; 9 | cargoLock.lockFile = ./Cargo.lock; 10 | src = ./.; 11 | } 12 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1717196966, 6 | "narHash": "sha256-yZKhxVIKd2lsbOqYd5iDoUIwsRZFqE87smE2Vzf6Ck0=", 7 | "owner": "nixos", 8 | "repo": "nixpkgs", 9 | "rev": "57610d2f8f0937f39dbd72251e9614b1561942d8", 10 | "type": "github" 11 | }, 12 | "original": { 13 | "owner": "nixos", 14 | "ref": "nixos-unstable", 15 | "repo": "nixpkgs", 16 | "type": "github" 17 | } 18 | }, 19 | "root": { 20 | "inputs": { 21 | "nixpkgs": "nixpkgs" 22 | } 23 | } 24 | }, 25 | "root": "root", 26 | "version": 7 27 | } 28 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "Synchronizes the clipboard across multiple X11 and wayland instances running on the same machine"; 3 | inputs = { 4 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; 5 | }; 6 | outputs = 7 | { self, nixpkgs }: 8 | let 9 | supportedSystems = [ "x86_64-linux" ]; 10 | forAllSystems = nixpkgs.lib.genAttrs supportedSystems; 11 | pkgsFor = nixpkgs.legacyPackages; 12 | in 13 | { 14 | formatter.x86_64-linux = nixpkgs.legacyPackages.x86_64-linux.nixfmt-rfc-style; 15 | packages = forAllSystems (system: { 16 | default = pkgsFor.${system}.callPackage ./. { }; 17 | }); 18 | 19 | nixosModules.default = 20 | # For illustration, probably want to break this definition out to a separate file 21 | { 22 | config, 23 | pkgs, 24 | lib, 25 | ... 26 | }: 27 | { 28 | options = { 29 | services.clipboard-sync.enable = lib.mkEnableOption "clipboard-sync"; 30 | }; 31 | 32 | config = lib.mkIf config.services.clipboard-sync.enable { 33 | systemd.user.services.clipboard-sync = { 34 | description = "Synchronize clipboards across all displays"; 35 | documentation = [ "https://github.com/dnut/clipboard-sync/" ]; 36 | wantedBy = [ "graphical-session.target" ]; 37 | after = [ "graphical-session.target" ]; 38 | partOf = [ "graphical-session.target" ]; 39 | requisite = [ "graphical-session.target" ]; 40 | serviceConfig.ExecStart = "/usr/bin/env ${ 41 | self.packages.${pkgs.system}.default 42 | }/bin/clipboard-sync --hide-timestamp --log-level debug"; 43 | serviceConfig.Restart = "on-failure"; 44 | }; 45 | }; 46 | }; 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /src/clipboard.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::rc::Rc; 3 | use std::{env, io::Read, process::Command}; 4 | use terminal_clipboard::Clipboard as TerminalClipboard; 5 | use wl_clipboard_rs::copy::{MimeType as CopyMimeType, Options, Source}; 6 | use wl_clipboard_rs::paste::{ 7 | get_contents, ClipboardType, Error as PasteError, MimeType as PasteMimeType, Seat, 8 | }; 9 | 10 | use crate::error::{Generify, MyResult, Standardize}; 11 | 12 | pub trait Clipboard: std::fmt::Debug { 13 | fn display(&self) -> String; 14 | 15 | fn get(&self) -> MyResult; 16 | 17 | fn set(&self, value: &str) -> MyResult<()>; 18 | 19 | fn should_poll(&self) -> bool { 20 | true 21 | } 22 | 23 | fn rank(&self) -> u8 { 24 | 100 25 | } 26 | } 27 | 28 | impl Clipboard for Box { 29 | fn get(&self) -> MyResult { 30 | (**self).get() 31 | } 32 | 33 | fn set(&self, value: &str) -> MyResult<()> { 34 | (**self).set(value) 35 | } 36 | 37 | fn display(&self) -> String { 38 | (**self).display() 39 | } 40 | 41 | fn should_poll(&self) -> bool { 42 | (**self).should_poll() 43 | } 44 | 45 | fn rank(&self) -> u8 { 46 | (**self).rank() 47 | } 48 | } 49 | 50 | #[derive(Debug)] 51 | pub struct WlrClipboard { 52 | pub display: String, 53 | } 54 | 55 | impl Clipboard for WlrClipboard { 56 | fn display(&self) -> String { 57 | self.display.clone() 58 | } 59 | 60 | fn get(&self) -> MyResult { 61 | env::set_var("WAYLAND_DISPLAY", self.display.clone()); 62 | let result = get_contents( 63 | ClipboardType::Regular, 64 | Seat::Unspecified, 65 | PasteMimeType::Text, 66 | ); 67 | 68 | match result { 69 | Ok((mut pipe, _)) => { 70 | let mut contents = vec![]; 71 | pipe.read_to_end(&mut contents)?; 72 | Ok(String::from_utf8_lossy(&contents).to_string()) 73 | } 74 | 75 | Err(PasteError::NoSeats) 76 | | Err(PasteError::ClipboardEmpty) 77 | | Err(PasteError::NoMimeType) => Ok("".to_string()), 78 | 79 | Err(err) => Err(err)?, 80 | } 81 | } 82 | 83 | fn set(&self, value: &str) -> MyResult<()> { 84 | env::set_var("WAYLAND_DISPLAY", self.display.clone()); 85 | let opts = Options::new(); 86 | let result = std::panic::catch_unwind(|| { 87 | opts.copy( 88 | Source::Bytes(value.to_string().into_bytes().into()), 89 | CopyMimeType::Text, 90 | ) 91 | }); 92 | 93 | Ok(result.standardize().generify()??) 94 | } 95 | 96 | fn rank(&self) -> u8 { 97 | 10 98 | } 99 | } 100 | 101 | #[derive(Debug)] 102 | pub struct WlCommandClipboard { 103 | pub display: String, 104 | } 105 | 106 | impl Clipboard for WlCommandClipboard { 107 | fn display(&self) -> String { 108 | self.display.clone() 109 | } 110 | 111 | fn get(&self) -> MyResult { 112 | let out = Command::new("wl-paste") 113 | .env("WAYLAND_DISPLAY", &self.display) 114 | .output()? 115 | .stdout; 116 | Ok(String::from_utf8_lossy(&out).trim().to_string()) 117 | } 118 | 119 | fn set(&self, value: &str) -> MyResult<()> { 120 | Command::new("wl-copy") 121 | .arg(value) 122 | .env("WAYLAND_DISPLAY", &self.display) 123 | .spawn()?; 124 | Ok(()) 125 | } 126 | 127 | fn should_poll(&self) -> bool { 128 | false 129 | } 130 | 131 | fn rank(&self) -> u8 { 132 | 200 133 | } 134 | } 135 | 136 | #[derive(Debug)] 137 | pub struct ArClipboard { 138 | display: String, 139 | } 140 | 141 | impl Clipboard for ArClipboard { 142 | fn display(&self) -> String { 143 | self.display.clone() 144 | } 145 | 146 | fn get(&self) -> MyResult { 147 | env::set_var("WAYLAND_DISPLAY", self.display.clone()); 148 | let mut clipboard = arboard::Clipboard::new()?; 149 | Ok(clipboard.get_text().unwrap_or_default()) 150 | } 151 | 152 | fn set(&self, value: &str) -> MyResult<()> { 153 | env::set_var("WAYLAND_DISPLAY", self.display.clone()); 154 | let mut clipboard = arboard::Clipboard::new()?; 155 | clipboard.set_text(value.into())?; 156 | 157 | Ok(()) 158 | } 159 | } 160 | 161 | pub struct X11Clipboard { 162 | display: String, 163 | backend: X11Backend, 164 | } 165 | 166 | #[derive(Clone)] 167 | pub struct X11Backend(Rc>); 168 | impl X11Backend { 169 | /// try to only call this once because repeated initializations may not work. 170 | /// i started seeing timeouts/errors after 4 171 | pub fn new(display: &str) -> MyResult { 172 | // let backend = stdio! { terminal_clipboard::X11Clipboard::new() }.standardize()?; 173 | env::set_var("DISPLAY", display); 174 | let backend = terminal_clipboard::X11Clipboard::new().standardize()?; 175 | 176 | Ok(Self(Rc::new(RefCell::new(backend)))) 177 | } 178 | } 179 | 180 | impl std::fmt::Debug for X11Clipboard { 181 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 182 | f.debug_struct("X11Clipboard") 183 | .field("display", &self.display) 184 | .finish() 185 | } 186 | } 187 | 188 | impl X11Clipboard { 189 | pub fn new(display: String) -> MyResult { 190 | Ok(Self { 191 | backend: X11Backend::new(&display)?, 192 | display, 193 | }) 194 | } 195 | 196 | // fn backend(&self) -> X11Backend { 197 | // X11Backend::new_str(&self.display).unwrap() 198 | // } 199 | } 200 | 201 | impl Clipboard for X11Clipboard { 202 | fn display(&self) -> String { 203 | self.display.clone() 204 | } 205 | 206 | fn get(&self) -> MyResult { 207 | Ok(self 208 | .backend 209 | .0 210 | .try_borrow()? 211 | .get_string() 212 | .unwrap_or_default()) 213 | } 214 | 215 | fn set(&self, value: &str) -> MyResult<()> { 216 | self.backend 217 | .0 218 | .try_borrow_mut()? 219 | .set_string(value) 220 | .standardize()?; 221 | 222 | Ok(()) 223 | } 224 | } 225 | 226 | #[derive(Debug)] 227 | pub struct HybridClipboard { 228 | getter: G, 229 | setter: S, 230 | } 231 | 232 | // impl HybridClipboard { 233 | // fn gnome(n: u8) -> MyResult { 234 | // Ok(Self { 235 | // getter: X11Clipboard::new(format!(":{}", n))?, 236 | // setter: CommandClipboard { 237 | // display: format!("wayland-{}", n), 238 | // }, 239 | // }) 240 | // } 241 | // } 242 | 243 | impl Clipboard for HybridClipboard { 244 | fn display(&self) -> String { 245 | self.getter.display() 246 | } 247 | 248 | fn get(&self) -> MyResult { 249 | self.getter.get() 250 | } 251 | 252 | fn set(&self, value: &str) -> MyResult<()> { 253 | self.setter.set(value) 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::cell::{BorrowError, BorrowMutError}; 2 | use std::error::Error as StdError; 3 | use std::fmt::{Debug, Display}; 4 | 5 | pub type MyResult = Result; 6 | 7 | #[derive(thiserror::Error, Debug)] 8 | pub enum MyError { 9 | /// Only use this catchall to deal with unexpected errors that are very 10 | /// difficult to deal with in another way. 11 | #[error("Unknown error: {0}")] 12 | Generic(#[from] Box), 13 | 14 | /// High level error to represent the idea that the application is crashing, 15 | /// indicating the cause(s) of that crash. 16 | #[error("Application crash '{msg}': {cause:#?}")] 17 | Crash { msg: String, cause: Vec }, 18 | 19 | #[error("failed to get wlr clipboard: {0}")] 20 | WlcrsPaste(#[from] wl_clipboard_rs::paste::Error), 21 | 22 | #[error("failed to set wlr clipboard: {0}")] 23 | WlcrsCopy(#[from] wl_clipboard_rs::copy::Error), 24 | 25 | #[error("{0}")] 26 | Io(#[from] std::io::Error), 27 | 28 | #[error("{0}")] 29 | TerminalClipboard(#[from] StandardizedError), 30 | 31 | #[error("{0}")] 32 | Arboard(#[from] arboard::Error), 33 | 34 | #[error("No clipboards.")] 35 | NoClipboards, 36 | 37 | #[error("{0}")] 38 | BorrowError(#[from] BorrowError), 39 | 40 | #[error("{0}")] 41 | BorrowMutError(#[from] BorrowMutError), 42 | } 43 | 44 | #[derive(Debug)] 45 | pub struct StandardizedError { 46 | pub inner: E, 47 | pub stdio: Option, 48 | } 49 | 50 | impl Display for StandardizedError { 51 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 52 | Debug::fmt(self, f) 53 | } 54 | } 55 | 56 | impl StdError for StandardizedError {} 57 | 58 | pub trait Standardize: Sized { 59 | /// Convert any Sized + Debug into a std::error::Error 60 | fn standardize(self) -> Result>; 61 | } 62 | 63 | impl Standardize for Result { 64 | fn standardize(self) -> Result> { 65 | match self { 66 | Ok(ok) => Ok(ok), 67 | Err(err) => Err(StandardizedError { 68 | inner: err, 69 | stdio: None, 70 | }), 71 | } 72 | } 73 | } 74 | 75 | impl Standardize for (Result, StdIo) { 76 | fn standardize(self) -> Result> { 77 | match self.0 { 78 | Ok(ok) => Ok(ok), 79 | Err(err) => Err(StandardizedError { 80 | inner: err, 81 | stdio: Some(self.1), 82 | }), 83 | } 84 | } 85 | } 86 | 87 | pub trait Generify { 88 | /// Convert any std::error::Error into MyError::Generic 89 | fn generify(self) -> Result; 90 | } 91 | 92 | impl Generify for Result { 93 | fn generify(self) -> Result { 94 | match self { 95 | Ok(ok) => Ok(ok), 96 | Err(err) => Err(MyError::Generic(Box::new(err))), 97 | } 98 | } 99 | } 100 | 101 | #[allow(unused)] 102 | macro_rules! stdio { 103 | ($block:expr) => {{ 104 | let mut stdout_redirect = gag::BufferRedirect::stdout().unwrap(); 105 | let mut stderr_redirect = gag::BufferRedirect::stderr().unwrap(); 106 | let ret = $block; 107 | let mut stdout = String::new(); 108 | let mut stderr = String::new(); 109 | stdout_redirect.read_to_string(&mut stdout).unwrap(); 110 | stderr_redirect.read_to_string(&mut stderr).unwrap(); 111 | (ret, crate::error::StdIo { stdout, stderr }) 112 | }}; 113 | } 114 | #[allow(unused)] 115 | pub(crate) use stdio; 116 | 117 | #[derive(Debug, Default, PartialEq, Eq)] 118 | pub struct StdIo { 119 | pub stdout: String, 120 | pub stderr: String, 121 | } 122 | 123 | impl StdIo { 124 | #[allow(unused)] 125 | pub fn extend(&mut self, other: StdIo) { 126 | self.stdout = format!("{}{}", self.stdout, other.stdout); 127 | self.stderr = format!("{}{}", self.stderr, other.stderr); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | mustatex! { 2 | pub(crate) level: Level = Level::Debug; 3 | pub(crate) timestamp: bool = true; 4 | pub(crate) log_sensitive_information: bool = false; 5 | } 6 | 7 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)] 8 | pub enum Level { 9 | Fatal, 10 | Error, 11 | Warn, 12 | Info, 13 | Debug, 14 | Trace, 15 | } 16 | 17 | impl Default for Level { 18 | fn default() -> Self { 19 | #[cfg(debug_assertions)] 20 | return Level::Debug; 21 | #[cfg(not(debug_assertions))] 22 | Level::Info 23 | } 24 | } 25 | 26 | /// This is for logging of sensitive information that you usually don't want to 27 | /// log in production, regardless of log level. It won't log anything unless 28 | /// `sensitive` is set to true. 29 | /// In this crate, it's used to log clipboard contents. 30 | macro_rules! sensitive { 31 | ($log_macro:path, $($arg:tt)*) => { 32 | if *crate::log::log_sensitive_information::get() { 33 | $log_macro!("Sensitive: {}", format!($($arg)*)) 34 | } 35 | }; 36 | } 37 | pub(crate) use sensitive; 38 | 39 | #[allow(unused)] 40 | macro_rules! trace { 41 | ($($arg:tt)*) => { 42 | if *crate::log::level::get() >= crate::log::Level::Trace { 43 | crate::log::_log!(println, "TRACE", $($arg)*) 44 | } 45 | }; 46 | } 47 | #[allow(unused)] 48 | pub(crate) use trace; 49 | 50 | macro_rules! debug { 51 | ($($arg:tt)*) => { 52 | if *crate::log::level::get() >= crate::log::Level::Debug { 53 | crate::log::_log!(println, "DEBUG", $($arg)*) 54 | } 55 | }; 56 | } 57 | pub(crate) use debug; 58 | 59 | macro_rules! info { 60 | ($($arg:tt)*) => { 61 | if *crate::log::level::get() >= crate::log::Level::Info { 62 | crate::log::_log!(println, " INFO", $($arg)*) 63 | } 64 | }; 65 | } 66 | pub(crate) use info; 67 | 68 | macro_rules! warning { 69 | ($($arg:tt)*) => { 70 | if *crate::log::level::get() >= crate::log::Level::Warn { 71 | crate::log::_log!(eprintln, " WARN", $($arg)*) 72 | } 73 | }; 74 | } 75 | pub(crate) use warning; 76 | 77 | macro_rules! error { 78 | ($($arg:tt)*) => { 79 | if *crate::log::level::get() >= crate::log::Level::Error { 80 | crate::log::_log!(eprintln, "ERROR", $($arg)*) 81 | } 82 | }; 83 | } 84 | pub(crate) use error; 85 | 86 | macro_rules! fatal { 87 | ($($arg:tt)*) => { 88 | if *crate::log::level::get() >= crate::log::Level::Fatal { 89 | crate::log::_log!(eprintln, "FATAL", $($arg)*) 90 | } 91 | }; 92 | } 93 | pub(crate) use fatal; 94 | 95 | macro_rules! _log { 96 | ($print:ident, $level:literal, $($arg:tt)*) => {{ 97 | let s = format!($($arg)*); 98 | if *crate::log::timestamp::get() { 99 | $print!("{} - {} - {}", Local::now().format("%Y-%m-%d %H:%M:%S"), $level, s); 100 | } else { 101 | $print!("{} - {}", $level, s); 102 | } 103 | }}; 104 | } 105 | pub(crate) use _log; 106 | 107 | #[allow(unused)] 108 | macro_rules! truncate_to_debug { 109 | ($logger:path, $($arg:tt)*) => { 110 | let s = format!($($arg)*); 111 | let s = if s.len() > 254 && !crate::log::DEBUG { 112 | format!("{}{}", crate::log::truncate(&s, 200), "... message truncated, enable debug for entire message") 113 | } else { 114 | s 115 | }; 116 | $logger!("{}", s); 117 | }; 118 | } 119 | #[allow(unused)] 120 | pub(crate) use truncate_to_debug; 121 | 122 | use crate::mustatex::mustatex; 123 | 124 | #[allow(unused)] 125 | pub fn truncate(s: &str, max_chars: usize) -> &str { 126 | match s.char_indices().nth(max_chars) { 127 | None => s, 128 | Some((idx, _)) => &s[..idx], 129 | } 130 | } 131 | 132 | pub fn concise_numbers(ns: &[u8]) -> String { 133 | if ns.is_empty() { 134 | return "[]".to_string(); 135 | } 136 | if ns.len() == 1 { 137 | return format!("[{}]", ns[0]); 138 | } 139 | let mut range_size = 1; 140 | let mut strings = vec![]; 141 | for nn in ns.windows(2) { 142 | let [n1, n2]: [u8] = *nn else {unreachable!()}; 143 | if n1 + 1 == n2 { 144 | range_size += 1; 145 | } else { 146 | range_size = 1; 147 | } 148 | if range_size < 3 { 149 | strings.push(format!("{n1}")); 150 | } 151 | if range_size == 3 { 152 | strings.push("..".to_string()); 153 | } 154 | } 155 | strings.push(format!("{}", ns[ns.len() - 1])); 156 | strings.push("..".to_owned()); 157 | let mut full_strings = vec![]; 158 | for ss in strings.windows(2) { 159 | let [s1, s2] = ss else {unreachable!()}; 160 | full_strings.push(s1.to_owned()); 161 | if s1 != ".." && s2 != ".." { 162 | full_strings.push(", ".to_owned()); 163 | } 164 | } 165 | format!("[{}]", full_strings.join("")) 166 | } 167 | 168 | #[test] 169 | fn test() { 170 | assert_eq!("[]", concise_numbers(&[])); 171 | assert_eq!("[123]", concise_numbers(&[123])); 172 | assert_eq!("[1..5]", concise_numbers(&[1, 2, 3, 4, 5])); 173 | assert_eq!( 174 | "[0, 2..4, 6..8, 10, 11]", 175 | concise_numbers(&[0, 2, 3, 4, 6, 7, 8, 10, 11]) 176 | ); 177 | assert_eq!( 178 | "[0, 2..4, 6..8, 10..12]", 179 | concise_numbers(&[0, 2, 3, 4, 6, 7, 8, 10, 11, 12]) 180 | ); 181 | assert_eq!( 182 | "[0, 2..4, 6..8, 10]", 183 | concise_numbers(&[0, 2, 3, 4, 6, 7, 8, 10]) 184 | ); 185 | assert_eq!( 186 | "[0..4, 6..8, 10]", 187 | concise_numbers(&[0, 1, 2, 3, 4, 6, 7, 8, 10]) 188 | ); 189 | assert_eq!( 190 | "[0, 1, 3, 4, 6..8, 10]", 191 | concise_numbers(&[0, 1, 3, 4, 6, 7, 8, 10]) 192 | ); 193 | } 194 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use chrono::Local; 2 | use clap::Parser; 3 | use error::MyResult; 4 | use nix::sys::signal::{self, Signal}; 5 | use nix::sys::wait::{WaitPidFlag, WaitStatus}; 6 | use nix::unistd::{fork, Pid}; 7 | use nix::{sys::wait::waitpid, unistd::ForkResult}; 8 | use std::f64::consts::E; 9 | use std::thread; 10 | use std::time::SystemTime; 11 | use std::{thread::sleep, time::Duration}; 12 | 13 | use crate::error::MyError; 14 | 15 | mod clipboard; 16 | mod error; 17 | mod log; 18 | mod mustatex; 19 | mod sync; 20 | 21 | fn main() { 22 | let args = Args::parse(); 23 | configure_logging(&args); 24 | if args.run_forked { 25 | run_forked() 26 | } else { 27 | run() 28 | } 29 | } 30 | 31 | /// cli arguments 32 | #[derive(Parser, Debug)] 33 | #[command(author, version, about, long_about = None, max_term_width = 120)] 34 | struct Args { 35 | /// granularity to log 36 | #[arg(long, value_enum, default_value_t = log::Level::default())] 37 | log_level: log::Level, 38 | 39 | /// whether to include timestamps in the logs (systemd already includes 40 | /// timestamps so you'll want to enable this for systemd) 41 | #[arg(long)] 42 | hide_timestamp: bool, 43 | 44 | /// whether to run the sync forked so the state can be cleaned up 45 | /// periodically. typically, this should be true. 46 | #[arg(long)] 47 | #[cfg_attr(not(debug_assertions), arg(default_value_t = true))] 48 | run_forked: bool, 49 | 50 | /// when debug logging is enabled, it won't show clipboard contents, because 51 | /// clipboard contents are sensitive user information. but if you set this 52 | /// to true, in addition to enabling debug logging, then it will log the 53 | /// clipboard contents. 54 | #[arg(long)] 55 | log_clipboard_contents: bool, 56 | } 57 | 58 | fn configure_logging(args: &Args) { 59 | log::level::set(args.log_level); 60 | log::timestamp::set(!args.hide_timestamp); 61 | log::log_sensitive_information::set(args.log_clipboard_contents); 62 | } 63 | 64 | #[allow(dead_code)] 65 | fn run_forked() { 66 | log::info!("started clipboard sync manager"); 67 | let mut panics = 0; 68 | loop { 69 | match unsafe { fork() }.expect("Failed to fork") { 70 | ForkResult::Parent { child } => { 71 | log::debug!("child process {child} successfully initialized."); 72 | kill_after(child, 600); 73 | let status = waitpid(Some(child), None) 74 | .expect("there was a problem managing the child process, so the service is exiting. check that pid {child} is not running before restarting this service"); 75 | log::debug!("child process {child} completed with: {status:?}"); 76 | if let WaitStatus::Exited(_, 101) = status { 77 | panics += 1; 78 | if panics < 4 { 79 | log::fatal!("child process {child} panicked. giving it another try"); 80 | } else { 81 | panic!("child process {child} panicked too many times."); 82 | } 83 | } 84 | sleep(Duration::from_secs(1)); 85 | } 86 | ForkResult::Child => run(), 87 | } 88 | } 89 | } 90 | 91 | fn run() { 92 | log::info!("starting clipboard sync"); 93 | loop_with_error_pain_management( 94 | sync::get_clipboards().unwrap(), 95 | |cb| sync::keep_synced(cb), 96 | |_| sync::get_clipboards().unwrap(), 97 | ) 98 | .unwrap(); 99 | } 100 | 101 | pub fn kill_after(pid: Pid, seconds: u64) { 102 | thread::spawn(move || { 103 | log::debug!("waiting {seconds} seconds and then killing {pid}."); 104 | thread::sleep(Duration::from_secs(seconds)); 105 | match waitpid(Some(pid), Some(WaitPidFlag::WNOHANG)) { 106 | Ok(WaitStatus::StillAlive) => log::debug!("child {pid} is still alive, as expected."), 107 | Ok(ok) => { 108 | log::warning!("expected child process {pid} to be StillAlive but got: {ok:?}") 109 | } 110 | Err(e) => log::error!("error getting status of child process {pid}: {e}"), 111 | } 112 | log::debug!("routinely attempting to kill child process {pid}."); 113 | if let Err(e) = signal::kill(pid, Signal::SIGTERM) { 114 | log::error!("error killing child process {pid}: {e}") 115 | } 116 | }); 117 | } 118 | 119 | /// Execute an action with a sophisticated retry mechanism 120 | /// If the action fails: 121 | /// - 1. run a recovery step to manipulate the input 122 | /// - 2. attempt to execute the action again 123 | /// If the action fails too frequently, exit 124 | fn loop_with_error_pain_management< 125 | Input, 126 | Return, 127 | Action: Fn(&Input) -> MyResult, 128 | Recovery: Fn(Input) -> Input, 129 | >( 130 | // data passed into action and reset by recovery 131 | initial_input: Input, 132 | // action to attempt on every iteration 133 | action: Action, 134 | // action to attempt on every error - errors here are not yet handled. you can panic if necessary 135 | recovery: Recovery, 136 | ) -> MyResult { 137 | let mut input = initial_input; 138 | let mut error_times = vec![]; 139 | let mut errors = vec![]; 140 | loop { 141 | match action(&input) { 142 | Ok(ret) => return Ok(ret), 143 | Err(err) => { 144 | log::fatal!("action exited with error: {:?}", err); 145 | let now = SystemTime::now(); 146 | error_times.push(now); 147 | errors.push(err); 148 | if total_pain(now, error_times.clone()) > 5.0 { 149 | return Err(MyError::Crash { 150 | msg: "too many errors, exiting".to_string(), 151 | cause: errors, 152 | }); 153 | } 154 | input = recovery(input); 155 | sleep(Duration::from_millis(1000)); 156 | } 157 | } 158 | log::info!("retrying"); 159 | } 160 | } 161 | 162 | /// Sum the pain of numerous painful events, measured by how long ago they 163 | /// happened. 164 | fn total_pain(now: SystemTime, errors: Vec) -> f64 { 165 | errors 166 | .into_iter() 167 | .map(|et| remaining_pain(now.duration_since(et).unwrap().as_secs())) 168 | .sum() 169 | } 170 | 171 | /// Looks at a painful event and determines how much of its pain is left. 172 | /// calculated as exponential decay with a half-life of 1 minute. 173 | fn remaining_pain(seconds_ago: u64) -> f64 { 174 | E.powf(-(seconds_ago as f64) / 86.561_702_453_337_8) 175 | } 176 | -------------------------------------------------------------------------------- /src/mustatex.rs: -------------------------------------------------------------------------------- 1 | /// A static variable that can be safely mutated, using a Mutex. 2 | /// Accessible with get/get_mut/set functions. 3 | macro_rules! mustatex { 4 | ($($viz:vis $name:ident: $Type:ty = $init:expr;)*) => { 5 | $( 6 | #[allow(unused)] 7 | $viz mod $name { 8 | use super::*; 9 | 10 | static mut INNER: std::sync::Mutex<$Type> = std::sync::Mutex::new($init); 11 | 12 | pub fn set(x: $Type) { 13 | unsafe { 14 | *INNER.lock().unwrap() = x; 15 | } 16 | } 17 | 18 | pub fn get() -> impl std::ops::Deref { 19 | unsafe { INNER.lock().unwrap() } 20 | } 21 | 22 | pub fn get_mut() -> impl std::ops::DerefMut { 23 | unsafe { INNER.lock().unwrap() } 24 | } 25 | } 26 | )* 27 | }; 28 | } 29 | pub(crate) use mustatex; 30 | -------------------------------------------------------------------------------- /src/sync.rs: -------------------------------------------------------------------------------- 1 | use chrono::Local; 2 | use std::collections::HashSet; 3 | use std::{thread::sleep, time::Duration}; 4 | use wayland_client::ConnectError; 5 | use wl_clipboard_rs::paste::Error as PasteError; 6 | 7 | use crate::clipboard::*; 8 | use crate::error::{MyError, MyResult, StandardizedError}; 9 | use crate::log::{self, concise_numbers}; 10 | 11 | pub fn get_clipboards() -> MyResult>> { 12 | log::debug!("identifying unique clipboards..."); 13 | let mut clipboards = get_clipboards_spec(get_wayland); 14 | // let x11_backend = X11Backend::new()?; 15 | clipboards.extend(get_clipboards_spec(get_x11)); 16 | 17 | let start = clipboards 18 | .iter() 19 | .map(|c| c.get().unwrap_or_default()) 20 | .find(|s| !s.is_empty()) 21 | .unwrap_or_default(); 22 | log::sensitive!(log::info, "Clipboard contents at the start: '{start}'"); 23 | 24 | let mut remove_me = HashSet::new(); 25 | let len = clipboards.len(); 26 | for i in 0..clipboards.len() { 27 | if !remove_me.contains(&i) { 28 | let cb1 = &clipboards[i]; 29 | for (j, cb2) in clipboards.iter().enumerate().take(len).skip(i + 1) { 30 | if are_same(&**cb1, &**cb2)? { 31 | if cb1.rank() < cb2.rank() { 32 | log::debug!("dupe detected: {cb1:?} == {cb2:?} -> removing {cb2:?}"); 33 | remove_me.insert(j); 34 | } else { 35 | log::debug!("dupe detected: {cb1:?} == {cb2:?} -> removing {cb1:?}"); 36 | remove_me.insert(i); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | let clipboards = clipboards 44 | .into_iter() 45 | .enumerate() 46 | .filter(|(i, _)| !remove_me.contains(i)) 47 | .map(|(_, c)| c) 48 | .collect::>>(); 49 | 50 | // let clipboards = dedupe(clipboards)?; 51 | 52 | for c in clipboards.iter() { 53 | c.set(&start)?; 54 | } 55 | 56 | log::info!("Using clipboards: {:?}", clipboards); 57 | 58 | Ok(clipboards) 59 | } 60 | 61 | pub fn keep_synced(clipboards: &Vec>) -> MyResult<()> { 62 | if clipboards.is_empty() { 63 | return Err(MyError::NoClipboards); 64 | } 65 | loop { 66 | sleep(Duration::from_millis(100)); 67 | let new_value = await_change(clipboards)?; 68 | for c in clipboards { 69 | c.set(&new_value)?; 70 | } 71 | } 72 | } 73 | 74 | fn are_same(one: &dyn Clipboard, two: &dyn Clipboard) -> MyResult { 75 | let d1 = &one.display(); 76 | let d2 = &two.display(); 77 | one.set(d1)?; 78 | if d1 != &two.get()? { 79 | return Ok(false); 80 | } 81 | two.set(d2)?; 82 | if d2 != &one.get()? { 83 | return Ok(false); 84 | } 85 | 86 | Ok(true) 87 | } 88 | 89 | // /// equality comparison is the bottleneck, and it's composed of two steps. so 90 | // /// the purpose of this function is to minimize the number of executions of 91 | // /// those steps. to do so, these steps are run at different times and combined 92 | // /// at the end. this requires some extra computation in here but that's fine. 93 | // fn dedupe(clipboards: Vec>) -> MyResult>> { 94 | // let mut k_cant_read_v: HashMap> = HashMap::new(); 95 | // let mut k_can_read_v: HashMap> = HashMap::new(); 96 | // for (i, one) in clipboards.iter().enumerate() { 97 | // // let i_can_read = k_can_read_v.entry(i).or_insert(HashSet::new()).clone(); 98 | // let i_cant_read = k_cant_read_v.entry(i).or_insert(HashSet::new()).clone(); 99 | // let d1 = &one.display(); 100 | // one.set(d1)?; 101 | // for (j, two) in clipboards.iter().enumerate() { 102 | // println!("{i} {j} {} {}", i != j, !i_cant_read.contains(&j)); 103 | // if i != j && !i_cant_read.contains(&j) { 104 | // if d1 == &two.get()? { 105 | // log::debug!("{two:?} can read {one:?}"); 106 | // k_can_read_v.entry(j).or_insert(HashSet::new()).insert(i); 107 | // } else { 108 | // log::debug!("{two:?} cannot read {one:?}"); 109 | // k_cant_read_v.entry(j).or_insert(HashSet::new()).insert(i); 110 | // } 111 | // } 112 | // } 113 | // } 114 | // let mut remove_me: HashSet = HashSet::new(); 115 | // let mut dont_remove_me: HashSet = HashSet::new(); 116 | // println!("{k_cant_read_v:#?}"); 117 | // println!("{k_can_read_v:#?}"); 118 | // for (k, v) in k_can_read_v.iter() { 119 | // for readable in v { 120 | // if !dont_remove_me.contains(readable) 121 | // && k_can_read_v 122 | // .get(readable) 123 | // .map(|what_readable_can_read| what_readable_can_read.contains(k)) 124 | // .unwrap_or(false) 125 | // { 126 | // remove_me.insert(*readable); 127 | // dont_remove_me.insert(*k); 128 | // } 129 | // } 130 | // } 131 | 132 | // Ok(clipboards 133 | // .into_iter() 134 | // .enumerate() 135 | // .filter(|(i, _)| !remove_me.contains(i)) 136 | // .map(|(_, c)| c) 137 | // .collect::>>()) 138 | // } 139 | 140 | // /// equality comparison is the bottleneck, and it's composed of two steps. so 141 | // /// the purpose of this function is to minimize the number of executions of 142 | // /// those steps. to do so, these steps are run at different times and combined 143 | // /// at the end. this requires some extra computation in here but that's fine. 144 | // fn dedupe(clipboards: Vec>) -> MyResult>> { 145 | // for (i, clipboard) in clipboards.iter().enumerate() { 146 | // println!("write {i}"); 147 | // clipboard.set(&format!("{}{i}", clipboard.get()?))?; 148 | // } 149 | // let mut results = HashMap::new(); 150 | // for (i, clipboard) in clipboards.iter().enumerate() { 151 | // println!("read {i}"); 152 | // results.insert(i, clipboard.get()?); 153 | // } 154 | 155 | // todo!() 156 | // } 157 | 158 | fn get_clipboards_spec MyResult>>>( 159 | getter: F, 160 | ) -> Vec> { 161 | let mut clipboards: Vec> = Vec::new(); 162 | let mut xcb_conn_err = None; 163 | let mut xcb_conn_failed_clipboards = vec![]; 164 | for i in 0..u8::MAX { 165 | let result = getter(i); 166 | match result { 167 | Ok(option) => { 168 | if let Some(clipboard) = option { 169 | log::debug!("Found clipboard: {:?}", clipboard); 170 | clipboards.push(clipboard); 171 | } 172 | } 173 | Err(MyError::TerminalClipboard(StandardizedError { 174 | inner, 175 | stdio: None, 176 | })) if format!("{inner}") == "clipboard error: X11 clipboard error : XCB connection error: Connection" => { 177 | xcb_conn_failed_clipboards.push(i); 178 | xcb_conn_err = Some(inner); 179 | }, 180 | Err(err) => log::error!( 181 | "unexpected error while attempting to setup clipboard {}: {}", 182 | i, 183 | err 184 | ), 185 | } 186 | } 187 | if let Some(err) = xcb_conn_err { 188 | let displays = concise_numbers(&xcb_conn_failed_clipboards); 189 | log::warning!( 190 | "Issue connecting to some x11 clipboards. \ 191 | This is expected when hooking up to gnome wayland, and not a problem in that context. \ 192 | Details: '{err}' for x11 displays: {displays}", 193 | ); 194 | } 195 | 196 | clipboards 197 | } 198 | 199 | fn get_wayland(n: u8) -> MyResult>> { 200 | let wl_display = format!("wayland-{}", n); 201 | let clipboard = WlrClipboard { 202 | display: wl_display.clone(), 203 | }; 204 | let attempt = clipboard.get(); 205 | if let Err(MyError::WlcrsPaste(PasteError::WaylandConnection( 206 | ConnectError::NoCompositorListening, 207 | ))) = attempt 208 | { 209 | return Ok(None); 210 | } 211 | if let Err(MyError::WlcrsPaste(PasteError::MissingProtocol { 212 | name: "zwlr_data_control_manager_v1", 213 | version: 1, 214 | })) = attempt 215 | { 216 | log::warning!( 217 | "{wl_display} does not support zwlr_data_control_manager_v1. If you are running \ 218 | gnome in wayland, that's OK because it provides an x11 clipboard, which will be used instead. \ 219 | Otherwise, `wl-copy` will be used to sync data *into* this clipboard, but it will not be possible \ 220 | to read data *from* this clipboard into other clipboards." 221 | ); 222 | let command = WlCommandClipboard { 223 | display: wl_display.clone(), 224 | }; 225 | let Ok(gotten) = command.get() else { 226 | return Ok(None); 227 | }; 228 | let Ok(_) = command.set(&gotten) else { 229 | return Ok(None); 230 | }; 231 | return Ok(Some(Box::new(command))); 232 | } 233 | attempt?; 234 | 235 | Ok(Some(Box::new(clipboard))) 236 | } 237 | 238 | fn get_x11(n: u8) -> MyResult>> { 239 | let display = format!(":{}", n); 240 | let clipboard = X11Clipboard::new(display)?; 241 | clipboard.get()?; 242 | 243 | Ok(Some(Box::new(clipboard))) 244 | } 245 | 246 | fn await_change(clipboards: &Vec>) -> MyResult { 247 | let start = clipboards[0].get()?; 248 | loop { 249 | for c in clipboards { 250 | if !c.should_poll() { 251 | continue; 252 | } 253 | let new = c.get()?; 254 | if new != start { 255 | log::info!("clipboard updated from display {}", c.display()); 256 | log::sensitive!(log::info, "clipboard contents: '{}'", new); 257 | return Ok(new); 258 | } 259 | } 260 | sleep(Duration::from_millis(200)); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/zombies.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Result as AnyResult; 2 | use std::{process::Command, thread, time}; 3 | 4 | pub fn spawn_reaper() { 5 | thread::spawn(|| { 6 | let delay = time::Duration::from_secs(1); 7 | let my_pid = std::process::id(); 8 | loop { 9 | reap_children(my_pid).unwrap_or_else(|err| eprintln!("{:?}", err)); 10 | thread::sleep(delay); 11 | } 12 | }); 13 | } 14 | 15 | pub fn reap_children(parent_pid: u32) -> AnyResult<()> { 16 | for pid in get_children(parent_pid)? { 17 | let _ = nix::sys::wait::waitpid( 18 | nix::unistd::Pid::from_raw(pid as i32), 19 | Some(nix::sys::wait::WaitPidFlag::WNOHANG), 20 | )?; 21 | } 22 | 23 | Ok(()) 24 | } 25 | 26 | pub fn get_children(pid: u32) -> AnyResult> { 27 | let stdout = Command::new("pgrep") 28 | .arg("-P") 29 | .arg(pid.to_string()) 30 | .output()? 31 | .stdout; 32 | 33 | Ok(String::from_utf8_lossy(&stdout) 34 | .trim() 35 | .to_string() 36 | .split("\n") 37 | .into_iter() 38 | .map(|s| s.parse::()) 39 | .filter(|r| r.is_ok()) 40 | .map(|r| r.unwrap()) 41 | .collect::>()) 42 | } 43 | --------------------------------------------------------------------------------