diff --git a/Cargo.lock b/Cargo.lock index d37e51e..206444c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24606928a235e73cdef55a0c909719cadd72fce573e5713d58cb2952d8f5794c" dependencies = [ "ab_glyph_rasterizer", - "owned_ttf_parser 0.15.0", + "owned_ttf_parser 0.15.1", ] [[package]] @@ -30,6 +30,15 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "alsa" version = "0.6.0" @@ -114,27 +123,32 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "base-x" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc19a4937b4fbd3fe3379793130e42060d10627a360f2127802b10b87e7baf74" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" [[package]] name = "bindgen" -version = "0.59.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +checksum = "c72a978d268b1d70b0e963217e60fdabd9523a941457a6c42a7315d15c7e89e5" dependencies = [ "bitflags", "cexpr", + "cfg-if 0.1.10", "clang-sys", + "clap", + "env_logger", "lazy_static", "lazycell", + "log", "peeking_take_while", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", + "which", ] [[package]] @@ -157,21 +171,21 @@ checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] name = "bumpalo" -version = "3.9.1" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" [[package]] name = "bytecount" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72feb31ffc86498dacdbd0fcebb56138e7177a8cc5cea4516031d15ae85a742e" +checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "bytemuck" -version = "1.9.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc" +checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" [[package]] name = "byteorder" @@ -181,9 +195,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "f0b3de4a0c5e67e16066a0715723abd91edc2f9001d09c46e1dca929351e130e" [[package]] name = "bzip2" @@ -218,9 +232,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fd178c5af4d59e83498ef15cf3f154e1a6f9d091270cb86283c65ef44e9ef0" +checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" dependencies = [ "serde", ] @@ -242,7 +256,7 @@ checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" dependencies = [ "camino", "cargo-platform", - "semver 1.0.9", + "semver 1.0.12", "serde", "serde_json", ] @@ -264,11 +278,11 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cexpr" -version = "0.6.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ - "nom", + "nom 5.1.2", ] [[package]] @@ -294,13 +308,13 @@ dependencies = [ [[package]] name = "clang-sys" -version = "1.3.1" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" +checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" dependencies = [ "glob", "libc", - "libloading 0.7.3", + "libloading 0.5.2", ] [[package]] @@ -324,6 +338,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" +[[package]] +name = "cmake" +version = "0.1.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" +dependencies = [ + "cc", +] + [[package]] name = "cocoa" version = "0.24.0" @@ -481,9 +504,9 @@ dependencies = [ [[package]] name = "coreaudio-sys" -version = "0.2.10" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dff444d80630d7073077d38d40b4501fd518bd2b922c2a55edcc8b0f7be57e6" +checksum = "17f73df0f29f4c3c374854f076c47dc018f19acaa63538880dba0937ad4fa8d7" dependencies = [ "bindgen", ] @@ -524,9 +547,9 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" dependencies = [ "cfg-if 1.0.0", "crossbeam-channel", @@ -538,9 +561,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -548,9 +571,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if 1.0.0", "crossbeam-epoch", @@ -559,23 +582,23 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.8" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" dependencies = [ "autocfg", "cfg-if 1.0.0", "crossbeam-utils", - "lazy_static", "memoffset", + "once_cell", "scopeguard", ] [[package]] name = "crossbeam-queue" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -583,12 +606,12 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ "cfg-if 1.0.0", - "lazy_static", + "once_cell", ] [[package]] @@ -762,9 +785,22 @@ dependencies = [ [[package]] name = "either" -version = "1.6.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] [[package]] name = "error-chain" @@ -802,23 +838,21 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" +checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" dependencies = [ "instant", ] [[package]] name = "flate2" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" dependencies = [ - "cfg-if 1.0.0", "crc32fast", - "libc", - "miniz_oxide 0.5.1", + "miniz_oxide 0.5.3", ] [[package]] @@ -832,9 +866,9 @@ dependencies = [ [[package]] name = "flume" -version = "0.10.12" +version = "0.10.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843c03199d0c0ca54bc1ea90ac0d507274c28abcc4f691ae8b4eaa375087c76a" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" dependencies = [ "futures-core", "futures-sink", @@ -864,6 +898,27 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + [[package]] name = "futures-core" version = "0.3.21" @@ -878,9 +933,9 @@ checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" [[package]] name = "getrandom" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad" +checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" dependencies = [ "cfg-if 1.0.0", "js-sys", @@ -965,9 +1020,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.11.3" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a7187e78088aead22ceedeee99779455b23fc231fe13ec443f99bb71694e5b" +checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" dependencies = [ "color_quant", "weezl", @@ -1172,6 +1227,15 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549" +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1197,9 +1261,9 @@ dependencies = [ [[package]] name = "image" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28edd9d7bc256be2502e325ac0628bde30b7001b9b52e0abe31a1a9dc2701212" +checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964" dependencies = [ "bytemuck", "byteorder", @@ -1207,12 +1271,11 @@ dependencies = [ "exr", "gif", "jpeg-decoder 0.2.6", - "num-iter", - "num-rational 0.4.0", + "num-rational 0.4.1", "num-traits", "png 0.17.5", "scoped_threadpool", - "tiff 0.7.2", + "tiff 0.7.3", ] [[package]] @@ -1295,9 +1358,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.57" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" dependencies = [ "wasm-bindgen", ] @@ -1339,9 +1402,19 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.125" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" + +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi", +] [[package]] name = "libloading" @@ -1375,9 +1448,9 @@ dependencies = [ [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" @@ -1420,9 +1493,9 @@ dependencies = [ [[package]] name = "lyon_geom" -version = "0.17.6" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce99ce77c22bfd8f39a95b9c749dffbfc3e2491ea30c874764c801a8b1485489" +checksum = "71d89ccbdafd83d259403e22061be27bccc3254bba65cdc5303250c4227c8c8e" dependencies = [ "arrayvec", "euclid", @@ -1546,9 +1619,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082" +checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" dependencies = [ "adler", ] @@ -1595,10 +1668,12 @@ dependencies = [ [[package]] name = "mpd_info_screen" -version = "0.2.20" +version = "0.3.0" dependencies = [ + "bindgen", + "freetype", "ggez", - "image 0.24.2", + "image 0.24.3", "structopt", ] @@ -1749,6 +1824,16 @@ dependencies = [ "memoffset", ] +[[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.1" @@ -1813,9 +1898,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", "num-integer", @@ -1916,9 +2001,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.10.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9" +checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" [[package]] name = "ordered-float" @@ -1949,11 +2034,11 @@ dependencies = [ [[package]] name = "owned_ttf_parser" -version = "0.15.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1e509cfe7a12db2a90bfa057dfcdbc55a347f5da677c506b53dd099cfec9d" +checksum = "07ef1a404ae479dd6906f4fa2c88b3c94028f1284beb42a47c183a7c27ee9a3e" dependencies = [ - "ttf-parser 0.15.0", + "ttf-parser 0.15.2", ] [[package]] @@ -1995,18 +2080,18 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" +checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" +checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" dependencies = [ "proc-macro2", "quote", @@ -2040,7 +2125,7 @@ dependencies = [ "bitflags", "crc32fast", "deflate 1.0.0", - "miniz_oxide 0.5.1", + "miniz_oxide 0.5.3", ] [[package]] @@ -2094,18 +2179,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.38" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa" +checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "pulldown-cmark" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ "bitflags", "memchr", @@ -2113,10 +2198,16 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.18" +name = "quick-error" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] @@ -2196,9 +2287,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] @@ -2216,18 +2307,20 @@ dependencies = [ [[package]] name = "regex" -version = "1.5.5" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "remove_dir_all" @@ -2331,9 +2424,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cb243bdfdb5936c8dc3c45762a19d12ab4550cdc753bc247637d4ec35a040fd" +checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" dependencies = [ "serde", ] @@ -2346,18 +2439,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.137" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.137" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", @@ -2366,9 +2459,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" +checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" dependencies = [ "itoa", "ryu", @@ -2402,9 +2495,9 @@ dependencies = [ [[package]] name = "shlex" -version = "1.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] name = "sid" @@ -2443,9 +2536,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" +checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" [[package]] name = "smart-default" @@ -2479,9 +2572,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d" +checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09" dependencies = [ "lock_api", ] @@ -2593,13 +2686,13 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.94" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a" +checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] @@ -2616,6 +2709,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "termcolor" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +dependencies = [ + "winapi-util", +] + [[package]] name = "textwrap" version = "0.11.0" @@ -2667,9 +2769,9 @@ dependencies = [ [[package]] name = "tiff" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cfada0986f446a770eca461e8c6566cb879682f7d687c8348aa0c857bd52286" +checksum = "7259662e32d1e219321eb309d5f9d898b779769d81b76e762c07c8e5d38fcb65" dependencies = [ "flate2", "jpeg-decoder 0.2.6", @@ -2708,9 +2810,9 @@ checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" [[package]] name = "ttf-parser" -version = "0.15.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c74c96594835e10fa545e2a51e8709f30b173a092bfd6036ef2cec53376244f3" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" [[package]] name = "twox-hash" @@ -2732,6 +2834,12 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-ident" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" + [[package]] name = "unicode-segmentation" version = "1.9.0" @@ -2744,12 +2852,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "unicode-xid" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" - [[package]] name = "uuid" version = "0.8.2" @@ -2781,15 +2883,15 @@ dependencies = [ [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" dependencies = [ "cfg-if 1.0.0", "wasm-bindgen-macro", @@ -2797,13 +2899,13 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -2812,9 +2914,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2822,9 +2924,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" dependencies = [ "proc-macro2", "quote", @@ -2835,9 +2937,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.80" +version = "0.2.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" [[package]] name = "wayland-client" @@ -2924,9 +3026,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.57" +version = "0.3.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" +checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" dependencies = [ "js-sys", "wasm-bindgen", @@ -2934,9 +3036,18 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] [[package]] name = "winapi" @@ -3018,7 +3129,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" dependencies = [ - "nom", + "nom 7.1.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e7ab232..db8b725 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mpd_info_screen" -version = "0.2.20" +version = "0.3.0" edition = "2018" description = "Displays info on currently playing music from an MPD daemon" license = "MIT" @@ -12,3 +12,10 @@ repository = "https://github.com/Stephen-Seo/mpd_info_screen" structopt = "0.3" image = "0.24" ggez = "0.7" +freetype = { version = "0.7", optional = true } + +[build-dependencies] +bindgen = { version = "0.53", optional = true } + +[features] +unicode_support = ["dep:freetype", "dep:bindgen"] diff --git a/README.md b/README.md index aa9d724..72e0c73 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,17 @@ A Rust program that displays info about the currently running MPD server. The window shows albumart (may be embedded in the audio file, or is a "cover.jpg" in the same directory as the song file), a "time-remaining" counter, and the filename currently being played +By default, unicode characters will not display properly. Build the project with +the `unicode_support` feature enabled to enable fetching fonts from the local +filesystem to display unicode characters properly (if the system is missing a +font, then it will still be displayed incorrectly). + + cargo build --release --features unicode_support + # Usage - mpd_info_screen 0.2.20 + + mpd_info_screen 0.3.0 USAGE: mpd_info_screen [FLAGS] [OPTIONS] [port] @@ -42,7 +50,7 @@ Also note that pressing the H key while displaying text will hide the text. # Issues / TODO -- [ ] UTF-8 Non-ascii font support +- [x] UTF-8 Non-ascii font support (Use the `unicode_support` feature to enable; only tested in linux) - [x] Support for album art not embedded but in the same directory ## MPD Version @@ -64,3 +72,12 @@ MIT license. Uses dependency [structopt](https://crates.io/crates/structopt) which is licensed under Apache-2.0 or MIT licenses. + +## Unicode Support Dependencies + +Uses dependency +[fontconfig](https://www.freedesktop.org/wiki/Software/fontconfig/) which is +[licensed with this license](https://www.freedesktop.org/software/fontconfig/fontconfig-devel/ln12.html). + +Uses dependency [freetype](https://freetype.org) which is +[licensed with this license](https://freetype.org/license.html). diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..f393618 --- /dev/null +++ b/build.rs @@ -0,0 +1,25 @@ +#[cfg(feature = "unicode_support")] +use bindgen; +use std::env; +use std::path::PathBuf; + +#[cfg(not(feature = "unicode_support"))] +fn main() {} + +#[cfg(feature = "unicode_support")] +fn main() { + println!("cargo:rustc-link-search=/usr/lib"); + println!("cargo:rustc-link-lib=fontconfig"); + println!("cargo:rerun-if-changed=src/bindgen_wrapper.h"); + + let bindings = bindgen::Builder::default() + .header("src/bindgen_wrapper.h") + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .generate() + .expect("Unable to generate bindings"); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("unicode_support_bindings.rs")) + .expect("Couldn't write bindings"); +} diff --git a/src/bindgen_wrapper.h b/src/bindgen_wrapper.h new file mode 100644 index 0000000..79bf5b9 --- /dev/null +++ b/src/bindgen_wrapper.h @@ -0,0 +1 @@ +#include diff --git a/src/display.rs b/src/display.rs index 94f1486..cde0067 100644 --- a/src/display.rs +++ b/src/display.rs @@ -9,6 +9,7 @@ use ggez::graphics::{ use ggez::{timer, Context, GameError, GameResult}; use image::io::Reader as ImageReader; use std::io::Cursor; +use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::{atomic::Ordering, Arc, RwLockReadGuard}; use std::thread; @@ -49,6 +50,125 @@ fn seconds_to_time(seconds: f64) -> String { result } +#[cfg(not(feature = "unicode_support"))] +fn string_to_text( + string: String, + loaded_fonts: &mut Vec<(PathBuf, Font)>, + ctx: &mut Context, +) -> Text { + Text::new(TextFragment::from(string)) +} + +#[cfg(feature = "unicode_support")] +fn string_to_text( + string: String, + loaded_fonts: &mut Vec<(PathBuf, Font)>, + ctx: &mut Context, +) -> Text { + use super::unicode_support; + + let mut text = Text::default(); + let mut current_fragment = TextFragment::default(); + + if string.is_ascii() { + current_fragment.text = string; + text.add(current_fragment); + return text; + } + + let find_font = + |c: char, loaded_fonts: &mut Vec<(PathBuf, Font)>, ctx: &mut Context| -> Option { + for (idx, (path, _)) in loaded_fonts.iter().enumerate() { + let result = unicode_support::font_has_char(c, path); + if result.is_ok() && result.unwrap() { + return Some(idx); + } + } + + let find_result = unicode_support::get_matching_font_from_char(c); + if let Ok(path) = find_result { + let new_font = Font::new(ctx, &path); + if let Ok(font) = new_font { + loaded_fonts.push((path, font)); + return Some(loaded_fonts.len() - 1); + } else { + println!("Failed to load {:?}: {:?}", &path, new_font); + } + } else { + println!("Failed to find font for {}", c); + } + + None + }; + + let mut prev_is_ascii = true; + for c in string.chars() { + if c.is_ascii() { + if prev_is_ascii { + current_fragment.text.push(c); + } else { + if !current_fragment.text.is_empty() { + text.add(current_fragment); + current_fragment = Default::default(); + } + current_fragment.text.push(c); + } + prev_is_ascii = true; + } else { + let idx_opt = find_font(c, loaded_fonts, ctx); + if prev_is_ascii { + if let Some(idx) = idx_opt { + if !current_fragment.text.is_empty() { + text.add(current_fragment); + current_fragment = Default::default(); + } + let (_, font) = loaded_fonts[idx]; + current_fragment.font = Some(font); + } + current_fragment.text.push(c); + } else { + if let Some(idx) = idx_opt { + let font = loaded_fonts[idx].1; + if let Some(current_font) = current_fragment.font { + if current_font == font { + current_fragment.text.push(c); + } else { + if !current_fragment.text.is_empty() { + text.add(current_fragment); + current_fragment = Default::default(); + } + current_fragment.text.push(c); + current_fragment.font = Some(font); + } + } else if current_fragment.text.is_empty() { + current_fragment.text.push(c); + current_fragment.font = Some(font); + } else { + text.add(current_fragment); + current_fragment = Default::default(); + + current_fragment.text.push(c); + current_fragment.font = Some(font); + } + } else { + if !current_fragment.text.is_empty() && current_fragment.font.is_some() { + text.add(current_fragment); + current_fragment = Default::default(); + } + current_fragment.text.push(c); + } + } + prev_is_ascii = false; + } + } + + if !current_fragment.text.is_empty() { + text.add(current_fragment); + } + + text +} + pub struct MPDDisplay { opts: Opt, mpd_handler: Result, @@ -63,10 +183,13 @@ pub struct MPDDisplay { album_art: Option, album_art_draw_transform: Option, filename_text: Text, + filename_string_cache: String, filename_transform: Transform, artist_text: Text, + artist_string_cache: String, artist_transform: Transform, title_text: Text, + title_string_cache: String, title_transform: Transform, timer_text: Text, timer_transform: Transform, @@ -78,6 +201,7 @@ pub struct MPDDisplay { hide_text: bool, tried_album_art_in_dir: bool, mpd_play_state: MPDPlayState, + loaded_fonts: Vec<(PathBuf, Font)>, } impl MPDDisplay { @@ -111,6 +235,10 @@ impl MPDDisplay { hide_text: false, tried_album_art_in_dir: false, mpd_play_state: MPDPlayState::Playing, + loaded_fonts: Vec::new(), + filename_string_cache: String::new(), + artist_string_cache: String::new(), + title_string_cache: String::new(), } } @@ -314,13 +442,12 @@ impl MPDDisplay { break; } - text.set_font( - Font::default(), - PxScale { + for fragment in text.fragments_mut() { + fragment.scale = Some(PxScale { x: current_x, y: current_y, - }, - ); + }); + } width = text.width(ctx); height = text.height(ctx); @@ -346,13 +473,12 @@ impl MPDDisplay { } else { let diff_scale_y = current_y / height * timer_height; let current_x = current_x * diff_scale_y / current_y; - text.set_font( - Font::default(), - PxScale { + for fragment in text.fragments_mut() { + fragment.scale = Some(PxScale { x: current_x, y: diff_scale_y, - }, - ); + }); + } *timer_x = current_x; *timer_y = diff_scale_y; // width = text.width(ctx); // not really used after this @@ -600,7 +726,14 @@ impl EventHandler for MPDDisplay { } else { self.mpd_play_state = MPDPlayState::Playing; if !shared.title.is_empty() { - self.title_text = Text::new(shared.title.clone()); + if shared.title != self.title_string_cache { + self.title_string_cache = shared.title.clone(); + self.title_text = string_to_text( + shared.title.clone(), + &mut self.loaded_fonts, + ctx, + ); + } } else { self.dirty_flag .as_ref() @@ -608,7 +741,14 @@ impl EventHandler for MPDDisplay { .store(true, Ordering::Relaxed); } if !shared.artist.is_empty() { - self.artist_text = Text::new(shared.artist.clone()); + if shared.artist != self.artist_string_cache { + self.artist_string_cache = shared.artist.clone(); + self.artist_text = string_to_text( + shared.artist.clone(), + &mut self.loaded_fonts, + ctx, + ); + } } else { self.dirty_flag .as_ref() @@ -616,11 +756,18 @@ impl EventHandler for MPDDisplay { .store(true, Ordering::Relaxed); } if !shared.filename.is_empty() { - if self.filename_text.contents() != shared.filename { - self.album_art = None; - self.tried_album_art_in_dir = false; + if shared.filename != self.filename_string_cache { + self.filename_string_cache = shared.filename.clone(); + if self.filename_text.contents() != shared.filename { + self.album_art = None; + self.tried_album_art_in_dir = false; + } + self.filename_text = string_to_text( + shared.filename.clone(), + &mut self.loaded_fonts, + ctx, + ); } - self.filename_text = Text::new(shared.filename.clone()); } else { self.dirty_flag .as_ref() diff --git a/src/main.rs b/src/main.rs index be43427..aa4ce5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,17 @@ mod debug_log; mod display; mod mpd_handler; +#[cfg(feature = "unicode_support")] +mod unicode_support; use ggez::conf::{WindowMode, WindowSetup}; use ggez::event::winit_event::{ElementState, KeyboardInput, ModifiersState}; use ggez::event::{self, ControlFlow, EventHandler}; +use ggez::filesystem::mount; use ggez::graphics::{self, Rect}; use ggez::{ContextBuilder, GameError}; use std::net::Ipv4Addr; +use std::path::PathBuf; use std::thread; use std::time::{Duration, Instant}; use structopt::StructOpt; @@ -61,6 +65,9 @@ fn main() -> Result<(), String> { .build() .expect("Failed to create ggez context"); + // mount "/" read-only so that fonts can be loaded via absolute paths + mount(&mut ctx, &PathBuf::from("/"), true); + let mut display = display::MPDDisplay::new(&mut ctx, opt.clone()); let mut modifiers_state: ModifiersState = ModifiersState::default(); diff --git a/src/unicode_support.rs b/src/unicode_support.rs new file mode 100644 index 0000000..eede2ef --- /dev/null +++ b/src/unicode_support.rs @@ -0,0 +1,5 @@ +mod fontconfig; +mod freetype; + +pub use self::freetype::font_has_char; +pub use fontconfig::{get_matching_font_from_char, get_matching_font_from_str}; diff --git a/src/unicode_support/fontconfig.rs b/src/unicode_support/fontconfig.rs new file mode 100644 index 0000000..1b6f19d --- /dev/null +++ b/src/unicode_support/fontconfig.rs @@ -0,0 +1,348 @@ +use std::{path::PathBuf, str::FromStr}; + +mod ffi { + use std::{ffi::CStr, os::raw::c_int}; + + mod bindgen { + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + #![allow(non_snake_case)] + #![allow(dead_code)] + #![allow(deref_nullptr)] + + include!(concat!(env!("OUT_DIR"), "/unicode_support_bindings.rs")); + } + + pub struct FcConfigWr { + config: *mut bindgen::FcConfig, + } + + impl Drop for FcConfigWr { + fn drop(&mut self) { + if !self.config.is_null() { + unsafe { + bindgen::FcConfigDestroy(self.config); + } + } + } + } + + impl FcConfigWr { + pub fn new() -> Result { + let config = unsafe { bindgen::FcInitLoadConfigAndFonts() }; + if config.is_null() { + Err(String::from("Failed to create FcConfig")) + } else { + Ok(Self { config }) + } + } + + pub fn get(&mut self) -> *mut bindgen::FcConfig { + self.config + } + + pub fn apply_pattern_to_config(&mut self, pattern: &mut FcPatternWr) -> bool { + unsafe { + bindgen::FcConfigSubstitute( + self.config, + pattern.get(), + bindgen::_FcMatchKind_FcMatchPattern, + ) == bindgen::FcTrue as bindgen::FcBool + } + } + + pub fn font_match(&mut self, pattern: &mut FcPatternWr) -> Result { + unsafe { + let mut result: bindgen::FcResult = 0 as bindgen::FcResult; + let result_pattern = bindgen::FcFontMatch( + self.config, + pattern.get(), + &mut result as *mut bindgen::FcResult, + ); + if result != bindgen::_FcResult_FcResultMatch { + if !result_pattern.is_null() { + bindgen::FcPatternDestroy(result_pattern); + return Err(String::from("Failed to FcFontMatch (FcResult is not FcResultMatch; result_pattern is not null)")); + } else { + return Err(format!( + "Failed to FcFontMatch (FcResult is not FcResultMatch; {:?})", + result + )); + } + } else if result_pattern.is_null() { + return Err(String::from( + "Failed to FcFontMatch (result_pattern is null)", + )); + } + + Ok(FcPatternWr { + pattern: result_pattern, + }) + } + } + } + + pub struct FcCharSetWr { + charset: *mut bindgen::FcCharSet, + } + + impl Drop for FcCharSetWr { + fn drop(&mut self) { + if !self.charset.is_null() { + unsafe { + bindgen::FcCharSetDestroy(self.charset); + } + } + } + } + + impl FcCharSetWr { + pub fn new_with_str(s: &str) -> Result { + let charset; + unsafe { + let charset_ptr = bindgen::FcCharSetCreate(); + if charset_ptr.is_null() { + return Err(String::from("Failed to create FcCharSet with str")); + } + charset = FcCharSetWr { + charset: charset_ptr, + }; + for c in s.chars() { + if bindgen::FcCharSetAddChar(charset.charset, c as u32) + == bindgen::FcFalse as bindgen::FcBool + { + return Err(String::from("Failed to add chars from str into FcCharSet")); + } + } + } + + Ok(charset) + } + + pub fn new_with_char(c: char) -> Result { + let charset; + unsafe { + let charset_ptr = bindgen::FcCharSetCreate(); + if charset_ptr.is_null() { + return Err(String::from("Failed to create FcCharSet with char")); + } + charset = FcCharSetWr { + charset: charset_ptr, + }; + if bindgen::FcCharSetAddChar(charset.charset, c as u32) + == bindgen::FcFalse as bindgen::FcBool + { + return Err(String::from("Failed to add char to FcCharSet")); + } + } + + Ok(charset) + } + + pub fn get(&mut self) -> *mut bindgen::FcCharSet { + self.charset + } + } + + pub struct FcPatternWr { + pattern: *mut bindgen::FcPattern, + } + + impl Drop for FcPatternWr { + fn drop(&mut self) { + if !self.pattern.is_null() { + unsafe { + bindgen::FcPatternDestroy(self.pattern); + } + } + } + } + + impl FcPatternWr { + pub fn new_with_charset(c: &mut FcCharSetWr) -> Result { + let pattern; + unsafe { + let pattern_ptr = bindgen::FcPatternCreate(); + if pattern_ptr.is_null() { + return Err(String::from("Failed to FcPatternCreate")); + } + pattern = Self { + pattern: pattern_ptr, + }; + bindgen::FcDefaultSubstitute(pattern.pattern); + + let value = bindgen::FcValue { + type_: bindgen::_FcType_FcTypeCharSet, + u: bindgen::_FcValue__bindgen_ty_1 { c: c.get() }, + }; + + if bindgen::FcPatternAdd( + pattern.pattern, + bindgen::FC_CHARSET as *const _ as *const i8, + value, + bindgen::FcTrue as bindgen::FcBool, + ) == bindgen::FcFalse as bindgen::FcBool + { + return Err(String::from("Failed to add FcCharSet to new Pattern")); + } + } + + Ok(pattern) + } + + pub fn get(&mut self) -> *mut bindgen::FcPattern { + self.pattern + } + + pub fn get_count(&self) -> c_int { + unsafe { bindgen::FcPatternObjectCount(self.pattern) } + } + + pub fn filter_to_filenames(&self) -> Result { + let pattern; + unsafe { + let mut file_object_set_filter = FcObjectSetWr::new_file_object_set()?; + let pattern_ptr = + bindgen::FcPatternFilter(self.pattern, file_object_set_filter.get()); + if pattern_ptr.is_null() { + return Err(String::from("Failed to FcPatternFilter")); + } + pattern = Self { + pattern: pattern_ptr, + }; + } + + Ok(pattern) + } + + pub fn get_filename_contents(&self) -> Result, String> { + let mut vec: Vec = Vec::new(); + let count = self.get_count(); + unsafe { + let mut value = bindgen::FcValue { + type_: 0, + u: bindgen::_FcValue__bindgen_ty_1 { i: 0 }, + }; + + for i in 0..count { + if bindgen::FcPatternGet( + self.pattern, + bindgen::FC_FILE as *const _ as *const i8, + i, + &mut value as *mut bindgen::FcValue, + ) == bindgen::_FcResult_FcResultMatch + { + if value.type_ == bindgen::_FcType_FcTypeString { + let cs = CStr::from_ptr(value.u.s as *const i8); + vec.push( + cs.to_str() + .map_err(|_| String::from("Failed to convert CStr to String"))? + .to_owned(), + ); + } + } + } + } + + Ok(vec) + } + } + + struct FcObjectSetWr { + object_set: *mut bindgen::FcObjectSet, + } + + impl Drop for FcObjectSetWr { + fn drop(&mut self) { + unsafe { + if !self.object_set.is_null() { + bindgen::FcObjectSetDestroy(self.object_set); + } + } + } + } + + impl FcObjectSetWr { + pub fn new_file_object_set() -> Result { + let object_set; + unsafe { + let object_set_ptr = bindgen::FcObjectSetCreate(); + if object_set_ptr.is_null() { + return Err(String::from("Failed to FcObjectSetCreate")); + } + + object_set = Self { + object_set: object_set_ptr, + }; + + if bindgen::FcObjectSetAdd( + object_set.object_set, + bindgen::FC_FILE as *const _ as *const i8, + ) == bindgen::FcFalse as bindgen::FcBool + { + return Err(String::from( + "Failed to add \"FC_FILE\" with FcObjectSetAdd", + )); + } + } + + Ok(object_set) + } + + pub fn get(&mut self) -> *mut bindgen::FcObjectSet { + self.object_set + } + } +} + +pub fn get_matching_font_from_str(s: &str) -> Result { + let mut config = ffi::FcConfigWr::new()?; + let mut charset = ffi::FcCharSetWr::new_with_str(s)?; + let mut search_pattern = ffi::FcPatternWr::new_with_charset(&mut charset)?; + if !config.apply_pattern_to_config(&mut search_pattern) { + return Err(String::from("Failed to apply_pattern_to_config")); + } + let result_pattern = config.font_match(&mut search_pattern)?; + let filtered_pattern = result_pattern.filter_to_filenames()?; + let result_vec = filtered_pattern.get_filename_contents()?; + + if result_vec.is_empty() { + Err(String::from( + "Empty result_vec for get_matching_font_from_str", + )) + } else { + PathBuf::from_str(&result_vec[0]).map_err(|e| e.to_string()) + } +} + +pub fn get_matching_font_from_char(c: char) -> Result { + let mut config = ffi::FcConfigWr::new()?; + let mut charset = ffi::FcCharSetWr::new_with_char(c)?; + let mut search_pattern = ffi::FcPatternWr::new_with_charset(&mut charset)?; + if !config.apply_pattern_to_config(&mut search_pattern) { + return Err(String::from("Failed to apply_pattern_to_config")); + } + let result_pattern = config.font_match(&mut search_pattern)?; + let filtered_pattern = result_pattern.filter_to_filenames()?; + let result_vec = filtered_pattern.get_filename_contents()?; + + if result_vec.is_empty() { + Err(String::from( + "Empty result_vec for get_matching_font_from_char", + )) + } else { + PathBuf::from_str(&result_vec[0]).map_err(|e| e.to_string()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ascii_fetching() { + let fetched_path = + get_matching_font_from_char('a').expect("Should be able to find match for 'a'"); + println!("{:?}", fetched_path); + } +} diff --git a/src/unicode_support/freetype.rs b/src/unicode_support/freetype.rs new file mode 100644 index 0000000..6a2ae46 --- /dev/null +++ b/src/unicode_support/freetype.rs @@ -0,0 +1,157 @@ +use std::path::Path; + +mod ffi { + use freetype::freetype::{ + FT_Done_Face, FT_Done_Library, FT_Face, FT_FaceRec_, FT_Get_Char_Index, FT_Init_FreeType, + FT_Library, FT_ModuleRec_, FT_Open_Args, FT_Open_Face, FT_Parameter_, FT_StreamRec_, + FT_OPEN_PATHNAME, + }; + use std::ffi::CString; + use std::path::Path; + + pub struct FTLibrary { + library: FT_Library, + } + + impl Drop for FTLibrary { + fn drop(&mut self) { + if !self.library.is_null() { + unsafe { + FT_Done_Library(self.library); + } + } + } + } + + impl FTLibrary { + pub fn new() -> Option { + unsafe { + let mut library_ptr: FT_Library = 0 as FT_Library; + if FT_Init_FreeType(&mut library_ptr) != 0 { + Some(FTLibrary { + library: library_ptr, + }) + } else { + None + } + } + } + + pub fn get(&self) -> FT_Library { + self.library + } + } + + pub struct FTOpenArgs { + args: FT_Open_Args, + pathname: Option, + } + + impl FTOpenArgs { + pub fn new_with_path(path: &Path) -> Self { + unsafe { + let cstring: CString = CString::from_vec_unchecked( + path.as_os_str().to_str().unwrap().as_bytes().to_vec(), + ); + let args = FT_Open_Args { + flags: FT_OPEN_PATHNAME, + memory_base: 0 as *const u8, + memory_size: 0, + pathname: cstring.as_ptr() as *mut i8, + stream: 0 as *mut FT_StreamRec_, + driver: 0 as *mut FT_ModuleRec_, + num_params: 0, + params: 0 as *mut FT_Parameter_, + }; + + FTOpenArgs { + args, + pathname: Some(cstring), + } + } + } + + pub fn get_args(&self) -> FT_Open_Args { + self.args + } + + pub fn get_ptr(&mut self) -> *mut FT_Open_Args { + &mut self.args as *mut FT_Open_Args + } + } + + pub struct FTFaces { + faces: Vec, + } + + impl Drop for FTFaces { + fn drop(&mut self) { + for face in &self.faces { + unsafe { + FT_Done_Face(*face); + } + } + } + } + + impl FTFaces { + pub fn new(library: &FTLibrary, args: &mut FTOpenArgs) -> Result { + let mut faces = FTFaces { faces: Vec::new() }; + unsafe { + let count; + let mut face: FT_Face = 0 as FT_Face; + // first get number of faces + let mut result = FT_Open_Face( + library.get(), + args.get_ptr(), + -1, + &mut face as *mut *mut FT_FaceRec_, + ); + if result != 0 { + FT_Done_Face(face); + return Err(()); + } + count = (*face).num_faces; + + for i in 0..count { + result = FT_Open_Face( + library.get(), + args.get_ptr(), + i, + &mut face as *mut *mut FT_FaceRec_, + ); + if result != 0 { + FT_Done_Face(face); + return Err(()); + } + faces.faces.push(face); + } + } + + Ok(faces) + } + + pub fn has_char(&self, c: char) -> bool { + let char_value: u64 = c as u64; + + for face in &self.faces { + unsafe { + let result = FT_Get_Char_Index(*face, char_value); + if result != 0 { + return true; + } + } + } + + false + } + } +} + +pub fn font_has_char(c: char, font_path: &Path) -> Result { + let library = ffi::FTLibrary::new().ok_or(())?; + let mut args = ffi::FTOpenArgs::new_with_path(font_path); + let faces = ffi::FTFaces::new(&library, &mut args)?; + + Ok(faces.has_char(c)) +}