]> git.seodisparate.com - mpd_info_screen/commitdiff
WIP heavy refactoring/rewriting, use ggez
authorStephen Seo <seo.disparate@gmail.com>
Tue, 14 Dec 2021 09:30:02 +0000 (18:30 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Tue, 14 Dec 2021 09:30:55 +0000 (18:30 +0900)
Cargo.lock
Cargo.toml
src/display.rs [new file with mode: 0644]
src/main.rs
src/mpd_handler.rs [new file with mode: 0644]

index 48980755a0270f3894ac2d2a29b5f2f3f286dced..4a538de2bae421c511820f2556760f044ef3e398 100644 (file)
@@ -2,6 +2,22 @@
 # It is not intended for manual editing.
 version = 3
 
+[[package]]
+name = "ab_glyph"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20b228f2c198f98d4337ceb560333fb12cbb2f4948a953bf8c57d09deb219603"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser 0.13.2",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e"
+
 [[package]]
 name = "adler"
 version = "1.0.2"
@@ -15,10 +31,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
 
 [[package]]
-name = "ahash"
-version = "0.4.7"
+name = "alsa"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75c4da790adcb2ce5e758c064b4f3ec17a30349f9961d3e5e6c9688b052a9e18"
+dependencies = [
+ "alsa-sys",
+ "bitflags",
+ "libc",
+ "nix 0.20.0",
+]
+
+[[package]]
+name = "alsa-sys"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
+checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "andrew"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf"
+dependencies = [
+ "bitflags",
+ "rusttype",
+ "walkdir",
+ "xdg",
+ "xml-rs",
+]
+
+[[package]]
+name = "android_glue"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407"
 
 [[package]]
 name = "ansi_term"
@@ -29,6 +80,21 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "approx"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "072df7202e63b127ab55acfe16ce97013d5b97bf160489336d3f1840fd78e99e"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "arrayvec"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
+
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -41,28 +107,35 @@ dependencies = [
 ]
 
 [[package]]
-name = "audir-sles"
-version = "0.1.0"
+name = "autocfg"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea47348666a8edb7ad80cbee3940eb2bccf70df0e6ce09009abe1a836cb779f5"
+checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 
 [[package]]
-name = "audrey"
-version = "0.3.0"
+name = "base-x"
+version = "0.2.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "58b92a84e89497e3cd25d3672cd5d1c288abaac02c18ff21283f17d118b889b8"
-dependencies = [
- "dasp_frame",
- "dasp_sample",
- "hound",
- "lewton",
-]
+checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
 
 [[package]]
-name = "autocfg"
-version = "1.0.1"
+name = "bindgen"
+version = "0.56.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239"
+dependencies = [
+ "bitflags",
+ "cexpr",
+ "clang-sys",
+ "lazy_static",
+ "lazycell",
+ "peeking_take_while",
+ "proc-macro2",
+ "quote",
+ "regex",
+ "rustc-hash",
+ "shlex",
+]
 
 [[package]]
 name = "bitflags"
@@ -70,12 +143,24 @@ version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
 [[package]]
 name = "bumpalo"
 version = "3.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
 
+[[package]]
+name = "bytecount"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e"
+
 [[package]]
 name = "bytemuck"
 version = "1.7.2"
@@ -89,669 +174,2510 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
 
 [[package]]
-name = "cc"
-version = "1.0.70"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
-
-[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
-name = "clap"
-version = "2.33.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
-dependencies = [
- "ansi_term",
- "atty",
- "bitflags",
- "strsim",
- "textwrap",
- "unicode-width",
- "vec_map",
-]
-
-[[package]]
-name = "color_quant"
+name = "bytes"
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
 
 [[package]]
-name = "crc32fast"
-version = "1.2.1"
+name = "bzip2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
+checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
 dependencies = [
- "cfg-if",
+ "bzip2-sys",
+ "libc",
 ]
 
 [[package]]
-name = "crossbeam-channel"
-version = "0.5.1"
+name = "bzip2-sys"
+version = "0.1.11+1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
+checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
 dependencies = [
- "cfg-if",
- "crossbeam-utils",
+ "cc",
+ "libc",
+ "pkg-config",
 ]
 
 [[package]]
-name = "crossbeam-deque"
-version = "0.8.1"
+name = "calloop"
+version = "0.6.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
+checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c"
 dependencies = [
- "cfg-if",
- "crossbeam-epoch",
- "crossbeam-utils",
+ "log",
+ "nix 0.18.0",
 ]
 
 [[package]]
-name = "crossbeam-epoch"
-version = "0.9.5"
+name = "cargo-platform"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
+checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
 dependencies = [
- "cfg-if",
- "crossbeam-utils",
- "lazy_static",
- "memoffset",
- "scopeguard",
+ "serde",
 ]
 
 [[package]]
-name = "crossbeam-utils"
-version = "0.8.5"
+name = "cargo_metadata"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
+checksum = "7714a157da7991e23d90686b9524b9e12e0407a108647f52e9328f4b3d51ac7f"
 dependencies = [
- "cfg-if",
- "lazy_static",
+ "cargo-platform",
+ "semver 0.11.0",
+ "semver-parser 0.10.2",
+ "serde",
+ "serde_json",
 ]
 
 [[package]]
-name = "dasp_frame"
-version = "0.11.0"
+name = "cc"
+version = "1.0.70"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2a3937f5fe2135702897535c8d4a5553f8b116f76c1529088797f2eee7c5cd6"
+checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
 dependencies = [
- "dasp_sample",
+ "jobserver",
 ]
 
 [[package]]
-name = "dasp_sample"
-version = "0.11.0"
+name = "cesu8"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
 
 [[package]]
-name = "deflate"
-version = "0.8.6"
+name = "cexpr"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
+checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
 dependencies = [
- "adler32",
- "byteorder",
+ "nom 5.1.2",
 ]
 
 [[package]]
-name = "either"
-version = "1.6.1"
+name = "cfg-if"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
+checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
 
 [[package]]
-name = "fontdue"
-version = "0.5.2"
+name = "cfg-if"
+version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c75712fff1702bac51b7eaa5a5ca9f9853b8055ef5906088a32f4fe196595a1d"
-dependencies = [
- "hashbrown",
- "ttf-parser",
-]
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
-name = "gif"
-version = "0.11.2"
+name = "cgl"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de"
+checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
 dependencies = [
- "color_quant",
- "weezl",
+ "libc",
 ]
 
 [[package]]
-name = "glam"
-version = "0.14.0"
+name = "clang-sys"
+version = "1.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da"
+checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90"
+dependencies = [
+ "glob",
+ "libc",
+ "libloading 0.7.2",
+]
 
 [[package]]
-name = "hashbrown"
-version = "0.9.1"
+name = "clap"
+version = "2.33.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
+checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
 dependencies = [
- "ahash",
+ "ansi_term",
+ "atty",
+ "bitflags",
+ "strsim 0.8.0",
+ "textwrap",
+ "unicode-width",
+ "vec_map",
 ]
 
 [[package]]
-name = "heck"
-version = "0.3.3"
+name = "claxon"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688"
+
+[[package]]
+name = "cocoa"
+version = "0.24.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832"
 dependencies = [
- "unicode-segmentation",
+ "bitflags",
+ "block",
+ "cocoa-foundation",
+ "core-foundation 0.9.2",
+ "core-graphics 0.22.3",
+ "foreign-types",
+ "libc",
+ "objc",
 ]
 
 [[package]]
-name = "hermit-abi"
-version = "0.1.19"
+name = "cocoa-foundation"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318"
 dependencies = [
+ "bitflags",
+ "block",
+ "core-foundation 0.9.2",
+ "core-graphics-types",
+ "foreign-types",
  "libc",
+ "objc",
 ]
 
 [[package]]
-name = "hound"
-version = "3.4.0"
+name = "color_quant"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
+checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
 
 [[package]]
-name = "image"
-version = "0.23.14"
+name = "combine"
+version = "4.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
+checksum = "b2b2f5d0ee456f3928812dfc8c6d9a1d592b98678f6d56db9b0cd2b7bc6c8db5"
 dependencies = [
- "bytemuck",
- "byteorder",
- "color_quant",
- "gif",
- "jpeg-decoder",
- "num-iter",
- "num-rational",
- "num-traits",
- "png",
- "scoped_threadpool",
- "tiff",
+ "bytes",
+ "memchr",
 ]
 
 [[package]]
-name = "jpeg-decoder"
-version = "0.1.22"
+name = "core-foundation"
+version = "0.6.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
+checksum = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d"
 dependencies = [
- "rayon",
+ "core-foundation-sys 0.6.2",
+ "libc",
 ]
 
 [[package]]
-name = "lazy_static"
-version = "1.4.0"
+name = "core-foundation"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
+dependencies = [
+ "core-foundation-sys 0.7.0",
+ "libc",
+]
 
 [[package]]
-name = "lewton"
-version = "0.9.4"
+name = "core-foundation"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d542c1a317036c45c2aa1cf10cc9d403ca91eb2d333ef1a4917e5cb10628bd0"
+checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3"
 dependencies = [
- "byteorder",
- "ogg",
- "smallvec",
+ "core-foundation-sys 0.8.3",
+ "libc",
 ]
 
 [[package]]
-name = "libc"
-version = "0.2.101"
+name = "core-foundation-sys"
+version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
+checksum = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b"
 
 [[package]]
-name = "macroquad"
-version = "0.3.9"
+name = "core-foundation-sys"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c11792fb3af71339e4354ee3dbde4bbe5731dac266cc087b414338dfb1ca1bd"
-dependencies = [
- "bumpalo",
- "fontdue",
- "glam",
- "image",
- "macroquad_macro",
- "miniquad",
- "quad-rand",
- "quad-snd",
-]
+checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
 
 [[package]]
-name = "macroquad_macro"
-version = "0.1.6"
+name = "core-foundation-sys"
+version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "15db41b6abd0156c498868ec69f8973731ef75874768ac234980422579e70805"
+checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
 
 [[package]]
-name = "maybe-uninit"
-version = "2.0.0"
+name = "core-graphics"
+version = "0.19.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
+checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923"
+dependencies = [
+ "bitflags",
+ "core-foundation 0.7.0",
+ "foreign-types",
+ "libc",
+]
 
 [[package]]
-name = "memoffset"
-version = "0.6.4"
+name = "core-graphics"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb"
 dependencies = [
- "autocfg",
+ "bitflags",
+ "core-foundation 0.9.2",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
 ]
 
 [[package]]
-name = "miniquad"
-version = "0.3.0-alpha.37"
+name = "core-graphics-types"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6793a3ef846953fc7c01302093abf8749be22742c63db05c66ef0a2c889a7fbb"
+checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b"
 dependencies = [
- "sapp-android",
- "sapp-darwin",
- "sapp-dummy",
- "sapp-ios",
- "sapp-linux",
- "sapp-wasm",
- "sapp-windows",
+ "bitflags",
+ "core-foundation 0.9.2",
+ "foreign-types",
+ "libc",
 ]
 
 [[package]]
-name = "miniz_oxide"
-version = "0.3.7"
+name = "core-video-sys"
+version = "0.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
+checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828"
 dependencies = [
- "adler32",
+ "cfg-if 0.1.10",
+ "core-foundation-sys 0.7.0",
+ "core-graphics 0.19.2",
+ "libc",
+ "objc",
 ]
 
 [[package]]
-name = "miniz_oxide"
-version = "0.4.4"
+name = "coreaudio-rs"
+version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+checksum = "11894b20ebfe1ff903cbdc52259693389eea03b94918a2def2c30c3bf227ad88"
 dependencies = [
- "adler",
- "autocfg",
+ "bitflags",
+ "coreaudio-sys",
 ]
 
 [[package]]
-name = "mpd_info_screen"
-version = "0.1.0"
+name = "coreaudio-sys"
+version = "0.2.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b7e3347be6a09b46aba228d6608386739fb70beff4f61e07422da87b0bb31fa"
 dependencies = [
- "image",
- "macroquad",
- "structopt",
+ "bindgen",
 ]
 
 [[package]]
-name = "ndk-sys"
-version = "0.2.1"
+name = "cpal"
+version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d"
-
-[[package]]
-name = "num-integer"
-version = "0.1.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+checksum = "98f45f0a21f617cd2c788889ef710b63f075c949259593ea09c826f1e47a2418"
 dependencies = [
- "autocfg",
- "num-traits",
+ "alsa",
+ "core-foundation-sys 0.8.3",
+ "coreaudio-rs",
+ "jni",
+ "js-sys",
+ "lazy_static",
+ "libc",
+ "mach 0.3.2",
+ "ndk 0.3.0",
+ "ndk-glue 0.3.0",
+ "nix 0.20.0",
+ "oboe",
+ "parking_lot",
+ "stdweb 0.1.3",
+ "thiserror",
+ "web-sys",
+ "winapi",
 ]
 
 [[package]]
-name = "num-iter"
-version = "0.1.42"
+name = "crc32fast"
+version = "1.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
+checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
 dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
+ "cfg-if 1.0.0",
 ]
 
 [[package]]
-name = "num-rational"
-version = "0.3.2"
+name = "crossbeam"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
+checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845"
 dependencies = [
- "autocfg",
- "num-integer",
- "num-traits",
+ "cfg-if 1.0.0",
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-epoch",
+ "crossbeam-queue",
+ "crossbeam-utils",
 ]
 
 [[package]]
-name = "num-traits"
-version = "0.2.14"
+name = "crossbeam-channel"
+version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
 dependencies = [
- "autocfg",
+ "cfg-if 1.0.0",
+ "crossbeam-utils",
 ]
 
 [[package]]
-name = "num_cpus"
-version = "1.13.0"
+name = "crossbeam-deque"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
 dependencies = [
- "hermit-abi",
- "libc",
+ "cfg-if 1.0.0",
+ "crossbeam-epoch",
+ "crossbeam-utils",
 ]
 
 [[package]]
-name = "ogg"
-version = "0.7.1"
+name = "crossbeam-epoch"
+version = "0.9.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13e571c3517af9e1729d4c63571a27edd660ade0667973bfc74a67c660c2b651"
+checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
 dependencies = [
- "byteorder",
+ "cfg-if 1.0.0",
+ "crossbeam-utils",
+ "lazy_static",
+ "memoffset",
+ "scopeguard",
 ]
 
 [[package]]
-name = "png"
-version = "0.16.8"
+name = "crossbeam-queue"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
+checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
 dependencies = [
- "bitflags",
- "crc32fast",
- "deflate",
- "miniz_oxide 0.3.7",
+ "cfg-if 1.0.0",
+ "crossbeam-utils",
 ]
 
 [[package]]
-name = "proc-macro-error"
-version = "1.0.4"
+name = "crossbeam-utils"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
 dependencies = [
- "proc-macro-error-attr",
- "proc-macro2",
- "quote",
- "syn",
- "version_check",
+ "cfg-if 1.0.0",
+ "lazy_static",
 ]
 
 [[package]]
-name = "proc-macro-error-attr"
-version = "1.0.4"
+name = "cty"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
-dependencies = [
- "proc-macro2",
- "quote",
- "version_check",
-]
+checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
 
 [[package]]
-name = "proc-macro2"
-version = "1.0.29"
+name = "darling"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
+checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
 dependencies = [
- "unicode-xid",
+ "darling_core",
+ "darling_macro",
 ]
 
 [[package]]
-name = "quad-alsa-sys"
-version = "0.3.2"
+name = "darling_core"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c66c2f04a6946293477973d85adc251d502da51c57b08cd9c997f0cfd8dcd4b5"
+checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
 dependencies = [
- "libc",
+ "fnv",
+ "ident_case",
+ "proc-macro2",
+ "quote",
+ "strsim 0.9.3",
+ "syn",
 ]
 
 [[package]]
-name = "quad-rand"
-version = "0.2.1"
+name = "darling_macro"
+version = "0.10.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "658fa1faf7a4cc5f057c9ee5ef560f717ad9d8dc66d975267f709624d6e1ab88"
+checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
+dependencies = [
+ "darling_core",
+ "quote",
+ "syn",
+]
 
 [[package]]
-name = "quad-snd"
-version = "0.2.2"
+name = "deflate"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9b33c4e021bc86d54061b7d23ec96e6fd4c4400066658b043e2b2b36ebca737"
+checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
 dependencies = [
- "audir-sles",
- "audrey",
- "libc",
- "quad-alsa-sys",
- "winapi",
+ "adler32",
+ "byteorder",
 ]
 
 [[package]]
-name = "quote"
-version = "1.0.9"
+name = "derivative"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
 dependencies = [
  "proc-macro2",
+ "quote",
+ "syn",
 ]
 
 [[package]]
-name = "rayon"
-version = "1.5.1"
+name = "directories"
+version = "3.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
+checksum = "e69600ff1703123957937708eb27f7a564e48885c537782722ed0ba3189ce1d7"
 dependencies = [
- "autocfg",
- "crossbeam-deque",
- "either",
- "rayon-core",
+ "dirs-sys",
 ]
 
 [[package]]
-name = "rayon-core"
-version = "1.9.1"
+name = "dirs"
+version = "3.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+checksum = "30baa043103c9d0c2a57cf537cc2f35623889dc0d405e6c3cccfadbc81c71309"
 dependencies = [
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-utils",
- "lazy_static",
- "num_cpus",
+ "dirs-sys",
 ]
 
 [[package]]
-name = "sapp-android"
-version = "0.1.8"
+name = "dirs-sys"
+version = "0.3.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a4a81f462ba2783213978528560aa138adf2f94da1ac940c1b5c854c03e1724"
+checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780"
 dependencies = [
  "libc",
- "ndk-sys",
+ "redox_users",
+ "winapi",
 ]
 
 [[package]]
-name = "sapp-darwin"
-version = "0.1.6"
+name = "discard"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0310e2445f307468aa13f1cde94d6fba6b8fd329afbb642dedbe3faf1a145f31"
-dependencies = [
- "cc",
-]
+checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
 
 [[package]]
-name = "sapp-dummy"
-version = "0.1.5"
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dlib"
+version = "0.4.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66f1ad26a5b6c682b9ca27c66db9aa91002b8d98a82ac7101ded57285215a478"
+checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76"
 dependencies = [
- "libc",
+ "libloading 0.6.7",
 ]
 
 [[package]]
-name = "sapp-ios"
-version = "0.1.2"
+name = "dlib"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "081e6e5261c9ac2e938979b6a854a53b439f065fc3c897205ce7e69d3028b4a9"
+checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
 dependencies = [
- "cc",
+ "libloading 0.7.2",
 ]
 
 [[package]]
-name = "sapp-linux"
-version = "0.1.13"
+name = "downcast-rs"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
+
+[[package]]
+name = "draw_state"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbdb2f8011955c62544d9e626a58333e788810d00bd7411d52b81611b92af142"
+checksum = "33cf9537e2d06891448799b96d5a8c8083e0e90522a7fdabe6ebf4f41d79d651"
 dependencies = [
- "libc",
+ "bitflags",
 ]
 
 [[package]]
-name = "sapp-wasm"
-version = "0.1.26"
+name = "either"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00e859e8645a3bcb85aecd40bab883438e4105f21b21bccbeac2348760f508bb"
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 
 [[package]]
-name = "sapp-windows"
-version = "0.2.18"
+name = "error-chain"
+version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8faec983cb54ce5e9529815fc0aae6c36bab9fba9cd0ae5590dfa17bc0719fa"
+checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
 dependencies = [
- "winapi",
+ "version_check",
 ]
 
 [[package]]
-name = "scoped_threadpool"
-version = "0.1.9"
+name = "euclid"
+version = "0.22.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+checksum = "da96828553a086d7b18dcebfc579bd9628b016f86590d7453c115e490fa74b80"
+dependencies = [
+ "num-traits",
+]
 
 [[package]]
-name = "scopeguard"
-version = "1.1.0"
+name = "float_next_after"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "4fc612c5837986b7104a87a0df74a5460931f1c5274be12f8d0f40aa2f30d632"
+dependencies = [
+ "num-traits",
+]
 
 [[package]]
-name = "smallvec"
-version = "0.6.14"
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
 dependencies = [
- "maybe-uninit",
+ "foreign-types-shared",
 ]
 
 [[package]]
-name = "strsim"
-version = "0.8.0"
+name = "foreign-types-shared"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
 
 [[package]]
-name = "structopt"
-version = "0.3.23"
+name = "getrandom"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa"
+checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
 dependencies = [
- "clap",
- "lazy_static",
- "structopt-derive",
+ "cfg-if 1.0.0",
+ "libc",
+ "wasi",
 ]
 
 [[package]]
-name = "structopt-derive"
-version = "0.4.16"
+name = "gfx"
+version = "0.18.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba"
+checksum = "01de46f9508a5c259aef105f0bff760ceddca832ea9c87ce03d1923e22ee155b"
 dependencies = [
- "heck",
- "proc-macro-error",
- "proc-macro2",
- "quote",
- "syn",
+ "draw_state",
+ "gfx_core",
+ "log",
 ]
 
 [[package]]
-name = "syn"
-version = "1.0.76"
+name = "gfx_core"
+version = "0.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
+checksum = "75fbddaef2e12b4995900539d7209d947b988a3d87ee8737484d049b526e5441"
 dependencies = [
- "proc-macro2",
- "quote",
- "unicode-xid",
+ "bitflags",
+ "draw_state",
+ "log",
 ]
 
 [[package]]
-name = "textwrap"
-version = "0.11.0"
+name = "gfx_device_gl"
+version = "0.16.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+checksum = "109c385fa380c18888633aa27d1e16cbae518469702a2f69dcb5f52d5378bebc"
 dependencies = [
- "unicode-width",
+ "gfx_core",
+ "gfx_gl",
+ "log",
 ]
 
 [[package]]
-name = "tiff"
+name = "gfx_gl"
 version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
+checksum = "f2d38164670920cfb7491bc0cf6f49f0554bd1c44cdbedc6c78d2bf91691ff5e"
 dependencies = [
- "jpeg-decoder",
- "miniz_oxide 0.4.4",
- "weezl",
+ "gl_generator",
 ]
 
 [[package]]
-name = "ttf-parser"
-version = "0.12.3"
+name = "ggez"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6"
+checksum = "c2481c5fcf83c5d51172850a3ee2ae36d86f9961d39851c6bec99108f87f085d"
+dependencies = [
+ "approx",
+ "bitflags",
+ "bytemuck",
+ "directories",
+ "gfx",
+ "gfx_core",
+ "gfx_device_gl",
+ "gilrs",
+ "glam",
+ "glutin",
+ "glyph_brush",
+ "image",
+ "log",
+ "lyon",
+ "mint",
+ "old_school_gfx_glutin_ext",
+ "rodio",
+ "serde",
+ "serde_derive",
+ "skeptic",
+ "smart-default",
+ "toml",
+ "winit",
+ "zip",
+]
 
 [[package]]
-name = "unicode-segmentation"
-version = "1.8.0"
+name = "gif"
+version = "0.11.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
+checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de"
+dependencies = [
+ "color_quant",
+ "weezl",
+]
 
 [[package]]
-name = "unicode-width"
-version = "0.1.8"
+name = "gilrs"
+version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+checksum = "0e986f911d937f4395dfc2a39618dcef452773d32dcdbe0828c623f76588f749"
+dependencies = [
+ "fnv",
+ "gilrs-core",
+ "log",
+ "uuid",
+ "vec_map",
+]
 
 [[package]]
-name = "unicode-xid"
-version = "0.2.2"
+name = "gilrs-core"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+checksum = "9a5e5bb97bf9a0d9519a28cf38839cf1d6d9bb572b48e3c67202271fec2ed5e7"
+dependencies = [
+ "core-foundation 0.6.4",
+ "io-kit-sys",
+ "libc",
+ "libudev-sys",
+ "log",
+ "nix 0.20.0",
+ "rusty-xinput",
+ "stdweb 0.4.20",
+ "uuid",
+ "vec_map",
+ "winapi",
+]
 
 [[package]]
-name = "vec_map"
-version = "0.8.2"
+name = "gl_generator"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
 
 [[package]]
-name = "version_check"
-version = "0.9.3"
+name = "glam"
+version = "0.20.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "68270e16582ea40f9c5b2fcd588fbc9cb696577222e04a64d9085cc314806a8a"
+dependencies = [
+ "mint",
+]
 
 [[package]]
-name = "weezl"
-version = "0.1.5"
+name = "glob"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
+checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+
+[[package]]
+name = "glutin"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "762d6cd2e1b855d99668ebe591cc9058659d85ac39a9a2078000eb122ddba8f0"
+dependencies = [
+ "android_glue",
+ "cgl",
+ "cocoa",
+ "core-foundation 0.9.2",
+ "glutin_egl_sys",
+ "glutin_emscripten_sys",
+ "glutin_gles2_sys",
+ "glutin_glx_sys",
+ "glutin_wgl_sys",
+ "lazy_static",
+ "libloading 0.7.2",
+ "log",
+ "objc",
+ "osmesa-sys",
+ "parking_lot",
+ "wayland-client",
+ "wayland-egl",
+ "winapi",
+ "winit",
+]
+
+[[package]]
+name = "glutin_egl_sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2abb6aa55523480c4adc5a56bbaa249992e2dddb2fc63dc96e04a3355364c211"
+dependencies = [
+ "gl_generator",
+ "winapi",
+]
+
+[[package]]
+name = "glutin_emscripten_sys"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1"
+
+[[package]]
+name = "glutin_gles2_sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103"
+dependencies = [
+ "gl_generator",
+ "objc",
+]
+
+[[package]]
+name = "glutin_glx_sys"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e393c8fc02b807459410429150e9c4faffdb312d59b8c038566173c81991351"
+dependencies = [
+ "gl_generator",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696"
+dependencies = [
+ "gl_generator",
+]
+
+[[package]]
+name = "glyph_brush"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "21932fbf719272848eec4583740d978203c6e7da4c4e203358f5b95946c97409"
+dependencies = [
+ "glyph_brush_draw_cache",
+ "glyph_brush_layout",
+ "log",
+ "ordered-float",
+ "rustc-hash",
+ "twox-hash",
+]
+
+[[package]]
+name = "glyph_brush_draw_cache"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6010675390f6889e09a21e2c8b575b3ee25667ea8237a8d59423f73cb8c28610"
+dependencies = [
+ "ab_glyph",
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "linked-hash-map",
+ "rayon",
+ "rustc-hash",
+]
+
+[[package]]
+name = "glyph_brush_layout"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cc32c2334f00ca5ac3695c5009ae35da21da8c62d255b5b96d56e2597a637a38"
+dependencies = [
+ "ab_glyph",
+ "approx",
+ "xi-unicode",
+]
+
+[[package]]
+name = "heck"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "hound"
+version = "3.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
+
+[[package]]
+name = "ident_case"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
+
+[[package]]
+name = "image"
+version = "0.23.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
+dependencies = [
+ "bytemuck",
+ "byteorder",
+ "color_quant",
+ "gif",
+ "jpeg-decoder",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+ "png",
+ "scoped_threadpool",
+ "tiff",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "io-kit-sys"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f21dcc74995dd4cd090b147e79789f8d65959cbfb5f0b118002db869ea3bd0a0"
+dependencies = [
+ "core-foundation-sys 0.6.2",
+ "mach 0.2.3",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
+
+[[package]]
+name = "jni"
+version = "0.19.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
+dependencies = [
+ "cesu8",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror",
+ "walkdir",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "jpeg-decoder"
+version = "0.1.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
+dependencies = [
+ "rayon",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
+dependencies = [
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "lazycell"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
+
+[[package]]
+name = "lewton"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "777b48df9aaab155475a83a7df3070395ea1ac6902f5cd062b8f2b028075c030"
+dependencies = [
+ "byteorder",
+ "ogg",
+ "tinyvec",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.101"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21"
+
+[[package]]
+name = "libloading"
+version = "0.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883"
+dependencies = [
+ "cfg-if 1.0.0",
+ "winapi",
+]
+
+[[package]]
+name = "libloading"
+version = "0.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52"
+dependencies = [
+ "cfg-if 1.0.0",
+ "winapi",
+]
+
+[[package]]
+name = "libudev-sys"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324"
+dependencies = [
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "linked-hash-map"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
+
+[[package]]
+name = "lock_api"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
+dependencies = [
+ "scopeguard",
+]
+
+[[package]]
+name = "log"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+dependencies = [
+ "cfg-if 1.0.0",
+]
+
+[[package]]
+name = "lyon"
+version = "0.17.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf0510ed5e3e2fb80f3db2061ef5ca92d87bfda1a624bb1eacf3bd50226e4cbb"
+dependencies = [
+ "lyon_algorithms",
+ "lyon_tessellation",
+]
+
+[[package]]
+name = "lyon_algorithms"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8037f716541ba0d84d3de05c0069f8068baf73990d55980558b84d944c8a244a"
+dependencies = [
+ "lyon_path",
+ "sid",
+]
+
+[[package]]
+name = "lyon_geom"
+version = "0.17.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce99ce77c22bfd8f39a95b9c749dffbfc3e2491ea30c874764c801a8b1485489"
+dependencies = [
+ "arrayvec",
+ "euclid",
+ "num-traits",
+]
+
+[[package]]
+name = "lyon_path"
+version = "0.17.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0a59fdf767ca0d887aa61d1b48d4bbf6a124c1a45503593f7d38ab945bfbc0"
+dependencies = [
+ "lyon_geom",
+]
+
+[[package]]
+name = "lyon_tessellation"
+version = "0.17.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7230e08dd0638048e46f387f255dbe7a7344a3e6705beab53242b5af25635760"
+dependencies = [
+ "float_next_after",
+ "lyon_path",
+]
+
+[[package]]
+name = "mach"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "mach"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
+
+[[package]]
+name = "memmap2"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
+
+[[package]]
+name = "minimp3"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "985438f75febf74c392071a975a29641b420dd84431135a6e6db721de4b74372"
+dependencies = [
+ "minimp3-sys",
+ "slice-deque",
+ "thiserror",
+]
+
+[[package]]
+name = "minimp3-sys"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e21c73734c69dc95696c9ed8926a2b393171d98b3f5f5935686a26a487ab9b90"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
+dependencies = [
+ "adler32",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
+dependencies = [
+ "adler",
+ "autocfg",
+]
+
+[[package]]
+name = "mint"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162e591484b4b8fe9e1ca16ebf07ab584fdc3334508d76a788cd54d89cfc20dc"
+
+[[package]]
+name = "mio"
+version = "0.7.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
+dependencies = [
+ "libc",
+ "log",
+ "miow",
+ "ntapi",
+ "winapi",
+]
+
+[[package]]
+name = "mio-misc"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c"
+dependencies = [
+ "crossbeam",
+ "crossbeam-queue",
+ "log",
+ "mio",
+]
+
+[[package]]
+name = "miow"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "mpd_info_screen"
+version = "0.1.0"
+dependencies = [
+ "ggez",
+ "image",
+ "structopt",
+]
+
+[[package]]
+name = "ndk"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8794322172319b972f528bf90c6b467be0079f1fa82780ffb431088e741a73ab"
+dependencies = [
+ "jni-sys",
+ "ndk-sys",
+ "num_enum",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d64d6af06fde0e527b1ba5c7b79a6cc89cfc46325b0b2887dffe8f70197e0c3c"
+dependencies = [
+ "bitflags",
+ "jni-sys",
+ "ndk-sys",
+ "num_enum",
+ "thiserror",
+]
+
+[[package]]
+name = "ndk-glue"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c5caf0c24d51ac1c905c27d4eda4fa0635bbe0de596b8f79235e0b17a4d29385"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "ndk 0.3.0",
+ "ndk-macro",
+ "ndk-sys",
+]
+
+[[package]]
+name = "ndk-glue"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d3e9e94628f24e7a3cb5b96a2dc5683acd9230bf11991c2a1677b87695138420"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "log",
+ "ndk 0.4.0",
+ "ndk-macro",
+ "ndk-sys",
+]
+
+[[package]]
+name = "ndk-macro"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d"
+dependencies = [
+ "darling",
+ "proc-macro-crate 0.1.5",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "ndk-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c44922cb3dbb1c70b5e5f443d63b64363a898564d739ba5198e3a9138442868d"
+
+[[package]]
+name = "nix"
+version = "0.18.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if 0.1.10",
+ "libc",
+]
+
+[[package]]
+name = "nix"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a"
+dependencies = [
+ "bitflags",
+ "cc",
+ "cfg-if 1.0.0",
+ "libc",
+]
+
+[[package]]
+name = "nom"
+version = "5.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
+dependencies = [
+ "memchr",
+ "version_check",
+]
+
+[[package]]
+name = "nom"
+version = "7.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109"
+dependencies = [
+ "memchr",
+ "minimal-lexical",
+ "version_check",
+]
+
+[[package]]
+name = "ntapi"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "num-derive"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "num-integer"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
+dependencies = [
+ "autocfg",
+ "num-traits",
+]
+
+[[package]]
+name = "num-iter"
+version = "0.1.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-rational"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "num_cpus"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
+dependencies = [
+ "hermit-abi",
+ "libc",
+]
+
+[[package]]
+name = "num_enum"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9bd055fb730c4f8f4f57d45d35cd6b3f0980535b056dc7ff119cee6a66ed6f"
+dependencies = [
+ "derivative",
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "486ea01961c4a818096de679a8b740b26d9033146ac5291b1c98557658f8cdd9"
+dependencies = [
+ "proc-macro-crate 1.1.0",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "oboe"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e15e22bc67e047fe342a32ecba55f555e3be6166b04dd157cd0f803dfa9f48e1"
+dependencies = [
+ "jni",
+ "ndk 0.4.0",
+ "ndk-glue 0.4.0",
+ "num-derive",
+ "num-traits",
+ "oboe-sys",
+]
+
+[[package]]
+name = "oboe-sys"
+version = "0.4.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "338142ae5ab0aaedc8275aa8f67f460e43ae0fca76a695a742d56da0a269eadc"
+dependencies = [
+ "cc",
+]
+
+[[package]]
+name = "ogg"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6951b4e8bf21c8193da321bcce9c9dd2e13c858fe078bf9054a288b419ae5d6e"
+dependencies = [
+ "byteorder",
+]
+
+[[package]]
+name = "old_school_gfx_glutin_ext"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1026bdd085523158bd5a1cf0ccbf39b7753c96ccbd54e7f6144ed1518f0046b3"
+dependencies = [
+ "gfx_core",
+ "gfx_device_gl",
+ "glutin",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
+
+[[package]]
+name = "ordered-float"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97c9d06878b3a851e8026ef94bf7fef9ba93062cd412601da4d9cf369b1cc62d"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "osmesa-sys"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b"
+dependencies = [
+ "shared_library",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f923fb806c46266c02ab4a5b239735c144bdeda724a50ed058e5226f594cde3"
+dependencies = [
+ "ttf-parser 0.6.2",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65ee3f72636e6f164cc41c9f9057f4e58c4e13507699ea7f5e5242b64b8198ee"
+dependencies = [
+ "ttf-parser 0.13.4",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
+dependencies = [
+ "instant",
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
+dependencies = [
+ "cfg-if 1.0.0",
+ "instant",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "winapi",
+]
+
+[[package]]
+name = "peeking_take_while"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+
+[[package]]
+name = "percent-encoding"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
+
+[[package]]
+name = "pest"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+dependencies = [
+ "ucd-trie",
+]
+
+[[package]]
+name = "pkg-config"
+version = "0.3.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
+
+[[package]]
+name = "png"
+version = "0.16.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
+dependencies = [
+ "bitflags",
+ "crc32fast",
+ "deflate",
+ "miniz_oxide 0.3.7",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
+
+[[package]]
+name = "proc-macro-crate"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785"
+dependencies = [
+ "toml",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83"
+dependencies = [
+ "thiserror",
+ "toml",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "pulldown-cmark"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffade02495f22453cd593159ea2f59827aae7f53fa8323f756799b670881dcf8"
+dependencies = [
+ "bitflags",
+ "memchr",
+ "unicase",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "rand"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+dependencies = [
+ "libc",
+ "rand_chacha",
+ "rand_core",
+ "rand_hc",
+]
+
+[[package]]
+name = "rand_chacha"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
+dependencies = [
+ "ppv-lite86",
+ "rand_core",
+]
+
+[[package]]
+name = "rand_core"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
+dependencies = [
+ "getrandom",
+]
+
+[[package]]
+name = "rand_hc"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
+dependencies = [
+ "rand_core",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76"
+dependencies = [
+ "libc",
+ "raw-window-handle 0.4.2",
+]
+
+[[package]]
+name = "raw-window-handle"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7"
+dependencies = [
+ "cty",
+]
+
+[[package]]
+name = "rayon"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
+dependencies = [
+ "autocfg",
+ "crossbeam-deque",
+ "either",
+ "rayon-core",
+]
+
+[[package]]
+name = "rayon-core"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
+dependencies = [
+ "crossbeam-channel",
+ "crossbeam-deque",
+ "crossbeam-utils",
+ "lazy_static",
+ "num_cpus",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
+dependencies = [
+ "getrandom",
+ "redox_syscall",
+]
+
+[[package]]
+name = "regex"
+version = "1.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+dependencies = [
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "rodio"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d98f5e557b61525057e2bc142c8cd7f0e70d75dc32852309bec440e6e046bf9"
+dependencies = [
+ "claxon",
+ "cpal",
+ "hound",
+ "lewton",
+ "minimp3",
+]
+
+[[package]]
+name = "rustc-hash"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+
+[[package]]
+name = "rustc_version"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+dependencies = [
+ "semver 0.9.0",
+]
+
+[[package]]
+name = "rusttype"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc7c727aded0be18c5b80c1640eae0ac8e396abf6fa8477d96cb37d18ee5ec59"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser 0.6.0",
+]
+
+[[package]]
+name = "rusty-xinput"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2aa654bc32eb9ca14cce1a084abc9dfe43949a4547c35269a094c39272db3bb"
+dependencies = [
+ "lazy_static",
+ "log",
+ "winapi",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2"
+
+[[package]]
+name = "scoped_threadpool"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
+
+[[package]]
+name = "scopeguard"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+
+[[package]]
+name = "semver"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
+dependencies = [
+ "semver-parser 0.7.0",
+]
+
+[[package]]
+name = "semver"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
+dependencies = [
+ "semver-parser 0.10.2",
+ "serde",
+]
+
+[[package]]
+name = "semver-parser"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
+
+[[package]]
+name = "semver-parser"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
+dependencies = [
+ "pest",
+]
+
+[[package]]
+name = "serde"
+version = "1.0.131"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.131"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "sha1"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
+
+[[package]]
+name = "shared_library"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
+dependencies = [
+ "lazy_static",
+ "libc",
+]
+
+[[package]]
+name = "shlex"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
+
+[[package]]
+name = "sid"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd5ac56c121948b4879bba9e519852c211bcdd8f014efff766441deff0b91bdb"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
+name = "skeptic"
+version = "0.13.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "188b810342d98f23f0bb875045299f34187b559370b041eb11520c905370a888"
+dependencies = [
+ "bytecount",
+ "cargo_metadata",
+ "error-chain",
+ "glob",
+ "pulldown-cmark",
+ "tempfile",
+ "walkdir",
+]
+
+[[package]]
+name = "slice-deque"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "31ef6ee280cdefba6d2d0b4b78a84a1c1a3f3a4cec98c2d4231c8bc225de0f25"
+dependencies = [
+ "libc",
+ "mach 0.3.2",
+ "winapi",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
+
+[[package]]
+name = "smart-default"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80"
+dependencies = [
+ "andrew",
+ "bitflags",
+ "calloop",
+ "dlib 0.4.2",
+ "lazy_static",
+ "log",
+ "memmap2",
+ "nix 0.18.0",
+ "wayland-client",
+ "wayland-cursor",
+ "wayland-protocols",
+]
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
+name = "stdweb"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef5430c8e36b713e13b48a9f709cc21e046723fe44ce34587b73a830203b533e"
+
+[[package]]
+name = "stdweb"
+version = "0.4.20"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5"
+dependencies = [
+ "discard",
+ "rustc_version",
+ "serde",
+ "serde_json",
+ "stdweb-derive",
+ "stdweb-internal-macros",
+ "stdweb-internal-runtime",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "stdweb-derive"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_derive",
+ "syn",
+]
+
+[[package]]
+name = "stdweb-internal-macros"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11"
+dependencies = [
+ "base-x",
+ "proc-macro2",
+ "quote",
+ "serde",
+ "serde_derive",
+ "serde_json",
+ "sha1",
+ "syn",
+]
+
+[[package]]
+name = "stdweb-internal-runtime"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0"
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+
+[[package]]
+name = "strsim"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
+
+[[package]]
+name = "structopt"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bf9d950ef167e25e0bdb073cf1d68e9ad2795ac826f2f3f59647817cf23c0bfa"
+dependencies = [
+ "clap",
+ "lazy_static",
+ "structopt-derive",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "134d838a2c9943ac3125cf6df165eda53493451b719f3255b2a26b85f772d0ba"
+dependencies = [
+ "heck",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
+dependencies = [
+ "cfg-if 1.0.0",
+ "libc",
+ "rand",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+dependencies = [
+ "unicode-width",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tiff"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
+dependencies = [
+ "jpeg-decoder",
+ "miniz_oxide 0.4.4",
+ "weezl",
+]
+
+[[package]]
+name = "tinyvec"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
+dependencies = [
+ "tinyvec_macros",
+]
+
+[[package]]
+name = "tinyvec_macros"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+
+[[package]]
+name = "toml"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "ttf-parser"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
+
+[[package]]
+name = "ttf-parser"
+version = "0.13.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76dacc724328b3d5e2ed67f9e30cdb56893a34ab239032502cc8f19f8dae4bbc"
+
+[[package]]
+name = "twox-hash"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f559b464de2e2bdabcac6a210d12e9b5a5973c251e102c44c585c71d51bd78e"
+dependencies = [
+ "cfg-if 1.0.0",
+ "rand",
+ "static_assertions",
+]
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+
+[[package]]
+name = "unicase"
+version = "2.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
+dependencies = [
+ "version_check",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
+
+[[package]]
+name = "uuid"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
+[[package]]
+name = "version_check"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+
+[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
+name = "wasi"
+version = "0.10.2+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
+dependencies = [
+ "cfg-if 1.0.0",
+ "wasm-bindgen-macro",
+]
+
+[[package]]
+name = "wasm-bindgen-backend"
+version = "0.2.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
+dependencies = [
+ "bumpalo",
+ "lazy_static",
+ "log",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-backend",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.78"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
+
+[[package]]
+name = "wayland-client"
+version = "0.28.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355"
+dependencies = [
+ "bitflags",
+ "downcast-rs",
+ "libc",
+ "nix 0.20.0",
+ "scoped-tls",
+ "wayland-commons",
+ "wayland-scanner",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-commons"
+version = "0.28.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda"
+dependencies = [
+ "nix 0.20.0",
+ "once_cell",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.28.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a"
+dependencies = [
+ "nix 0.20.0",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-egl"
+version = "0.28.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956"
+dependencies = [
+ "wayland-client",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.28.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f"
+dependencies = [
+ "bitflags",
+ "wayland-client",
+ "wayland-commons",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.28.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "xml-rs",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.28.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8"
+dependencies = [
+ "dlib 0.5.0",
+ "lazy_static",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "weezl"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
 
 [[package]]
 name = "winapi"
@@ -769,8 +2695,102 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+dependencies = [
+ "winapi",
+]
+
 [[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+
+[[package]]
+name = "winit"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79610794594d5e86be473ef7763f604f2159cbac8c94debd00df8fb41e86c2f8"
+dependencies = [
+ "bitflags",
+ "cocoa",
+ "core-foundation 0.9.2",
+ "core-graphics 0.22.3",
+ "core-video-sys",
+ "dispatch",
+ "instant",
+ "lazy_static",
+ "libc",
+ "log",
+ "mio",
+ "mio-misc",
+ "ndk 0.3.0",
+ "ndk-glue 0.3.0",
+ "ndk-sys",
+ "objc",
+ "parking_lot",
+ "percent-encoding",
+ "raw-window-handle 0.3.4",
+ "scopeguard",
+ "smithay-client-toolkit",
+ "wayland-client",
+ "winapi",
+ "x11-dl",
+]
+
+[[package]]
+name = "x11-dl"
+version = "2.19.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59"
+dependencies = [
+ "lazy_static",
+ "libc",
+ "pkg-config",
+]
+
+[[package]]
+name = "xcursor"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7"
+dependencies = [
+ "nom 7.1.0",
+]
+
+[[package]]
+name = "xdg"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a23fe958c70412687039c86f578938b4a0bb50ec788e96bce4d6ab00ddd5803"
+dependencies = [
+ "dirs",
+]
+
+[[package]]
+name = "xi-unicode"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a67300977d3dc3f8034dae89778f502b6ba20b269527b3223ba59c0cf393bb8a"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
+
+[[package]]
+name = "zip"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815"
+dependencies = [
+ "byteorder",
+ "bzip2",
+ "crc32fast",
+ "thiserror",
+]
index 4cd2d12f89cff6bf5ac1e6fb8e1ef04a3c5ead52..516272e2349d5f3def739de7e6ef96c399f57efe 100644 (file)
@@ -7,5 +7,5 @@ edition = "2018"
 
 [dependencies]
 structopt = "0.3"
-macroquad = "0.3"
 image = "0.23"
+ggez = "0.7"
diff --git a/src/display.rs b/src/display.rs
new file mode 100644 (file)
index 0000000..32568d7
--- /dev/null
@@ -0,0 +1,50 @@
+use crate::mpd_handler::MPDHandler;
+use crate::Opt;
+use ggez::event::{self, EventHandler};
+use ggez::graphics::{self, Color, DrawParam, Drawable, Text, TextFragment};
+use ggez::timer::{check_update_time, fps, yield_now};
+use ggez::Context;
+use ggez::GameError;
+use std::sync::{Arc, RwLock};
+
+pub struct MPDDisplay {
+    opts: Opt,
+    mpd_handler: Option<Arc<RwLock<MPDHandler>>>,
+    is_valid: bool,
+    is_initialized: bool,
+    notice_text: Text,
+}
+
+impl MPDDisplay {
+    pub fn new(ctx: &mut Context, opts: Opt) -> Self {
+        Self {
+            opts,
+            mpd_handler: None,
+            is_valid: true,
+            is_initialized: false,
+            notice_text: Text::new(""),
+        }
+    }
+}
+
+impl EventHandler for MPDDisplay {
+    fn update(&mut self, ctx: &mut ggez::Context) -> Result<(), GameError> {
+        if !check_update_time(ctx, 10) {
+            yield_now();
+            return Ok(());
+        }
+
+        self.notice_text = Text::new(TextFragment::new(format!("fps is {}", fps(ctx))));
+
+        yield_now();
+        Ok(())
+    }
+
+    fn draw(&mut self, ctx: &mut ggez::Context) -> Result<(), GameError> {
+        graphics::clear(ctx, Color::BLACK);
+
+        self.notice_text.draw(ctx, DrawParam::default());
+
+        graphics::present(ctx)
+    }
+}
index 47471f6a7a93ecbe9ce1c7a994b4912b0c89d48b..b2d2a2b5a2129aee9d38d30d871cb62cbddebacf 100644 (file)
@@ -1,37 +1,15 @@
-use image::{DynamicImage, ImageResult};
-use macroquad::prelude::*;
-use std::convert::TryInto;
-//use std::fs::File;
-use std::io::{Read, Write};
-use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
-use std::str::FromStr;
-use std::sync::{
-    atomic::{AtomicBool, Ordering},
-    Arc, Mutex,
-};
-use std::thread;
-use std::time::{Duration, Instant};
-use structopt::StructOpt;
+mod display;
+mod mpd_handler;
 
-const BUF_SIZE: usize = 1024 * 4;
-const POLL_DURATION: Duration = Duration::from_secs(10);
-const TEXT_X_OFFSET: f32 = 16.0f32;
-const TEXT_Y_OFFSET: f32 = 16.0f32;
-const TIME_MAX_DIFF: f64 = 2.0f64;
-const INITIAL_FONT_SIZE: u16 = 96;
-const TITLE_INITIAL_FONT_SIZE: u16 = 196;
-const TITLE_INITIAL_MIN_FONT_SIZE: u16 = 44;
-const TITLE_SCREEN_FACTOR: u16 = 800;
-const ARTIST_INITIAL_FONT_SIZE: u16 = 48;
-const TIMER_FONT_SIZE: u16 = 64;
-const SCREEN_DIFF_MARGIN: f32 = 1.0;
-const PROMPT_Y_OFFSET: f32 = 48.0;
-const CHECK_SHARED_WAIT_TIME: f64 = 3.0;
-const CHECK_TRACK_TIMER_MAX_COUNT: u64 = 30;
+use ggez::conf::WindowSetup;
+use ggez::event;
+use ggez::ContextBuilder;
+use std::net::Ipv4Addr;
+use structopt::StructOpt;
 
 #[derive(StructOpt, Debug)]
 #[structopt(name = "mpd_info_screen")]
-struct Opt {
+pub struct Opt {
     host: Ipv4Addr,
     #[structopt(default_value = "6600")]
     port: u16,
@@ -47,988 +25,19 @@ struct Opt {
     enable_prompt_password: bool,
 }
 
-struct Shared {
-    art_data: Vec<u8>,
-    art_data_size: usize,
-    current_song_filename: String,
-    current_song_title: String,
-    current_song_artist: String,
-    current_song_length: f64,
-    current_song_position: f64,
-    current_song_pos_rec: Instant,
-    thread_running: bool,
-    stream: TcpStream,
-    password: String,
-    can_authenticate: bool,
-    can_get_album_art: bool,
-    can_get_album_art_in_dir: bool,
-    can_get_status: bool,
-}
-
-impl Shared {
-    fn new(stream: TcpStream) -> Self {
-        Self {
-            art_data: Vec::new(),
-            art_data_size: 0,
-            current_song_filename: String::new(),
-            current_song_title: String::new(),
-            current_song_artist: String::new(),
-            current_song_length: 0.0,
-            current_song_position: 0.0,
-            current_song_pos_rec: Instant::now(),
-            thread_running: true,
-            stream,
-            password: String::new(),
-            can_authenticate: true,
-            can_get_album_art: true,
-            can_get_album_art_in_dir: true,
-            can_get_status: true,
-        }
-    }
-}
-
-#[derive(Debug, PartialEq, Copy, Clone)]
-enum PollState {
-    None,
-    Password,
-    CurrentSong,
-    Status,
-    ReadPicture,
-    ReadPictureInDir,
-}
-
-#[derive(Debug, Clone)]
-struct InfoFromShared {
-    filename: String,
-    title: String,
-    artist: String,
-    length: f64,
-    pos: f64,
-    instant_rec: Instant,
-    error_text: String,
-}
-
-fn get_connection(host: Ipv4Addr, port: u16) -> Result<TcpStream, String> {
-    let stream = TcpStream::connect_timeout(
-        &SocketAddr::new(IpAddr::V4(host), port),
-        Duration::from_secs(5),
-    )
-    .map_err(|_| String::from("Failed to get connection"))?;
-
-    Ok(stream)
-}
-
-fn check_next_chars(
-    buf: &[u8],
-    idx: usize,
-    saved: &mut Vec<u8>,
-) -> Result<(char, u8), (String, u8)> {
-    if idx >= buf.len() {
-        return Err((String::from("idx out of bounds"), 0u8));
-    }
-    if buf[idx] & 0b10000000 == 0 {
-        let result_str = String::from_utf8(vec![buf[idx]]);
-        if let Ok(mut s) = result_str {
-            let popped_char = s.pop();
-            if s.is_empty() {
-                Ok((popped_char.unwrap(), 1u8))
-            } else {
-                Err((String::from("Not one-byte UTF-8 char"), 0u8))
-            }
-        } else {
-            Err((String::from("Not one-byte UTF-8 char"), 0u8))
-        }
-    } else if buf[idx] & 0b11100000 == 0b11000000 {
-        if idx + 1 >= buf.len() {
-            saved.push(buf[idx]);
-            return Err((
-                String::from("Is two-byte UTF-8, but not enough bytes provided"),
-                1u8,
-            ));
-        }
-        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1]]);
-        if let Ok(mut s) = result_str {
-            let popped_char = s.pop();
-            if s.is_empty() {
-                Ok((popped_char.unwrap(), 2u8))
-            } else {
-                Err((String::from("Not two-byte UTF-8 char"), 0u8))
-            }
-        } else {
-            Err((String::from("Not two-byte UTF-8 char"), 0u8))
-        }
-    } else if buf[idx] & 0b11110000 == 0b11100000 {
-        if idx + 2 >= buf.len() {
-            for c in buf.iter().skip(idx) {
-                saved.push(*c);
-            }
-            return Err((
-                String::from("Is three-byte UTF-8, but not enough bytes provided"),
-                (idx + 3 - buf.len()) as u8,
-            ));
-        }
-        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1], buf[idx + 2]]);
-        if let Ok(mut s) = result_str {
-            let popped_char = s.pop();
-            if s.is_empty() {
-                Ok((popped_char.unwrap(), 3u8))
-            } else {
-                Err((String::from("Not three-byte UTF-8 char"), 0u8))
-            }
-        } else {
-            Err((String::from("Not three-byte UTF-8 char"), 0u8))
-        }
-    } else if buf[idx] & 0b11111000 == 0b11110000 {
-        if idx + 3 >= buf.len() {
-            for c in buf.iter().skip(idx) {
-                saved.push(*c);
-            }
-            return Err((
-                String::from("Is four-byte UTF-8, but not enough bytes provided"),
-                (idx + 4 - buf.len()) as u8,
-            ));
-        }
-        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1], buf[idx + 2]]);
-        if let Ok(mut s) = result_str {
-            let popped_char = s.pop();
-            if s.is_empty() {
-                Ok((popped_char.unwrap(), 4u8))
-            } else {
-                Err((String::from("Not four-byte UTF-8 char"), 0u8))
-            }
-        } else {
-            Err((String::from("Not four-byte UTF-8 char"), 0u8))
-        }
-    } else {
-        Err((String::from("Invalid UTF-8 char"), 0u8))
-    }
-}
-
-fn read_line(
-    buf: &mut Vec<u8>,
-    count: usize,
-    saved: &mut Vec<u8>,
-    init: bool,
-) -> Result<String, (String, String)> {
-    let mut result = String::new();
-
-    let mut buf_to_read: Vec<u8> = Vec::with_capacity(saved.len() + buf.len());
-
-    if !saved.is_empty() {
-        buf_to_read.append(saved);
-    }
-    buf_to_read.append(buf);
-
-    let mut prev_three: Vec<char> = Vec::with_capacity(4);
-
-    let mut skip_count = 0;
-    for idx in 0..count {
-        if skip_count > 0 {
-            skip_count -= 1;
-            continue;
-        }
-        let next_char_result = check_next_chars(&buf_to_read, idx, saved);
-        if let Ok((c, s)) = next_char_result {
-            if !init {
-                prev_three.push(c);
-                if prev_three.len() > 3 {
-                    prev_three.remove(0);
-                }
-                if ['O', 'K', '\n'] == prev_three.as_slice() && idx + 1 == count {
-                    buf_to_read = buf_to_read.split_off(2);
-                    result = String::from("OK");
-                    buf.append(&mut buf_to_read);
-                    //println!("WARNING: OK was reached"); // DEBUG
-                    return Ok(result);
-                }
-            }
-            if c == '\n' {
-                buf_to_read = buf_to_read.split_off(idx + s as usize);
-                buf.append(&mut buf_to_read);
-                return Ok(result);
-            }
-            result.push(c);
-            skip_count = s - 1;
-        } else if let Err((msg, count)) = next_char_result {
-            //println!("ERROR: {}", msg); // DEBUG
-            for i in 0..count {
-                saved.push(buf_to_read[idx + i as usize]);
-            }
-            buf_to_read = buf_to_read.split_off(idx);
-            buf.append(&mut buf_to_read);
-            return Err((msg, result));
-        } else {
-            unreachable!();
-        }
-    }
-
-    *saved = buf_to_read;
-    Err((String::from("Newline not reached"), result))
-}
-
-//fn debug_write_albumart_to_file(data: &[u8]) -> Result<(), String> {
-//    let mut f = File::create("albumartOut.jpg")
-//        .map_err(|_| String::from("Failed to open file for writing albumart"))?;
-//    f.write_all(data)
-//        .map_err(|_| String::from("Failed to write to albumartOut.jpg"))?;
-//
-//    Ok(())
-//}
-
-fn info_loop(shared_data: Arc<Mutex<Shared>>, dirty_flag: Arc<AtomicBool>) -> Result<(), String> {
-    let mut buf: [u8; BUF_SIZE] = [0; BUF_SIZE];
-    let mut init: bool = true;
-    let mut saved: Vec<u8> = Vec::new();
-    let mut saved_str: String = String::new();
-    let mut authenticated: bool = false;
-    let mut song_title_get_time: Instant = Instant::now() - Duration::from_secs(10);
-    let mut song_pos_get_time: Instant = Instant::now() - Duration::from_secs(10);
-    let mut song_length_get_time: Instant = Instant::now() - Duration::from_secs(10);
-    let mut current_binary_size: usize = 0;
-    let mut poll_state = PollState::None;
-    let mut did_check_overtime = false;
-    let mut force_get_currentsong = false;
-    let mut force_get_status = false;
-    'main: loop {
-        if !shared_data
-            .lock()
-            .map_err(|_| String::from("Failed to get shared_data.thread_running"))?
-            .thread_running
-        {
-            break;
-        }
-
-        // read block
-        {
-            let lock_result = shared_data.try_lock();
-            if let Ok(mut lock) = lock_result {
-                let read_result = lock.stream.read(&mut buf);
-                if let Ok(count) = read_result {
-                    let mut read_vec: Vec<u8> = Vec::from(&buf[0..count]);
-                    //println!("{}", String::from_utf8(read_vec.clone()).unwrap_or("Unknown reply".into())); // DEBUG
-                    loop {
-                        let mut count = read_vec.len();
-                        if current_binary_size > 0 {
-                            if current_binary_size <= count {
-                                lock.art_data
-                                    .extend_from_slice(&read_vec[0..current_binary_size]);
-                                read_vec = read_vec.split_off(current_binary_size + 1);
-                                count = read_vec.len();
-                                current_binary_size = 0;
-                                poll_state = PollState::None;
-                                dirty_flag.store(true, Ordering::Relaxed);
-                                //println!(
-                                //    "art_data len is {} after fully reading",
-                                //    lock.art_data.len()
-                                //);
-                                // DEBUG
-                                //let write_file_result = debug_write_albumart_to_file(&lock.art_data);
-                            } else {
-                                lock.art_data.extend_from_slice(&read_vec[0..count]);
-                                current_binary_size -= count;
-                                //println!("art_data len is {}", lock.art_data.len());
-                                continue 'main;
-                            }
-                        }
-                        let read_line_result = read_line(&mut read_vec, count, &mut saved, init);
-                        if let Ok(mut line) = read_line_result {
-                            //println!("read_line_result line: \"{}\"", line); // DEBUG
-                            line = saved_str + &line;
-                            saved_str = String::new();
-                            if init {
-                                if line.starts_with("OK MPD ") {
-                                    init = false;
-                                    println!("Got initial \"OK\" from MPD");
-                                    poll_state = PollState::None;
-                                    break;
-                                } else {
-                                    return Err(String::from(
-                                        "Did not get expected init message from MPD",
-                                    ));
-                                }
-                            } else {
-                                //println!("Got response: {}", line); // DEBUG
-                                if line.starts_with("OK") {
-                                    match poll_state {
-                                        PollState::Password => authenticated = true,
-                                        PollState::ReadPicture => {
-                                            if lock.art_data.is_empty() {
-                                                lock.can_get_album_art = false;
-                                                dirty_flag.store(true, Ordering::Relaxed);
-                                                println!("No embedded album art");
-                                            }
-                                        }
-                                        PollState::ReadPictureInDir => {
-                                            if lock.art_data.is_empty() {
-                                                lock.can_get_album_art_in_dir = false;
-                                                dirty_flag.store(true, Ordering::Relaxed);
-                                                println!("No album art in dir");
-                                            }
-                                        }
-                                        _ => (),
-                                    }
-                                    poll_state = PollState::None;
-                                    break;
-                                } else if line.starts_with("ACK") {
-                                    println!("ERROR: {}", line);
-                                    match poll_state {
-                                        PollState::Password => {
-                                            lock.can_authenticate = false;
-                                            dirty_flag.store(true, Ordering::Relaxed);
-                                        }
-                                        PollState::CurrentSong | PollState::Status => {
-                                            lock.can_get_status = false;
-                                            dirty_flag.store(true, Ordering::Relaxed);
-                                        }
-                                        PollState::ReadPicture => {
-                                            lock.can_get_album_art = false;
-                                            dirty_flag.store(true, Ordering::Relaxed);
-                                            println!("Failed to get readpicture");
-                                        }
-                                        PollState::ReadPictureInDir => {
-                                            lock.can_get_album_art_in_dir = false;
-                                            dirty_flag.store(true, Ordering::Relaxed);
-                                            println!("Failed to get albumart");
-                                        }
-                                        _ => (),
-                                    }
-                                    poll_state = PollState::None;
-                                } else if line.starts_with("file: ") {
-                                    //println!("Got \"file:\" : \"{}\"", line); // DEBUG
-                                    let song_file = line.split_off(6);
-                                    if song_file != lock.current_song_filename {
-                                        lock.current_song_filename = song_file;
-                                        lock.art_data.clear();
-                                        lock.art_data_size = 0;
-                                        lock.can_get_album_art = true;
-                                        lock.can_get_album_art_in_dir = true;
-                                        //println!("Got different song file, clearing art_data...");
-                                        lock.current_song_title.clear();
-                                        lock.current_song_artist.clear();
-                                        lock.current_song_length = 0.0;
-                                        lock.current_song_position = 0.0;
-                                        did_check_overtime = false;
-                                        force_get_status = true;
-                                    }
-                                    dirty_flag.store(true, Ordering::Relaxed);
-                                    song_title_get_time = Instant::now();
-                                } else if line.starts_with("elapsed: ") {
-                                    //println!("Got \"elapsed:\" : \"{}\"", line); // DEBUG
-                                    let parse_pos_result = f64::from_str(&line.split_off(9));
-                                    if let Ok(value) = parse_pos_result {
-                                        lock.current_song_position = value;
-                                        dirty_flag.store(true, Ordering::Relaxed);
-                                        song_pos_get_time = Instant::now();
-                                        lock.current_song_pos_rec = Instant::now();
-                                    } else {
-                                        println!("Got error trying to get current_song_position");
-                                    }
-                                } else if line.starts_with("duration: ") {
-                                    //println!("Got \"duration:\" : \"{}\"", line); // DEBUG
-                                    let parse_pos_result = f64::from_str(&line.split_off(10));
-                                    if let Ok(value) = parse_pos_result {
-                                        lock.current_song_length = value;
-                                        dirty_flag.store(true, Ordering::Relaxed);
-                                        song_length_get_time = Instant::now();
-                                    }
-                                } else if line.starts_with("size: ") {
-                                    //println!("Got \"size:\" : \"{}\"", line); // DEBUG
-                                    let parse_artsize_result = usize::from_str(&line.split_off(6));
-                                    if let Ok(value) = parse_artsize_result {
-                                        lock.art_data_size = value;
-                                        dirty_flag.store(true, Ordering::Relaxed);
-                                    }
-                                } else if line.starts_with("binary: ") {
-                                    //println!("Got \"binary:\" : \"{}\"", line); // DEBUG
-                                    let parse_artbinarysize_result =
-                                        usize::from_str(&line.split_off(8));
-                                    if let Ok(value) = parse_artbinarysize_result {
-                                        current_binary_size = value;
-                                    }
-                                } else if line.starts_with("Title: ") {
-                                    //println!("Got \"Title:\" : \"{}\"", line); // DEBUG
-                                    lock.current_song_title = line.split_off(7);
-                                } else if line.starts_with("Artist: ") {
-                                    //println!("Got \"Artist:\" : \"{}\"", line); // DEBUG
-                                    lock.current_song_artist = line.split_off(8);
-                                }
-                            }
-                        } else if let Err((msg, read_line_in_progress)) = read_line_result {
-                            println!("Error during \"read_line\": {}", msg);
-                            saved_str = read_line_in_progress;
-                            break;
-                        } else {
-                            unreachable!();
-                        }
-                    }
-                }
-            } else {
-                println!("INFO: Temporarily failed to acquire lock for reading from tcp stream");
-            }
-        }
-
-        // write block
-        if poll_state == PollState::None {
-            let lock_result = shared_data.try_lock();
-            if let Ok(mut lock) = lock_result {
-                // first check if overtime
-                if !did_check_overtime
-                    && lock.current_song_position + song_pos_get_time.elapsed().as_secs_f64() - 0.2
-                        > lock.current_song_length
-                {
-                    did_check_overtime = true;
-                    force_get_currentsong = true;
-                    //println!("set \"force_get_currentsong\""); // DEBUG
-                }
-
-                if !authenticated && !lock.password.is_empty() && lock.can_authenticate {
-                    let p = lock.password.clone();
-                    let write_result = lock.stream.write(format!("password {}\n", p).as_bytes());
-                    if write_result.is_ok() {
-                        poll_state = PollState::Password;
-                        //println!("password"); // DEBUG
-                    } else if let Err(e) = write_result {
-                        println!("Got error requesting authentication: {}", e);
-                    }
-                } else if (song_title_get_time.elapsed() > POLL_DURATION || force_get_currentsong)
-                    && lock.can_get_status
-                {
-                    force_get_currentsong = false;
-                    let write_result = lock.stream.write(b"currentsong\n");
-                    if let Err(e) = write_result {
-                        println!("Got error requesting currentsong info: {}", e);
-                    } else {
-                        poll_state = PollState::CurrentSong;
-                        //println!("currentsong"); // DEBUG
-                    }
-                } else if (song_length_get_time.elapsed() > POLL_DURATION
-                    || song_pos_get_time.elapsed() > POLL_DURATION
-                    || force_get_status)
-                    && lock.can_get_status
-                {
-                    force_get_status = false;
-                    let write_result = lock.stream.write(b"status\n");
-                    if let Err(e) = write_result {
-                        println!("Got error requesting status: {}", e);
-                    } else {
-                        poll_state = PollState::Status;
-                        //println!("status"); // DEBUG
-                    }
-                } else if (lock.art_data.is_empty() || lock.art_data.len() != lock.art_data_size)
-                    && !lock.current_song_filename.is_empty()
-                {
-                    let title = lock.current_song_filename.clone();
-                    let art_data_length = lock.art_data.len();
-                    if lock.can_get_album_art {
-                        let write_result = lock.stream.write(
-                            format!("readpicture \"{}\" {}\n", title, art_data_length).as_bytes(),
-                        );
-                        if let Err(e) = write_result {
-                            println!("Got error requesting albumart: {}", e);
-                        } else {
-                            poll_state = PollState::ReadPicture;
-                            //println!("readpicture"); // DEBUG
-                        }
-                    } else if lock.can_get_album_art_in_dir {
-                        let write_result = lock.stream.write(
-                            format!("albumart \"{}\" {}\n", title, art_data_length).as_bytes(),
-                        );
-                        if let Err(e) = write_result {
-                            println!("Got error requesting albumart in dir: {}", e);
-                        } else {
-                            poll_state = PollState::ReadPictureInDir;
-                            //println!("albumart"); // DEBUG
-                        }
-                    }
-                }
-            } else {
-                println!("INFO: Temporarily failed to acquire lock for writing to tcp stream");
-            }
-        } else {
-            // DEBUG
-            //println!("poll_state == {:?}, skipping write...", poll_state);
-        }
-
-        if poll_state != PollState::ReadPicture && poll_state != PollState::ReadPictureInDir {
-            thread::sleep(Duration::from_millis(50));
-        }
-    }
-    Ok(())
-}
-
-fn get_info_from_shared(shared: Arc<Mutex<Shared>>) -> Result<InfoFromShared, ()> {
-    let mut filename: String = String::new();
-    let mut title: String = String::new();
-    let mut artist: String = String::new();
-    let mut length: f64 = 0.0;
-    let mut pos: f64 = 0.0;
-    let mut instant_rec: Instant = Instant::now();
-    let mut error_text = String::new();
-    if let Ok(lock) = shared.lock() {
-        filename = lock.current_song_filename.clone();
-        title = lock.current_song_title.clone();
-        artist = lock.current_song_artist.clone();
-        length = lock.current_song_length;
-        pos = lock.current_song_position;
-        instant_rec = lock.current_song_pos_rec;
-        //println!("Current song: {}", lock.current_song_filename);
-        //println!("Current song length: {}", lock.current_song_length);
-        //println!("Current song position: {}", lock.current_song_position);
-        if !lock.can_authenticate {
-            error_text = String::from("Failed to authenticate to mpd");
-        } else if !lock.can_get_status {
-            error_text = String::from("Failed to get status from mpd");
-        } else if !lock.can_get_album_art && !lock.can_get_album_art_in_dir {
-            error_text = String::from("Failed to get albumart from mpd");
-        }
-    }
-
-    Ok(InfoFromShared {
-        filename,
-        title,
-        artist,
-        length,
-        pos,
-        instant_rec,
-        error_text,
-    })
-}
-
-fn window_conf() -> Conf {
-    Conf {
-        window_title: String::from("mpd info screen"),
-        fullscreen: false,
-        window_width: 800,
-        window_height: 800,
-        ..Default::default()
-    }
-}
-
-fn seconds_to_time(seconds: f64) -> String {
-    let seconds_int: u64 = seconds.floor() as u64;
-    let minutes = seconds_int / 60;
-    let new_seconds: f64 = seconds - (minutes * 60) as f64;
-    let mut result: String;
-    if minutes > 0 {
-        result = minutes.to_string();
-        result.push(':');
-        if new_seconds < 10.0 {
-            result.push('0');
-        }
-    } else {
-        result = String::new();
-    }
-    result.push_str(&new_seconds.to_string());
-    let idx_result = result.find('.');
-    if let Some(idx) = idx_result {
-        result.truncate(idx + 2);
-    }
-
-    result
-}
-
-#[macroquad::main(window_conf)]
-async fn main() -> Result<(), String> {
+fn main() -> Result<(), String> {
     let opt = Opt::from_args();
     println!("Got host addr == {}, port == {}", opt.host, opt.port);
 
-    let mut password: Option<String> = opt.password;
-
-    if opt.enable_prompt_password {
-        let mut input: String = String::new();
-        let mut asterisks: String = String::new();
-        let mut dirty: bool = true;
-        'prompt_loop: loop {
-            draw_text(
-                "Input password:",
-                TEXT_X_OFFSET,
-                TEXT_Y_OFFSET + PROMPT_Y_OFFSET,
-                PROMPT_Y_OFFSET,
-                WHITE,
-            );
-            if let Some(k) = get_last_key_pressed() {
-                if k == KeyCode::Backspace {
-                    input.pop();
-                    dirty = true;
-                } else if k == KeyCode::Enter {
-                    password = Some(input);
-                    break 'prompt_loop;
-                }
-            }
-            if let Some(c) = get_char_pressed() {
-                input.push(c);
-                dirty = true;
-            }
-            if dirty {
-                let input_count = input.chars().count();
-                if asterisks.len() < input_count {
-                    for _ in 0..(input_count - asterisks.len()) {
-                        asterisks.push('*');
-                    }
-                } else {
-                    asterisks.truncate(input_count);
-                }
-                dirty = false;
-            }
-            draw_text(
-                &asterisks,
-                TEXT_X_OFFSET,
-                TEXT_Y_OFFSET + PROMPT_Y_OFFSET * 2.0,
-                PROMPT_Y_OFFSET,
-                WHITE,
-            );
-
-            next_frame().await
-        }
-    }
-
-    let connection = get_connection(opt.host, opt.port)?;
-    connection
-        .set_read_timeout(Some(Duration::from_millis(50)))
-        .expect("Should be able to set timeout for TcpStream reads");
-    connection
-        .set_write_timeout(Some(Duration::from_secs(1)))
-        .expect("Should be able to set timeout for TcpStream writes");
-
-    let shared_data = Arc::new(Mutex::new(Shared::new(connection)));
-    if let Some(p) = password {
-        shared_data
-            .lock()
-            .expect("Should be able to get mutex lock")
-            .password = p;
-    }
-    let atomic_dirty_flag = Arc::new(AtomicBool::new(true));
-    let thread_shared_data = shared_data.clone();
-    let thread_dirty_flag = atomic_dirty_flag.clone();
-
-    let child = thread::spawn(move || {
-        info_loop(thread_shared_data, thread_dirty_flag).expect("Failure during info_loop");
-    });
-
-    let mut timer: f64 = 0.0;
-    let mut track_timer: f64 = 0.0;
-    let mut filename: String = String::new();
-    let mut title: String = String::new();
-    let mut artist: String = String::new();
-    let mut art_texture: Option<Texture2D> = None;
-    let mut art_draw_params: Option<DrawTextureParams> = None;
-    let mut art_draw_width: f32 = 32.0;
-    let mut art_draw_height: f32 = 32.0;
-    let mut filename_font_size: Option<u16> = None;
-    let mut text_dim: TextDimensions = measure_text("undefined", None, 24, 1.0);
-    let mut prev_width = screen_width();
-    let mut prev_height = screen_height();
-    let mut error_text = String::new();
-    let mut title_dim_opt: Option<TextDimensions> = None;
-    let mut title_font_size: u16 = INITIAL_FONT_SIZE;
-    let mut artist_dim_opt: Option<TextDimensions> = None;
-    let mut artist_font_size: u16 = ARTIST_INITIAL_FONT_SIZE;
-    let mut temp_offset_y: f32;
-    let mut check_due_to_track_timer_count: u64 = 0;
-
-    'macroquad_main: loop {
-        let dt: f64 = get_frame_time() as f64;
-        clear_background(BLACK);
-
-        if is_key_pressed(KeyCode::Escape) || is_key_pressed(KeyCode::Q) {
-            break 'macroquad_main;
-        } else if (prev_width - screen_width()).abs() > SCREEN_DIFF_MARGIN
-            || (prev_height - screen_height()).abs() > SCREEN_DIFF_MARGIN
-        {
-            prev_width = screen_width();
-            prev_height = screen_height();
-            filename_font_size = None;
-            title_dim_opt = None;
-            artist_dim_opt = None;
-            art_draw_params = None;
-        }
-
-        timer -= dt;
-        track_timer -= dt;
-        if timer < 0.0
-            || (track_timer < 0.0 && check_due_to_track_timer_count < CHECK_TRACK_TIMER_MAX_COUNT)
-        {
-            if track_timer < 0.0 {
-                check_due_to_track_timer_count += 1;
-                //println!("check_due_to_track_timer_count incremented"); // DEBUG
-            }
-            timer = CHECK_SHARED_WAIT_TIME;
-            let dirty_flag = atomic_dirty_flag.load(Ordering::Relaxed);
-            if dirty_flag
-                || (track_timer < 0.0
-                    && check_due_to_track_timer_count < CHECK_TRACK_TIMER_MAX_COUNT)
-            {
-                if dirty_flag {
-                    atomic_dirty_flag.store(false, Ordering::Relaxed);
-                }
-                let info_result = get_info_from_shared(shared_data.clone());
-                if let Ok(info) = info_result {
-                    if info.filename != filename {
-                        filename = info.filename;
-                        art_texture = None;
-                        filename_font_size = None;
-                        title.clear();
-                        title_dim_opt = None;
-                        artist.clear();
-                        artist_dim_opt = None;
-                        art_draw_params = None;
-                        check_due_to_track_timer_count = 0;
-                    }
-                    let duration_since = info.instant_rec.elapsed();
-                    let recorded_time = info.length - info.pos - duration_since.as_secs_f64();
-                    if (recorded_time - track_timer).abs() > TIME_MAX_DIFF {
-                        track_timer = info.length - info.pos - duration_since.as_secs_f64();
-                    }
-                    if !info.error_text.is_empty() {
-                        error_text = info.error_text;
-                    }
-                    if !info.title.is_empty() {
-                        title = info.title;
-                    }
-                    if !info.artist.is_empty() {
-                        artist = info.artist;
-                    }
-                }
-            }
-
-            if art_texture.is_none() {
-                let mut image_result: Option<ImageResult<DynamicImage>> = None;
-                {
-                    let lock_result = shared_data.lock();
-                    if let Ok(l) = lock_result {
-                        image_result = Some(image::load_from_memory(&l.art_data));
-                    }
-                }
-                if let Some(Ok(dynimg)) = image_result {
-                    let img_buf = dynimg.to_rgba8();
-                    art_texture = Some(Texture2D::from_rgba8(
-                        img_buf
-                            .width()
-                            .try_into()
-                            .expect("width of image should fit in u16"),
-                        img_buf
-                            .height()
-                            .try_into()
-                            .expect("height of image should fit into u16"),
-                        &img_buf.to_vec(),
-                    ));
-                }
-            }
-        }
-
-        if let Some(texture) = art_texture {
-            if texture.width() > prev_width || texture.height() > prev_height {
-                if art_draw_params.is_none() {
-                    let ratio: f32 = texture.width() / texture.height();
-                    // try filling to height
-                    art_draw_height = prev_height;
-                    art_draw_width = prev_height * ratio;
-                    if art_draw_width > prev_width {
-                        // try filling to width instead
-                        art_draw_width = prev_width;
-                        art_draw_height = prev_width / ratio;
-                    }
-
-                    art_draw_params = Some(DrawTextureParams {
-                        dest_size: Some(Vec2::new(art_draw_width, art_draw_height)),
-                        ..Default::default()
-                    });
-                }
-                draw_texture_ex(
-                    texture,
-                    (prev_width - art_draw_width) / 2.0f32,
-                    (prev_height - art_draw_height) / 2.0f32,
-                    WHITE,
-                    art_draw_params.as_ref().unwrap().clone(),
-                );
-            } else {
-                draw_texture(
-                    texture,
-                    (prev_width - texture.width()) / 2.0f32,
-                    (prev_height - texture.height()) / 2.0f32,
-                    WHITE,
-                );
-            }
-        }
-
-        if !is_key_down(KeyCode::H) {
-            temp_offset_y = 0.0;
-            if !filename.is_empty() && !opt.disable_show_filename {
-                if filename_font_size.is_none() {
-                    filename_font_size = Some(INITIAL_FONT_SIZE);
-                    loop {
-                        text_dim = measure_text(
-                            &filename,
-                            None,
-                            *filename_font_size.as_ref().unwrap(),
-                            1.0f32,
-                        );
-                        if text_dim.width + TEXT_X_OFFSET > prev_width {
-                            filename_font_size = filename_font_size.map(|s| s - 4);
-                        } else {
-                            break;
-                        }
-
-                        if *filename_font_size.as_ref().unwrap() <= 4 {
-                            filename_font_size = Some(4);
-                            text_dim = measure_text(
-                                &filename,
-                                None,
-                                *filename_font_size.as_ref().unwrap(),
-                                1.0f32,
-                            );
-                            break;
-                        }
-                    }
-                }
-                draw_rectangle(
-                    TEXT_X_OFFSET,
-                    prev_height - TEXT_Y_OFFSET - text_dim.height,
-                    text_dim.width,
-                    text_dim.height,
-                    Color::new(0.0, 0.0, 0.0, 0.4),
-                );
-                draw_text(
-                    &filename,
-                    TEXT_X_OFFSET,
-                    prev_height - TEXT_Y_OFFSET,
-                    *filename_font_size.as_ref().unwrap() as f32,
-                    WHITE,
-                );
-
-                temp_offset_y += TEXT_Y_OFFSET + text_dim.height;
-            }
-
-            // Get title dimensions early so that artist size is at most title size
-            if !title.is_empty() && !opt.disable_show_title && title_dim_opt.is_none() {
-                let mut length: u16 = title.chars().count().try_into().unwrap_or(u16::MAX);
-                length /= 10;
-                if length <= 2 {
-                    length = 3;
-                }
-                let screen_factor = 4 * screen_width() as u16 / TITLE_SCREEN_FACTOR;
-                title_font_size = TITLE_INITIAL_FONT_SIZE / length as u16 + screen_factor;
-                if title_font_size < TITLE_INITIAL_MIN_FONT_SIZE {
-                    title_font_size = TITLE_INITIAL_MIN_FONT_SIZE;
-                }
-                loop {
-                    title_dim_opt = Some(measure_text(&title, None, title_font_size, 1.0f32));
-                    if title_dim_opt.as_ref().unwrap().width + TEXT_X_OFFSET > prev_width {
-                        title_font_size -= 4;
-                        if title_font_size < 4 {
-                            title_font_size = 4;
-                            title_dim_opt =
-                                Some(measure_text(&title, None, title_font_size, 1.0f32));
-                            break;
-                        }
-                    } else {
-                        break;
-                    }
-                }
-            }
-
-            if !artist.is_empty() && !opt.disable_show_artist {
-                if artist_dim_opt.is_none() {
-                    if !title.is_empty() && !opt.disable_show_title {
-                        artist_font_size = title_font_size;
-                    } else {
-                        artist_font_size = ARTIST_INITIAL_FONT_SIZE;
-                    }
-                    loop {
-                        artist_dim_opt =
-                            Some(measure_text(&artist, None, artist_font_size, 1.0f32));
-                        if artist_dim_opt.as_ref().unwrap().width + TEXT_X_OFFSET > prev_width {
-                            artist_font_size -= 4;
-                            if artist_font_size < 4 {
-                                artist_font_size = 4;
-                                artist_dim_opt =
-                                    Some(measure_text(&artist, None, artist_font_size, 1.0f32));
-                                break;
-                            }
-                        } else {
-                            break;
-                        }
-                    }
-                }
-
-                let temp_dim_opt = artist_dim_opt.as_ref().unwrap();
-                draw_rectangle(
-                    TEXT_X_OFFSET,
-                    prev_height - temp_offset_y - TEXT_Y_OFFSET - temp_dim_opt.height,
-                    temp_dim_opt.width,
-                    temp_dim_opt.height,
-                    Color::new(0.0, 0.0, 0.0, 0.4),
-                );
-                draw_text(
-                    &artist,
-                    TEXT_X_OFFSET,
-                    prev_height - temp_offset_y - TEXT_Y_OFFSET,
-                    artist_font_size.into(),
-                    WHITE,
-                );
-
-                temp_offset_y += TEXT_Y_OFFSET + temp_dim_opt.height;
-            }
-
-            if !title.is_empty() && !opt.disable_show_title {
-                let temp_dim_opt = title_dim_opt.as_ref().unwrap();
-                draw_rectangle(
-                    TEXT_X_OFFSET,
-                    prev_height - temp_offset_y - TEXT_Y_OFFSET - temp_dim_opt.height,
-                    temp_dim_opt.width,
-                    temp_dim_opt.height,
-                    Color::new(0.0, 0.0, 0.0, 0.4),
-                );
-                draw_text(
-                    &title,
-                    TEXT_X_OFFSET,
-                    prev_height - temp_offset_y - TEXT_Y_OFFSET,
-                    title_font_size.into(),
-                    WHITE,
-                );
-
-                temp_offset_y += TEXT_Y_OFFSET + temp_dim_opt.height;
-            }
-
-            let timer_string = seconds_to_time(track_timer);
-            let timer_dim = measure_text(&timer_string, None, TIMER_FONT_SIZE, 1.0f32);
-            draw_rectangle(
-                TEXT_X_OFFSET,
-                prev_height - temp_offset_y - TEXT_Y_OFFSET - timer_dim.height,
-                timer_dim.width,
-                timer_dim.height,
-                Color::new(0.0, 0.0, 0.0, 0.4),
-            );
-            draw_text(
-                &timer_string,
-                TEXT_X_OFFSET,
-                prev_height - temp_offset_y - TEXT_Y_OFFSET,
-                TIMER_FONT_SIZE.into(),
-                WHITE,
-            );
-
-            if !error_text.is_empty() {
-                draw_text(&error_text, 0.0, 32.0f32, 32.0f32, WHITE);
-            }
-        }
-
-        next_frame().await
-    }
-
-    println!("Stopping thread...");
-    shared_data
-        .lock()
-        .map_err(|_| String::from("Failed to get shared_data.thread_running in main"))?
-        .thread_running = false;
-
-    //println!("Waiting on thread...");
-    thread::sleep(Duration::from_millis(200));
-
-    println!("Joining on thread...");
-    child.join().expect("Should be able to join on thread");
+    let (mut ctx, event_loop) = ContextBuilder::new("mpd_info_screen", "Stephen Seo")
+        .window_setup(WindowSetup {
+            title: "mpd info screen".into(),
+            ..Default::default()
+        })
+        .build()
+        .expect("Failed to create ggez context");
 
-    //get_info_from_shared(shared_data.clone(), true)
-    //    .expect("Should be able to get info from shared");
+    let display = display::MPDDisplay::new(&mut ctx, opt);
 
-    Ok(())
+    event::run(ctx, event_loop, display);
 }
diff --git a/src/mpd_handler.rs b/src/mpd_handler.rs
new file mode 100644 (file)
index 0000000..546f7c2
--- /dev/null
@@ -0,0 +1,604 @@
+use std::io::{Read, Write};
+use std::net::{IpAddr, Ipv4Addr, SocketAddr, TcpStream};
+use std::str::FromStr;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::{Arc, Mutex, RwLock};
+use std::thread;
+use std::time::{Duration, Instant};
+
+const SLEEP_DURATION: Duration = Duration::from_millis(100);
+const POLL_DURATION: Duration = Duration::from_secs(10);
+const BUF_SIZE: usize = 1024 * 4;
+
+#[derive(Debug, PartialEq, Copy, Clone)]
+enum PollState {
+    None,
+    Password,
+    CurrentSong,
+    Status,
+    ReadPicture,
+    ReadPictureInDir,
+}
+
+#[derive(Debug, Clone)]
+pub struct InfoFromShared {
+    filename: String,
+    title: String,
+    artist: String,
+    length: f64,
+    pos: f64,
+    error_text: String,
+}
+
+pub struct MPDHandler {
+    art_data: Vec<u8>,
+    art_data_size: usize,
+    current_song_filename: String,
+    current_song_title: String,
+    current_song_artist: String,
+    current_song_length: f64,
+    current_song_position: f64,
+    current_binary_size: usize,
+    poll_state: PollState,
+    thread_running: bool,
+    stream: TcpStream,
+    password: String,
+    error_text: String,
+    can_authenticate: bool,
+    is_authenticated: bool,
+    can_get_album_art: bool,
+    can_get_album_art_in_dir: bool,
+    can_get_status: bool,
+    is_init: bool,
+    did_check_overtime: bool,
+    force_get_status: bool,
+    force_get_current_song: bool,
+    song_title_get_time: Instant,
+    song_pos_get_time: Instant,
+    song_length_get_time: Instant,
+    self_thread: Option<Arc<Mutex<thread::JoinHandle<Result<(), String>>>>>,
+    dirty_flag: Arc<AtomicBool>,
+    stop_flag: Arc<AtomicBool>,
+}
+
+fn check_next_chars(
+    buf: &[u8],
+    idx: usize,
+    saved: &mut Vec<u8>,
+) -> Result<(char, u8), (String, u8)> {
+    if idx >= buf.len() {
+        return Err((String::from("idx out of bounds"), 0u8));
+    }
+    if buf[idx] & 0b10000000 == 0 {
+        let result_str = String::from_utf8(vec![buf[idx]]);
+        if let Ok(mut s) = result_str {
+            let popped_char = s.pop();
+            if s.is_empty() {
+                Ok((popped_char.unwrap(), 1u8))
+            } else {
+                Err((String::from("Not one-byte UTF-8 char"), 0u8))
+            }
+        } else {
+            Err((String::from("Not one-byte UTF-8 char"), 0u8))
+        }
+    } else if buf[idx] & 0b11100000 == 0b11000000 {
+        if idx + 1 >= buf.len() {
+            saved.push(buf[idx]);
+            return Err((
+                String::from("Is two-byte UTF-8, but not enough bytes provided"),
+                1u8,
+            ));
+        }
+        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1]]);
+        if let Ok(mut s) = result_str {
+            let popped_char = s.pop();
+            if s.is_empty() {
+                Ok((popped_char.unwrap(), 2u8))
+            } else {
+                Err((String::from("Not two-byte UTF-8 char"), 0u8))
+            }
+        } else {
+            Err((String::from("Not two-byte UTF-8 char"), 0u8))
+        }
+    } else if buf[idx] & 0b11110000 == 0b11100000 {
+        if idx + 2 >= buf.len() {
+            for c in buf.iter().skip(idx) {
+                saved.push(*c);
+            }
+            return Err((
+                String::from("Is three-byte UTF-8, but not enough bytes provided"),
+                (idx + 3 - buf.len()) as u8,
+            ));
+        }
+        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1], buf[idx + 2]]);
+        if let Ok(mut s) = result_str {
+            let popped_char = s.pop();
+            if s.is_empty() {
+                Ok((popped_char.unwrap(), 3u8))
+            } else {
+                Err((String::from("Not three-byte UTF-8 char"), 0u8))
+            }
+        } else {
+            Err((String::from("Not three-byte UTF-8 char"), 0u8))
+        }
+    } else if buf[idx] & 0b11111000 == 0b11110000 {
+        if idx + 3 >= buf.len() {
+            for c in buf.iter().skip(idx) {
+                saved.push(*c);
+            }
+            return Err((
+                String::from("Is four-byte UTF-8, but not enough bytes provided"),
+                (idx + 4 - buf.len()) as u8,
+            ));
+        }
+        let result_str = String::from_utf8(vec![buf[idx], buf[idx + 1], buf[idx + 2]]);
+        if let Ok(mut s) = result_str {
+            let popped_char = s.pop();
+            if s.is_empty() {
+                Ok((popped_char.unwrap(), 4u8))
+            } else {
+                Err((String::from("Not four-byte UTF-8 char"), 0u8))
+            }
+        } else {
+            Err((String::from("Not four-byte UTF-8 char"), 0u8))
+        }
+    } else {
+        Err((String::from("Invalid UTF-8 char"), 0u8))
+    }
+}
+
+fn read_line(
+    buf: &mut Vec<u8>,
+    saved: &mut Vec<u8>,
+    init: bool,
+) -> Result<String, (String, String)> {
+    let count = buf.len();
+    let mut result = String::new();
+
+    let mut buf_to_read: Vec<u8> = Vec::with_capacity(saved.len() + buf.len());
+
+    if !saved.is_empty() {
+        buf_to_read.append(saved);
+    }
+    buf_to_read.append(buf);
+
+    let mut prev_three: Vec<char> = Vec::with_capacity(4);
+
+    let mut skip_count = 0;
+    for idx in 0..count {
+        if skip_count > 0 {
+            skip_count -= 1;
+            continue;
+        }
+        let next_char_result = check_next_chars(&buf_to_read, idx, saved);
+        if let Ok((c, s)) = next_char_result {
+            if !init {
+                prev_three.push(c);
+                if prev_three.len() > 3 {
+                    prev_three.remove(0);
+                }
+                if ['O', 'K', '\n'] == prev_three.as_slice() && idx + 1 == count {
+                    buf_to_read = buf_to_read.split_off(2);
+                    result = String::from("OK");
+                    buf.append(&mut buf_to_read);
+                    //println!("WARNING: OK was reached"); // DEBUG
+                    return Ok(result);
+                }
+            }
+            if c == '\n' {
+                buf_to_read = buf_to_read.split_off(idx + s as usize);
+                buf.append(&mut buf_to_read);
+                return Ok(result);
+            }
+            result.push(c);
+            skip_count = s - 1;
+        } else if let Err((msg, count)) = next_char_result {
+            //println!("ERROR: {}", msg); // DEBUG
+            for i in 0..count {
+                saved.push(buf_to_read[idx + i as usize]);
+            }
+            buf_to_read = buf_to_read.split_off(idx);
+            buf.append(&mut buf_to_read);
+            return Err((msg, result));
+        } else {
+            unreachable!();
+        }
+    }
+
+    *saved = buf_to_read;
+    Err((String::from("Newline not reached"), result))
+}
+
+fn seconds_to_time(seconds: f64) -> String {
+    let seconds_int: u64 = seconds.floor() as u64;
+    let minutes = seconds_int / 60;
+    let new_seconds: f64 = seconds - (minutes * 60) as f64;
+    let mut result: String;
+    if minutes > 0 {
+        result = minutes.to_string();
+        result.push(':');
+        if new_seconds < 10.0 {
+            result.push('0');
+        }
+    } else {
+        result = String::new();
+    }
+    result.push_str(&new_seconds.to_string());
+    let idx_result = result.find('.');
+    if let Some(idx) = idx_result {
+        result.truncate(idx + 2);
+    }
+
+    result
+}
+
+impl MPDHandler {
+    pub fn new(host: Ipv4Addr, port: u16, password: String) -> Result<Arc<RwLock<Self>>, String> {
+        let stream = TcpStream::connect_timeout(
+            &SocketAddr::new(IpAddr::V4(host), port),
+            Duration::from_secs(5),
+        )
+        .map_err(|_| String::from("Failed to get TCP connection"))?;
+
+        let mut s = Arc::new(RwLock::new(Self {
+            art_data: Vec::new(),
+            art_data_size: 0,
+            current_song_filename: String::new(),
+            current_song_title: String::new(),
+            current_song_artist: String::new(),
+            current_song_length: 0.0,
+            current_song_position: 0.0,
+            current_binary_size: 0,
+            poll_state: PollState::None,
+            thread_running: true,
+            stream,
+            password,
+            error_text: String::new(),
+            can_authenticate: true,
+            is_authenticated: false,
+            can_get_album_art: true,
+            can_get_album_art_in_dir: true,
+            can_get_status: true,
+            is_init: true,
+            did_check_overtime: false,
+            force_get_status: false,
+            force_get_current_song: false,
+            song_title_get_time: Instant::now() - Duration::from_secs(10),
+            song_pos_get_time: Instant::now() - Duration::from_secs(10),
+            song_length_get_time: Instant::now() - Duration::from_secs(10),
+            self_thread: None,
+            dirty_flag: Arc::new(AtomicBool::new(true)),
+            stop_flag: Arc::new(AtomicBool::new(false)),
+        }));
+
+        let mut s_clone = s.clone();
+        let mut thread = Arc::new(Mutex::new(thread::spawn(|| Self::handler_loop(s_clone))));
+
+        s.write()
+            .map_err(|_| String::from("Failed to start MPDHandler thread"))?
+            .self_thread = Some(thread.clone());
+
+        Ok(s)
+    }
+
+    pub fn get_art_data(h: Arc<RwLock<Self>>) -> Result<Vec<u8>, ()> {
+        if let Ok(read_lock) = h.read() {
+            if read_lock.art_data.len() == read_lock.art_data_size {
+                return Ok(read_lock.art_data.clone());
+            }
+        }
+
+        Err(())
+    }
+
+    pub fn can_get_art_data(h: Arc<RwLock<Self>>) -> bool {
+        if let Ok(read_lock) = h.read() {
+            return read_lock.can_get_album_art || read_lock.can_get_album_art_in_dir;
+        }
+
+        false
+    }
+
+    pub fn get_current_song_info(h: Arc<RwLock<Self>>) -> Result<InfoFromShared, ()> {
+        if let Ok(read_lock) = h.read() {
+            return Ok(InfoFromShared {
+                filename: read_lock.current_song_filename.clone(),
+                title: read_lock.current_song_title.clone(),
+                artist: read_lock.current_song_artist.clone(),
+                length: read_lock.current_song_length,
+                pos: read_lock.current_song_position,
+                error_text: read_lock.error_text.clone(),
+            });
+        }
+
+        Err(())
+    }
+
+    fn handler_loop(h: Arc<RwLock<Self>>) -> Result<(), String> {
+        let mut buf: [u8; BUF_SIZE] = [0; BUF_SIZE];
+        let mut saved: Vec<u8> = Vec::new();
+        let mut saved_str: String = String::new();
+        'main: loop {
+            if !Self::is_reading_picture(h.clone()) {
+                thread::sleep(SLEEP_DURATION);
+            }
+
+            if let Err(err_string) =
+                Self::handler_read_block(h.clone(), &mut buf, &mut saved, &mut saved_str)
+            {
+                println!("WARNING: read_block error: {}", err_string);
+            } else if let Err(err_string) = Self::handler_write_block(h.clone()) {
+                println!("WARNING: write_block error: {}", err_string);
+            }
+
+            if let Ok(read_handle) = h.read() {
+                if read_handle.stop_flag.load(Ordering::Relaxed) {
+                    break 'main;
+                }
+            }
+        }
+
+        'exit: loop {
+            if let Ok(mut write_handle) = h.write() {
+                write_handle.self_thread = None;
+                break 'exit;
+            }
+            thread::sleep(SLEEP_DURATION);
+        }
+
+        Ok(())
+    }
+
+    fn handler_read_block(
+        h: Arc<RwLock<Self>>,
+        buf: &mut [u8; BUF_SIZE],
+        saved: &mut Vec<u8>,
+        saved_str: &mut String,
+    ) -> Result<(), String> {
+        let mut write_handle = h
+            .write()
+            .map_err(|_| String::from("Failed to get MPDHandler write lock (read_block)"))?;
+        let read_result = write_handle
+            .stream
+            .read(buf)
+            .map_err(|io_err| format!("Failed to read from TCP stream: {}", io_err))?;
+        if read_result == 0 {
+            return Err(String::from("Got zero bytes from TCP stream"));
+        }
+        let mut buf_vec: Vec<u8> = Vec::from(&buf[0..read_result]);
+
+        'handle_buf: loop {
+            if write_handle.current_binary_size > 0 {
+                if write_handle.current_binary_size <= buf_vec.len() {
+                    let count = write_handle.current_binary_size;
+                    write_handle.art_data.extend_from_slice(&buf_vec[0..count]);
+                    buf_vec = buf_vec.split_off(write_handle.current_binary_size + 1);
+                    write_handle.current_binary_size = 0;
+                    write_handle.poll_state = PollState::None;
+                    write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                } else {
+                    write_handle.art_data.extend_from_slice(&buf_vec);
+                    write_handle.current_binary_size -= buf_vec.len();
+                    break 'handle_buf;
+                }
+            }
+            let read_line_result = read_line(&mut buf_vec, saved, write_handle.is_init);
+            if let Ok(mut line) = read_line_result {
+                line = saved_str.clone() + &line;
+                *saved_str = String::new();
+                if write_handle.is_init {
+                    if line.starts_with("OK MPD ") {
+                        write_handle.is_init = false;
+                        println!("Got initial \"OK\" from MPD");
+                        write_handle.poll_state = PollState::None;
+                        break 'handle_buf;
+                    } else {
+                        return Err(String::from("Did not get expected init message from MPD"));
+                    }
+                } // write_handle.is_init
+
+                if line.starts_with("OK") {
+                    match write_handle.poll_state {
+                        PollState::Password => write_handle.is_authenticated = true,
+                        PollState::ReadPicture => {
+                            if write_handle.art_data.is_empty() {
+                                write_handle.can_get_album_art = false;
+                                write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                                println!("No embedded album art");
+                            }
+                        }
+                        PollState::ReadPictureInDir => {
+                            if write_handle.art_data.is_empty() {
+                                write_handle.can_get_album_art_in_dir = false;
+                                write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                                println!("No album art in dir");
+                            }
+                        }
+                        _ => (),
+                    }
+                    write_handle.poll_state = PollState::None;
+                    break 'handle_buf;
+                } else if line.starts_with("ACK") {
+                    println!("ERROR: {}", line);
+                    match write_handle.poll_state {
+                        PollState::Password => {
+                            write_handle.can_authenticate = false;
+                            write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                        }
+                        PollState::CurrentSong | PollState::Status => {
+                            write_handle.can_get_status = false;
+                            write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                        }
+                        PollState::ReadPicture => {
+                            write_handle.can_get_album_art = false;
+                            write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                            println!("Failed to get readpicture");
+                        }
+                        PollState::ReadPictureInDir => {
+                            write_handle.can_get_album_art_in_dir = false;
+                            write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                            println!("Failed to get albumart");
+                        }
+                        _ => (),
+                    }
+                    write_handle.poll_state = PollState::None;
+                } else if line.starts_with("file: ") {
+                    let song_file = line.split_off(6);
+                    if song_file != write_handle.current_song_filename {
+                        write_handle.current_song_filename = song_file;
+                        write_handle.art_data.clear();
+                        write_handle.art_data_size = 0;
+                        write_handle.can_get_album_art = true;
+                        write_handle.can_get_album_art_in_dir = true;
+                        write_handle.current_song_title.clear();
+                        write_handle.current_song_artist.clear();
+                        write_handle.current_song_length = 0.0;
+                        write_handle.current_song_position = 0.0;
+                        write_handle.did_check_overtime = false;
+                        write_handle.force_get_status = true;
+                    }
+                    write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                    write_handle.song_title_get_time = Instant::now();
+                } else if line.starts_with("elapsed: ") {
+                    let parse_pos_result = f64::from_str(&line.split_off(9));
+                    if let Ok(value) = parse_pos_result {
+                        write_handle.current_song_position = value;
+                        write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                        write_handle.song_pos_get_time = Instant::now();
+                    } else {
+                        println!("WARNING: Failed to parse current song position");
+                    }
+                } else if line.starts_with("duration: ") {
+                    let parse_pos_result = f64::from_str(&line.split_off(10));
+                    if let Ok(value) = parse_pos_result {
+                        write_handle.current_song_length = value;
+                        write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                        write_handle.song_length_get_time = Instant::now();
+                    } else {
+                        println!("WARNING: Failed to parse current song duration");
+                    }
+                } else if line.starts_with("size: ") {
+                    let parse_artsize_result = usize::from_str(&line.split_off(6));
+                    if let Ok(value) = parse_artsize_result {
+                        write_handle.art_data_size = value;
+                        write_handle.dirty_flag.store(true, Ordering::Relaxed);
+                    } else {
+                        println!("WARNING: Failed to parse album art byte size");
+                    }
+                } else if line.starts_with("binary: ") {
+                    let parse_artbinarysize_result = usize::from_str(&line.split_off(8));
+                    if let Ok(value) = parse_artbinarysize_result {
+                        write_handle.current_binary_size = value;
+                    } else {
+                        println!("WARNING: Failed to parse album art chunk byte size");
+                    }
+                } else if line.starts_with("Title: ") {
+                    write_handle.current_song_title = line.split_off(7);
+                } else if line.starts_with("Artist: ") {
+                    write_handle.current_song_artist = line.split_off(8);
+                } else {
+                    println!("WARNING: Got unrecognized line: {}", line);
+                }
+            } else if let Err((msg, read_line_in_progress)) = read_line_result {
+                println!("ERROR read_line: {}", msg);
+                *saved_str = read_line_in_progress;
+                break 'handle_buf;
+            } else {
+                unreachable!();
+            }
+        } // 'handle_buf: loop
+
+        Ok(())
+    }
+
+    fn handler_write_block(h: Arc<RwLock<MPDHandler>>) -> Result<(), String> {
+        let mut write_handle = h
+            .write()
+            .map_err(|_| String::from("Failed to get MPDHandler write lock (write_block)"))?;
+        if write_handle.poll_state == PollState::None {
+            if !write_handle.did_check_overtime
+                && write_handle.current_song_position
+                    + write_handle.song_pos_get_time.elapsed().as_secs_f64()
+                    - 0.2
+                    > write_handle.current_song_length
+            {
+                write_handle.did_check_overtime = true;
+                write_handle.force_get_current_song = true;
+            }
+
+            if !write_handle.is_authenticated
+                && !write_handle.password.is_empty()
+                && write_handle.can_authenticate
+            {
+                let p = write_handle.password.clone();
+                let write_result = write_handle
+                    .stream
+                    .write(format!("password {}\n", p).as_bytes());
+                if write_result.is_ok() {
+                    write_handle.poll_state = PollState::Password;
+                } else if let Err(e) = write_result {
+                    println!("ERROR: Failed to send password for authentication: {}", e);
+                }
+            } else if write_handle.can_get_status
+                && (write_handle.song_title_get_time.elapsed() > POLL_DURATION
+                    || write_handle.force_get_current_song)
+            {
+                write_handle.force_get_current_song = false;
+                let write_result = write_handle.stream.write(b"currentsong\n");
+                if write_result.is_ok() {
+                    write_handle.poll_state = PollState::CurrentSong;
+                } else if let Err(e) = write_result {
+                    println!("ERROR: Failed to request song info over stream: {}", e);
+                }
+            } else if write_handle.can_get_status
+                && (write_handle.song_length_get_time.elapsed() > POLL_DURATION
+                    || write_handle.song_pos_get_time.elapsed() > POLL_DURATION
+                    || write_handle.force_get_status)
+            {
+                write_handle.force_get_status = false;
+                let write_result = write_handle.stream.write(b"status\n");
+                if write_result.is_ok() {
+                    write_handle.poll_state = PollState::Status;
+                } else if let Err(e) = write_result {
+                    println!("ERROR: Failed to request status over stream: {}", e);
+                }
+            } else if (write_handle.art_data.is_empty()
+                || write_handle.art_data.len() != write_handle.art_data_size)
+                && !write_handle.current_song_filename.is_empty()
+            {
+                let title = write_handle.current_song_filename.clone();
+                let art_data_length = write_handle.art_data.len();
+                if write_handle.can_get_album_art {
+                    let write_result = write_handle.stream.write(
+                        format!("readpicture \"{}\" {}\n", title, art_data_length).as_bytes(),
+                    );
+                    if write_result.is_ok() {
+                        write_handle.poll_state = PollState::ReadPicture;
+                    } else if let Err(e) = write_result {
+                        println!("ERROR: Failed to request album art: {}", e);
+                    }
+                } else if write_handle.can_get_album_art_in_dir {
+                    let write_result = write_handle
+                        .stream
+                        .write(format!("albumart \"{}\" {}\n", title, art_data_length).as_bytes());
+                    if write_result.is_ok() {
+                        write_handle.poll_state = PollState::ReadPictureInDir;
+                    } else if let Err(e) = write_result {
+                        println!("ERROR: Failed to request album art in dir: {}", e);
+                    }
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    fn is_reading_picture(h: Arc<RwLock<MPDHandler>>) -> bool {
+        if let Ok(read_handle) = h.read() {
+            return read_handle.poll_state == PollState::ReadPicture
+                || read_handle.poll_state == PollState::ReadPictureInDir;
+        } else {
+            return false;
+        }
+    }
+}