tiny-dfr-0.3.5/.cargo_vcs_info.json0000644000000001360000000000100125750ustar { "git": { "sha1": "c63dcb34c37b28f2edc814d94f46a4a6f5461c16" }, "path_in_vcs": "" }tiny-dfr-0.3.5/.gitignore000064400000000000000000000000201046102023000133450ustar 00000000000000/target /result tiny-dfr-0.3.5/Cargo.lock0000644000001014300000000000100105470ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "android-tzdata" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bumpalo" version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "cairo-rs" version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8a0ea147c94108c9613235388f540e4d14c327f7081c9e471fc8ee8a2533e69" dependencies = [ "bitflags 2.6.0", "cairo-sys-rs", "freetype-rs", "glib", "libc", ] [[package]] name = "cairo-sys-rs" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428290f914b9b86089f60f5d8a9f6e440508e1bcff23b25afd51502b0a2da88f" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "cc" version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" dependencies = [ "libc", ] [[package]] name = "cfg-expr" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c" dependencies = [ "smallvec", "target-lexicon", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "pure-rust-locales", "wasm-bindgen", "windows-link", ] [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "dirs" version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" dependencies = [ "dirs-sys", ] [[package]] name = "dirs-sys" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", "redox_users", "windows-sys 0.48.0", ] [[package]] name = "drm" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f8a69e60d75ae7dab4ef26a59ca99f2a89d4c142089b537775ae0c198bdcde" dependencies = [ "bitflags 2.6.0", "bytemuck", "drm-ffi", "drm-fourcc", "rustix", ] [[package]] name = "drm-ffi" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41334f8405792483e32ad05fbb9c5680ff4e84491883d2947a4757dc54cb2ac6" dependencies = [ "drm-sys", "rustix", ] [[package]] name = "drm-fourcc" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" [[package]] name = "drm-sys" version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d09ff881f92f118b11105ba5e34ff8f4adf27b30dae8f12e28c193af1c83176" dependencies = [ "libc", "linux-raw-sys 0.6.4", ] [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "freedesktop-icons" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95f87364ea709292a3b3f74014ce3ee78412c89807eea75a358c8e029b000994" dependencies = [ "dirs", "ini_core", "once_cell", "thiserror", "tracing", "xdg", ] [[package]] name = "freetype-rs" version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1d1f81b925f09d7040682dbc91eb1b6ad43232f4bc6ee080f518001c05b5415" dependencies = [ "bitflags 2.6.0", "freetype-sys", "libc", ] [[package]] name = "freetype-sys" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddb84abd9992afaa8eb9b8bef907a94553504d0a890924e1bf1f1ab1249455af" dependencies = [ "cc", "libc", "pkg-config", ] [[package]] name = "futures-channel" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", ] [[package]] name = "futures-core" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-executor" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-macro" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-task" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "gdk-pixbuf-sys" version = "0.20.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67f2587c9202bf997476bbba6aaed4f78a11538a2567df002a5f57f5331d0b5c" dependencies = [ "gio-sys", "glib-sys", "gobject-sys", "libc", "system-deps", ] [[package]] name = "getrandom" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "gio" version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8d999e8fb09583e96080867e364bc1e701284ad206c76a5af480d63833ad43c" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-util", "gio-sys", "glib", "libc", "pin-project-lite", "smallvec", ] [[package]] name = "gio-sys" version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e" dependencies = [ "glib-sys", "gobject-sys", "libc", "system-deps", "windows-sys 0.52.0", ] [[package]] name = "glib" version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcf1ec6d3650bf9fdbc6cee242d4fcebc6f6bfd9bea5b929b6a8b7344eb85ff" dependencies = [ "bitflags 2.6.0", "futures-channel", "futures-core", "futures-executor", "futures-task", "futures-util", "gio-sys", "glib-macros", "glib-sys", "gobject-sys", "libc", "memchr", "smallvec", ] [[package]] name = "glib-macros" version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6bf88f70cd5720a6197639dcabcb378dd528d0cb68cb1f45e3b358bcb841cd7" dependencies = [ "heck", "proc-macro-crate", "proc-macro2", "quote", "syn", ] [[package]] name = "glib-sys" version = "0.20.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8928869a44cfdd1fccb17d6746e4ff82c8f82e41ce705aa026a52ca8dc3aefb" dependencies = [ "libc", "system-deps", ] [[package]] name = "gobject-sys" version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c674d2ff8478cf0ec29d2be730ed779fef54415a2fb4b565c52def62696462" dependencies = [ "glib-sys", "libc", "system-deps", ] [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" [[package]] name = "iana-time-zone" version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "log", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "indexmap" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "ini_core" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a467a31a9f439b5262fa99c17084537bff57f24703d5a09a2b5c9657ec73a61" dependencies = [ "cfg-if", ] [[package]] name = "input" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e74cd82cedcd66db78742a8337bdc48f188c4d2c12742cbc5cd85113f0b059" dependencies = [ "bitflags 1.3.2", "input-sys", "io-lifetimes", "libc", "log", "udev", ] [[package]] name = "input-linux" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e8c4821c88b95582ca69234a1d233f87e44182c42e121f740efb0bec1142e0" dependencies = [ "input-linux-sys", "nix 0.29.0", "serde", ] [[package]] name = "input-linux-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b91b2248b0eaf0a576ef5e60b7f2107a749e705a876bc0b9fe952ac8d83a724" dependencies = [ "libc", "nix 0.29.0", ] [[package]] name = "input-sys" version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f6c2a17e8aba7217660e32863af87b0febad811d4b8620ef76b386603fddc2" dependencies = [ "libc", ] [[package]] name = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi", "libc", "windows-sys 0.48.0", ] [[package]] name = "js-sys" version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "libc" version = "0.2.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" [[package]] name = "libredox" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.6.0", "libc", ] [[package]] name = "librsvg-rebind" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15630f09af77f5e84889ce244a8402a1f9ac0b5546171ad77a02fe514a830d06" dependencies = [ "bitflags 2.6.0", "cairo-rs", "gio", "glib", "libc", "librsvg-rebind-sys", ] [[package]] name = "librsvg-rebind-sys" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "635a4abf7b375659c9bab1839779487eb2a964934ad4f67ceb4e0364b75c37e2" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", "gio-sys", "glib-sys", "gobject-sys", "libc", "system-deps", ] [[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 = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "linux-raw-sys" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0b5399f6804fbab912acbd8878ed3532d506b7c951b8f9f164ef90fef39e3f4" [[package]] name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" dependencies = [ "autocfg", ] [[package]] name = "nix" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", "memoffset", "pin-utils", ] [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "privdrop" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bc12de3935536ed9b69488faea4450a298dac44179b54f71806e63f55034bf9" dependencies = [ "libc", "nix 0.26.4", ] [[package]] name = "proc-macro-crate" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" dependencies = [ "toml_edit 0.22.22", ] [[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] name = "pure-rust-locales" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1190fd18ae6ce9e137184f207593877e70f39b015040156b1e05081cdfe3733a" [[package]] name = "quote" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[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.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] [[package]] name = "redox_users" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ "getrandom", "libredox", "thiserror", ] [[package]] name = "rustix" version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] [[package]] name = "rustversion" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" [[package]] name = "serde" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_spanned" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" dependencies = [ "serde", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" version = "2.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "system-deps" version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" dependencies = [ "cfg-expr", "heck", "pkg-config", "toml", "version-compare", ] [[package]] name = "target-lexicon" version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "thiserror" version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tiny-dfr" version = "0.3.5" dependencies = [ "anyhow", "cairo-rs", "chrono", "drm", "freedesktop-icons", "freetype-rs", "input", "input-linux", "input-linux-sys", "libc", "librsvg-rebind", "nix 0.29.0", "pkg-config", "privdrop", "pure-rust-locales", "rand", "serde", "toml", ] [[package]] name = "toml" version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit 0.21.0", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow 0.5.15", ] [[package]] name = "toml_edit" version = "0.22.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", "winnow 0.6.20", ] [[package]] name = "tracing" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tracing-core" version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", ] [[package]] name = "udev" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ebdbbd670373442a12fe9ef7aeb53aec4147a5a27a00bbc3ab639f08f48191a" dependencies = [ "libc", "libudev-sys", "pkg-config", ] [[package]] name = "unicode-ident" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "version-compare" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" dependencies = [ "unicode-ident", ] [[package]] name = "windows-core" version = "0.61.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "windows-interface" version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "windows-link" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" [[package]] name = "windows-result" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2ba9642430ee452d5a7aa78d72907ebe8cfda358e8cb7918a2050581322f97" dependencies = [ "windows-link", ] [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.0", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ "windows_aarch64_gnullvm 0.52.0", "windows_aarch64_msvc 0.52.0", "windows_i686_gnu 0.52.0", "windows_i686_msvc 0.52.0", "windows_x86_64_gnu 0.52.0", "windows_x86_64_gnullvm 0.52.0", "windows_x86_64_msvc 0.52.0", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" dependencies = [ "memchr", ] [[package]] name = "winnow" version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" dependencies = [ "memchr", ] [[package]] name = "xdg" version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546" tiny-dfr-0.3.5/Cargo.toml0000644000000035230000000000100105760ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "tiny-dfr" version = "0.3.5" build = "build.rs" autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "The most basic dynamic function row daemon possible" homepage = "https://github.com/AsahiLinux/tiny-dfr" readme = "README.md" license = "MIT AND Apache-2.0" repository = "https://github.com/AsahiLinux/tiny-dfr.git" [[bin]] name = "tiny-dfr" path = "src/main.rs" [dependencies.anyhow] version = "1" [dependencies.cairo-rs] version = "0.20" features = [ "freetype", "png", ] default-features = false [dependencies.chrono] version = "0.4" features = ["unstable-locales"] [dependencies.drm] version = "0.11.1" [dependencies.freedesktop-icons] version = "0.4.0" [dependencies.freetype-rs] version = "0.37" [dependencies.input] version = "0.8" [dependencies.input-linux] version = "0.7" features = ["serde"] [dependencies.input-linux-sys] version = "0.9" [dependencies.libc] version = "0.2" [dependencies.librsvg-rebind] version = "0.1" [dependencies.nix] version = "0.29" features = [ "event", "signal", "inotify", ] [dependencies.privdrop] version = "0.5.3" [dependencies.pure-rust-locales] version = "0.8" [dependencies.rand] version = "0.8" [dependencies.serde] version = "1" features = ["derive"] [dependencies.toml] version = "0.8" [build-dependencies.pkg-config] version = "0.3" tiny-dfr-0.3.5/Cargo.toml.orig000064400000000000000000000017201046102023000142540ustar 00000000000000[package] name = "tiny-dfr" version = "0.3.5" edition = "2021" license = "MIT AND Apache-2.0" description = "The most basic dynamic function row daemon possible" homepage = "https://github.com/AsahiLinux/tiny-dfr" repository = "https://github.com/AsahiLinux/tiny-dfr.git" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] cairo-rs = { version = "0.20", default-features = false, features = ["freetype", "png"] } librsvg-rebind = "0.1" drm = "0.11.1" anyhow = "1" input = "0.8" libc = "0.2" input-linux = { version = "0.7", features = ["serde"] } input-linux-sys = "0.9" nix = { version = "0.29", features = ["event", "signal", "inotify"] } privdrop = "0.5.3" serde = { version = "1", features = ["derive"] } toml = "0.8" rand = "0.8" freetype-rs = "0.37" freedesktop-icons = "0.4.0" chrono = { version = "0.4", features = ["unstable-locales"] } pure-rust-locales = "0.8" [build-dependencies] pkg-config = "0.3" tiny-dfr-0.3.5/LICENSE000064400000000000000000000020711046102023000123720ustar 00000000000000MIT License Copyright (c) 2023 WhatAmISupposedToPutHere Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tiny-dfr-0.3.5/LICENSE.material000064400000000000000000000261351046102023000141760ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.tiny-dfr-0.3.5/README.md000064400000000000000000000011521046102023000126430ustar 00000000000000# tiny-dfr The most basic dynamic function row daemon possible ## Dependencies cairo, libinput, freetype, fontconfig, librsvg 2.59 or later, uinput enabled in kernel config ## License tiny-dfr is licensed under the MIT license, as included in the [LICENSE](LICENSE) file. * Copyright The Asahi Linux Contributors Please see the Git history for authorship information. tiny-dfr embeds Google's [material-design-icons](https://github.com/google/material-design-icons) which are licensed under [Apache License Version 2.0](LICENSE.material) Some icons are derivatives of material-icons, with edits made by kekrby. tiny-dfr-0.3.5/build.rs000064400000000000000000000001041046102023000130250ustar 00000000000000fn main() { pkg_config::probe_library("fontconfig").unwrap(); } tiny-dfr-0.3.5/etc/systemd/system/systemd-backlight@backlight:228200000.display-pipe.0.service000064400000000000000000000000001046102023000276400ustar 00000000000000tiny-dfr-0.3.5/etc/systemd/system/systemd-backlight@backlight:appletb_backlight.service000064400000000000000000000000001046102023000275650ustar 00000000000000tiny-dfr-0.3.5/etc/systemd/system/tiny-dfr.service000064400000000000000000000012661046102023000202770ustar 00000000000000[Unit] Description=Tiny Apple silicon touch bar daemon After=systemd-user-sessions.service getty@tty1.service plymouth-quit.service systemd-logind.service dev-tiny_dfr_display.device dev-tiny_dfr_backlight.device dev-tiny_dfr_display_backlight.device BindsTo=dev-tiny_dfr_display.device dev-tiny_dfr_backlight.device dev-tiny_dfr_display_backlight.device [Service] ExecStart=/usr/bin/tiny-dfr Restart=always NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true PrivateIPC=true ProtectKernelTunables=true ProtectKernelModules=true ProtectKernelLogs=true ProtectControlGroups=strict RestrictAddressFamilies=AF_UNIX AF_NETLINK RestrictNamespaces=true RestrictSUIDSGID=true tiny-dfr-0.3.5/etc/udev/rules.d/99-touchbar-seat.rules000064400000000000000000000006041046102023000205330ustar 00000000000000SUBSYSTEM=="drm", KERNEL=="card*", DRIVERS=="adp|appletbdrm", TAG-="master-of-seat", ENV{ID_SEAT}="seat-touchbar" SUBSYSTEM=="input", ATTR{name}=="Apple Inc. Touch Bar Display Touchpad", ENV{ID_SEAT}="seat-touchbar" SUBSYSTEM=="input", ATTR{name}=="MacBookPro17,1 Touch Bar", ENV{ID_SEAT}="seat-touchbar" SUBSYSTEM=="input", ATTR{name}=="Mac14,7 Touch Bar", ENV{ID_SEAT}="seat-touchbar" tiny-dfr-0.3.5/etc/udev/rules.d/99-touchbar-tiny-dfr.rules000064400000000000000000000030201046102023000213260ustar 00000000000000ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="05ac", ATTR{idProduct}=="8302", ATTR{bConfigurationValue}=="1", ATTR{bConfigurationValue}="0", ATTR{bConfigurationValue}="2" SUBSYSTEM=="input", ATTR{name}=="Apple Inc. Touch Bar Display Touchpad", TAG+="systemd", ENV{SYSTEMD_WANTS}="tiny-dfr.service" SUBSYSTEM=="input", ATTR{name}=="MacBookPro17,1 Touch Bar", TAG+="systemd", ENV{SYSTEMD_WANTS}="tiny-dfr.service" SUBSYSTEM=="input", ATTR{name}=="Mac14,7 Touch Bar", TAG+="systemd", ENV{SYSTEMD_WANTS}="tiny-dfr.service" SUBSYSTEM=="drm", KERNEL=="card[0-9]*", DRIVERS=="adp|appletbdrm", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_display" SUBSYSTEM=="backlight", KERNEL=="appletb_backlight", DRIVERS=="hid-appletb-bl", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_backlight" SUBSYSTEM=="backlight", KERNEL=="228200000.display-pipe.0", DRIVERS=="panel-summit", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_backlight" SUBSYSTEM=="backlight", KERNELS=="228600000.dsi.0", DRIVERS=="panel-summit", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_backlight" SUBSYSTEM=="backlight", KERNEL=="apple-panel-bl", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_display_backlight" SUBSYSTEM=="backlight", KERNEL=="gmux_backlight", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_display_backlight" SUBSYSTEM=="backlight", KERNEL=="intel_backlight", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_display_backlight" SUBSYSTEM=="backlight", KERNEL=="acpi_video0", TAG+="systemd", ENV{SYSTEMD_ALIAS}="/dev/tiny_dfr_display_backlight" tiny-dfr-0.3.5/flake.lock000064400000000000000000000010641046102023000133220ustar 00000000000000{ "nodes": { "nixpkgs": { "locked": { "lastModified": 1729449015, "narHash": "sha256-Gf04dXB0n4q0A9G5nTGH3zuMGr6jtJppqdeljxua1fo=", "owner": "nixos", "repo": "nixpkgs", "rev": "89172919243df199fe237ba0f776c3e3e3d72367", "type": "github" }, "original": { "owner": "nixos", "ref": "nixos-24.05", "repo": "nixpkgs", "type": "github" } }, "root": { "inputs": { "nixpkgs": "nixpkgs" } } }, "root": "root", "version": 7 } tiny-dfr-0.3.5/flake.nix000064400000000000000000000027741046102023000132010ustar 00000000000000{ description = "The most basic dynamic function row daemon possible"; inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-24.05"; }; outputs = { self, nixpkgs }: let supportedSystems = [ "x86_64-linux" "aarch64-linux" ]; forAllSystems = nixpkgs.lib.genAttrs supportedSystems; pkgsFor = forAllSystems (system: import nixpkgs { inherit system; }); in rec { packages = forAllSystems(system: let pkgs = pkgsFor.${system}; in { default = pkgs.rustPlatform.buildRustPackage { name = "tiny-dfr"; version = "0.2.0"; src = ./.; cargoLock = { lockFile = ./Cargo.lock; }; nativeBuildInputs = [ pkgs.pkg-config ]; buildInputs = [ pkgs.cairo pkgs.libinput pkgs.freetype pkgs.fontconfig pkgs.glib pkgs.pango pkgs.gdk-pixbuf pkgs.libxml2 ]; }; } ); devShells = forAllSystems(system: let pkgs = pkgsFor.${system}; in { default = pkgs.mkShell { inputsFrom = [ packages.${system}.default ]; packages = [ pkgs.rustfmt pkgs.rust-analyzer ]; RUST_SRC_PATH = "${pkgs.rustPlatform.rustLibSrc}"; }; } ); }; } tiny-dfr-0.3.5/share/tiny-dfr/backlight_high.svg000064400000000000000000000017761046102023000177060ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/backlight_low.svg000064400000000000000000000017551046102023000175650ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/brightness_high.svg000064400000000000000000000026741046102023000201240ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/brightness_low.svg000064400000000000000000000035671046102023000200100ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/config.toml000064400000000000000000000151431046102023000163710ustar 00000000000000# tiny-dfr config template. Do not edit this file directly, instead # copy it to /etc/tiny-dfr/config.toml and edit that copy. # The daemon will merge those two files, giving preference to the one in /etc # F{number} keys are shown when Fn is not pressed by default. # Set this to true if you want the media keys to be shown without Fn pressed MediaLayerDefault = false # Set this to false if you want to hide the button outline, # leaving only the text/logo ShowButtonOutlines = true # Set this to true to slowly shift the entire screen contents. # In theory this helps with screen longevity, but macos does not bother doing it # Disabling ShowButtonOutlines will make this effect less noticeable to the eye EnablePixelShift = false # Set this to the fontconfig pattern to be used to pick a font for text labels # Some examples are: # "" - default regular sans-serif font # ":bold" - default bold sans-serif font # For full reference on accepted values see the fontconfig user guide, # section "Font Names" # https://www.freedesktop.org/software/fontconfig/fontconfig-user.html FontTemplate = ":bold" # Set this to false if you want the brightness of the touchbar # to be set to a static value instead of following the primary # screen's brightness AdaptiveBrightness = true # With adaptive brightness disabled this is used as the brightness # in the active state # With it enabled, this is the maximum point on the brightness curve # Accepted values are 0-255 ActiveBrightness = 128 # This key defines the contents of the primary layer # (the one with F{number} keys) # You can change the individual buttons, add, or remove them # Any number of keys that is greater than 0 is allowed # however rendering will start to break around 24 keys # Buttons can be made larger by setting the optional Stretch field # to a number greater than 1 (which means the button will take up # that many button spaces). PrimaryLayerKeys = [ # Action defines the key code to send when the button is pressed # Text defines the button label # Icon specifies the icon to be used for the button. # Theme specifies the XDG icons theme. # Stretch specifies how many button spaces the button should take up # and defaults to 1 # Icons can either be svgs or pngs, with svgs being preferred # For best results with pngs, they should be 48x48 # Do not include the extension in the file name. # If a Theme is set, icons are looked up in XDG_DATA_DIRS. # Otherwise, they are first looked up in /etc/tiny-dfr, and then in /usr/share/tiny-dfr. # Time can be either 24hr, or 12hr. Locale is optional and will default to POSIX. # Only one of Text, Icon or Time is allowed, # if both are present, the behavior is undefined. # For the list of supported key codes see # https://docs.rs/input-linux/latest/input_linux/enum.Key.html # Note that the escape key is not specified here, as it is added # automatically on Macs without a physical one { Text = "F1", Action = "F1" }, { Text = "F2", Action = "F2" }, { Text = "F3", Action = "F3" }, { Text = "F4", Action = "F4" }, { Text = "F5", Action = "F5" }, { Text = "F6", Action = "F6" }, { Text = "F7", Action = "F7" }, { Text = "F8", Action = "F8" }, { Text = "F9", Action = "F9" }, { Text = "F10", Action = "F10" }, { Text = "F11", Action = "F11" }, { Text = "F12", Action = "F12" } # Example with Stretch: # # because most buttons have stretch 2, they behave as if they all had 1: # { Text = "F1", Action = "F1", Stretch = 2 }, # { Text = "F2", Action = "F2", Stretch = 2 }, # # these two buttons are half the size of the other buttons: # { Text = "F3", Action = "F3", Stretch = 1 }, # { Text = "F4", Action = "F4", Stretch = 1 }, # { Text = "F5", Action = "F5", Stretch = 2 }, # { Text = "F6", Action = "F6", Stretch = 2 }, # { Text = "F7", Action = "F7", Stretch = 2 }, # # these two buttons are one and a half the size of the other buttons: # { Text = "F8", Action = "F8", Stretch = 3 }, # { Text = "F9", Action = "F9", Stretch = 3 }, # { Text = "F10", Action = "F10", Stretch = 2 }, # { Text = "F11", Action = "F11", Stretch = 2 }, # { Text = "F12", Action = "F12", Stretch = 2 } # Example of Time: # { Time = "12hr", Action = "Time"}, # Example of Time with locale: # { Time = "12hr", Locale = "en_IN", Action = "Time"}, # Example of Time with stretch: # # the time key by default will be 3 times wider than the rest keys. # # So the stretch value assigned will be 3 times the regular keys. # { Time = "12hr", Action = "Time", Stretch = 2}, # # Here, time key will be 3x2=6 times the normal keys. ] # This key defines the contents of the media key layer MediaLayerKeys = [ { Icon = "brightness_low", Action = "BrightnessDown" }, { Icon = "brightness_high", Action = "BrightnessUp" }, { Icon = "mic_off", Action = "MicMute" }, { Icon = "search", Action = "Search" }, { Icon = "backlight_low", Action = "IllumDown" }, { Icon = "backlight_high", Action = "IllumUp" }, { Icon = "fast_rewind", Action = "PreviousSong" }, { Icon = "play_pause", Action = "PlayPause" }, { Icon = "fast_forward", Action = "NextSong" }, { Icon = "volume_off", Action = "Mute" }, { Icon = "volume_down", Action = "VolumeDown" }, { Icon = "volume_up", Action = "VolumeUp" } # Example with XDG icons (requires `breeze-dark` theme installed): # { Icon = "brightness-low", Theme = "breeze-dark", Action = "BrightnessDown" }, # { Icon = "brightness-high", Theme = "breeze-dark", Action = "BrightnessUp" }, # { Icon = "microphone", Theme = "breeze-dark", Action = "MicMute" }, # { Icon = "search", Theme = "breeze-dark", Action = "Search" }, # { Icon = "redshift-status-day", Theme = "breeze-dark", Action = "IllumDown" }, # { Icon = "redshift-status-on", Theme = "breeze-dark", Action = "IllumUp" }, # { Icon = "media-skip-backward", Theme = "breeze-dark", Action = "PreviousSong" }, # { Icon = "media-playback-start", Theme = "breeze-dark", Action = "PlayPause" }, # { Icon = "media-skip-forward", Theme = "breeze-dark", Action = "NextSong" }, # { Icon = "audio-volume-muted", Theme = "breeze-dark", Action = "Mute" }, # { Icon = "audio-volume-low", Theme = "breeze-dark", Action = "VolumeDown" }, # { Icon = "audio-volume-high", Theme = "breeze-dark", Action = "VolumeUp" } ] tiny-dfr-0.3.5/share/tiny-dfr/fast_forward.svg000064400000000000000000000004511046102023000174250ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/fast_rewind.svg000064400000000000000000000004501046102023000172500ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/mic_off.svg000064400000000000000000000011251046102023000163450ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/play_pause.svg000064400000000000000000000015051046102023000171070ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/search.svg000064400000000000000000000007351046102023000162160ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/volume_down.svg000064400000000000000000000005241046102023000173030ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/volume_off.svg000064400000000000000000000012651046102023000171110ustar 00000000000000tiny-dfr-0.3.5/share/tiny-dfr/volume_up.svg000064400000000000000000000010101046102023000167470ustar 00000000000000tiny-dfr-0.3.5/src/backlight.rs000064400000000000000000000113711046102023000144550ustar 00000000000000use crate::config::Config; use crate::TIMEOUT_MS; use anyhow::{anyhow, Result}; use input::event::{ switch::{Switch, SwitchEvent, SwitchState}, Event, }; use std::{ cmp::min, fs::{self, File, OpenOptions}, io::Write, path::{Path, PathBuf}, time::Instant, }; const MAX_DISPLAY_BRIGHTNESS: u32 = 509; const MAX_TOUCH_BAR_BRIGHTNESS: u32 = 255; const BRIGHTNESS_DIM_TIMEOUT: i32 = TIMEOUT_MS * 3; // should be a multiple of TIMEOUT_MS const BRIGHTNESS_OFF_TIMEOUT: i32 = TIMEOUT_MS * 6; // should be a multiple of TIMEOUT_MS const DIMMED_BRIGHTNESS: u32 = 1; fn read_attr(path: &Path, attr: &str) -> u32 { fs::read_to_string(path.join(attr)) .unwrap_or_else(|_| panic!("Failed to read {attr}")) .trim() .parse::() .unwrap_or_else(|_| panic!("Failed to parse {attr}")) } fn find_backlight() -> Result { for entry in fs::read_dir("/sys/class/backlight/")? { let entry = entry?; let file_name = entry.file_name(); let name = file_name.to_string_lossy(); if ["display-pipe", "228600000.dsi.0", "appletb_backlight"] .iter() .any(|s| name.contains(s)) { return Ok(entry.path()); } } Err(anyhow!("No Touch Bar backlight device found")) } fn find_display_backlight() -> Result { for entry in fs::read_dir("/sys/class/backlight/")? { let entry = entry?; if [ "apple-panel-bl", "gmux_backlight", "intel_backlight", "acpi_video0", ] .iter() .any(|s| entry.file_name().to_string_lossy().contains(s)) { return Ok(entry.path()); } } Err(anyhow!("No Built-in Retina Display backlight device found")) } fn set_backlight(mut file: &File, value: u32) { file.write_all(format!("{}\n", value).as_bytes()).unwrap(); } pub struct BacklightManager { last_active: Instant, max_bl: u32, current_bl: u32, lid_state: SwitchState, bl_file: File, display_bl_path: PathBuf, } impl BacklightManager { pub fn new() -> BacklightManager { let bl_path = find_backlight().unwrap(); let display_bl_path = find_display_backlight().unwrap(); let bl_file = OpenOptions::new() .write(true) .open(bl_path.join("brightness")) .unwrap(); BacklightManager { bl_file, lid_state: SwitchState::Off, max_bl: read_attr(&bl_path, "max_brightness"), current_bl: read_attr(&bl_path, "brightness"), last_active: Instant::now(), display_bl_path, } } fn display_to_touchbar(display: u32, active_brightness: u32) -> u32 { let normalized = display as f64 / MAX_DISPLAY_BRIGHTNESS as f64; // Add one so that the touch bar does not turn off let adjusted = (normalized.powf(0.5) * active_brightness as f64) as u32 + 1; adjusted.min(MAX_TOUCH_BAR_BRIGHTNESS) // Clamp the value to the maximum allowed brightness } pub fn process_event(&mut self, event: &Event) { match event { Event::Keyboard(_) | Event::Pointer(_) | Event::Gesture(_) | Event::Touch(_) => { self.last_active = Instant::now(); } Event::Switch(SwitchEvent::Toggle(toggle)) => { if let Some(Switch::Lid) = toggle.switch() { self.lid_state = toggle.switch_state(); println!("Lid Switch event: {:?}", self.lid_state); if toggle.switch_state() == SwitchState::Off { self.last_active = Instant::now(); } } } _ => {} } } pub fn update_backlight(&mut self, cfg: &Config) { let since_last_active = (Instant::now() - self.last_active).as_millis() as u64; let new_bl = min( self.max_bl, if self.lid_state == SwitchState::On { 0 } else if since_last_active < BRIGHTNESS_DIM_TIMEOUT as u64 { if cfg.adaptive_brightness { BacklightManager::display_to_touchbar( read_attr(&self.display_bl_path, "brightness"), cfg.active_brightness, ) } else { cfg.active_brightness } } else if since_last_active < BRIGHTNESS_OFF_TIMEOUT as u64 { DIMMED_BRIGHTNESS } else { 0 }, ); if self.current_bl != new_bl { self.current_bl = new_bl; set_backlight(&self.bl_file, self.current_bl); } } pub fn current_bl(&self) -> u32 { self.current_bl } } tiny-dfr-0.3.5/src/config.rs000064400000000000000000000135401046102023000137720ustar 00000000000000use crate::fonts::{FontConfig, Pattern}; use crate::FunctionLayer; use anyhow::Error; use cairo::FontFace; use freetype::Library as FtLibrary; use input_linux::Key; use nix::{ errno::Errno, sys::inotify::{AddWatchFlags, InitFlags, Inotify, WatchDescriptor}, }; use serde::Deserialize; use std::{fs::read_to_string, os::fd::AsFd}; const USER_CFG_PATH: &str = "/etc/tiny-dfr/config.toml"; pub struct Config { pub show_button_outlines: bool, pub enable_pixel_shift: bool, pub font_face: FontFace, pub adaptive_brightness: bool, pub active_brightness: u32, } #[derive(Deserialize)] #[serde(rename_all = "PascalCase")] struct ConfigProxy { media_layer_default: Option, show_button_outlines: Option, enable_pixel_shift: Option, font_template: Option, adaptive_brightness: Option, active_brightness: Option, primary_layer_keys: Option>, media_layer_keys: Option>, } #[derive(Deserialize)] #[serde(rename_all = "PascalCase")] pub struct ButtonConfig { #[serde(alias = "Svg")] pub icon: Option, pub text: Option, pub theme: Option, pub time: Option, pub locale: Option, pub action: Key, pub stretch: Option, } fn load_font(name: &str) -> FontFace { let fontconfig = FontConfig::new(); let mut pattern = Pattern::new(name); fontconfig.perform_substitutions(&mut pattern); let pat_match = match fontconfig.match_pattern(&pattern) { Ok(pat) => pat, Err(_) => panic!("Unable to find specified font. If you are using the default config, make sure you have at least one font installed") }; let file_name = pat_match.get_file_name(); let file_idx = pat_match.get_font_index(); let ft_library = FtLibrary::init().unwrap(); let face = ft_library.new_face(file_name, file_idx).unwrap(); FontFace::create_from_ft(&face).unwrap() } fn load_config(width: u16) -> (Config, [FunctionLayer; 2]) { let mut base = toml::from_str::(&read_to_string("/usr/share/tiny-dfr/config.toml").unwrap()) .unwrap(); let user = read_to_string(USER_CFG_PATH) .map_err::(|e| e.into()) .and_then(|r| Ok(toml::from_str::(&r)?)); if let Ok(user) = user { base.media_layer_default = user.media_layer_default.or(base.media_layer_default); base.show_button_outlines = user.show_button_outlines.or(base.show_button_outlines); base.enable_pixel_shift = user.enable_pixel_shift.or(base.enable_pixel_shift); base.font_template = user.font_template.or(base.font_template); base.adaptive_brightness = user.adaptive_brightness.or(base.adaptive_brightness); base.media_layer_keys = user.media_layer_keys.or(base.media_layer_keys); base.primary_layer_keys = user.primary_layer_keys.or(base.primary_layer_keys); base.active_brightness = user.active_brightness.or(base.active_brightness); }; let mut media_layer_keys = base.media_layer_keys.unwrap(); let mut primary_layer_keys = base.primary_layer_keys.unwrap(); if width >= 2170 { for layer in [&mut media_layer_keys, &mut primary_layer_keys] { layer.insert( 0, ButtonConfig { icon: None, text: Some("esc".into()), theme: None, action: Key::Esc, stretch: None, time: None, locale: None, }, ); } } let media_layer = FunctionLayer::with_config(media_layer_keys); let fkey_layer = FunctionLayer::with_config(primary_layer_keys); let layers = if base.media_layer_default.unwrap() { [media_layer, fkey_layer] } else { [fkey_layer, media_layer] }; let cfg = Config { show_button_outlines: base.show_button_outlines.unwrap(), enable_pixel_shift: base.enable_pixel_shift.unwrap(), adaptive_brightness: base.adaptive_brightness.unwrap(), font_face: load_font(&base.font_template.unwrap()), active_brightness: base.active_brightness.unwrap(), }; (cfg, layers) } pub struct ConfigManager { inotify_fd: Inotify, watch_desc: Option, } fn arm_inotify(inotify_fd: &Inotify) -> Option { let flags = AddWatchFlags::IN_MOVED_TO | AddWatchFlags::IN_CLOSE | AddWatchFlags::IN_ONESHOT; match inotify_fd.add_watch(USER_CFG_PATH, flags) { Ok(wd) => Some(wd), Err(Errno::ENOENT) => None, e => Some(e.unwrap()), } } impl ConfigManager { pub fn new() -> ConfigManager { let inotify_fd = Inotify::init(InitFlags::IN_NONBLOCK).unwrap(); let watch_desc = arm_inotify(&inotify_fd); ConfigManager { inotify_fd, watch_desc, } } pub fn load_config(&self, width: u16) -> (Config, [FunctionLayer; 2]) { load_config(width) } pub fn update_config( &mut self, cfg: &mut Config, layers: &mut [FunctionLayer; 2], width: u16, ) -> bool { if self.watch_desc.is_none() { self.watch_desc = arm_inotify(&self.inotify_fd); return false; } let evts = match self.inotify_fd.read_events() { Ok(e) => e, Err(Errno::EAGAIN) => Vec::new(), r => r.unwrap(), }; let mut ret = false; for evt in evts { if evt.wd != self.watch_desc.unwrap() { continue; } let parts = load_config(width); *cfg = parts.0; *layers = parts.1; ret = true; self.watch_desc = arm_inotify(&self.inotify_fd); } ret } pub fn fd(&self) -> &impl AsFd { &self.inotify_fd } } tiny-dfr-0.3.5/src/crash_bitmap.raw000064400000000000000000000373001046102023000153260ustar 00000000000000?????????????????00 ?0?????????????????????????????????????93333?3333;???????;;;;;??????????????????????????????????????????????????933333???????????????????;;;;;???????????????????????????0 !????????????????????3333;???;;;;;????????0?8!???????????????;;;;;?????????????????????????????????????????????;;;;;?????????????????????????tiny-dfr-0.3.5/src/display.rs000064400000000000000000000137331046102023000141760ustar 00000000000000use anyhow::{anyhow, Result}; use drm::{ buffer::DrmFourcc, control::{ atomic, connector, dumbbuffer::{DumbBuffer, DumbMapping}, framebuffer, property, AtomicCommitFlags, ClipRect, Device as ControlDevice, Mode, ResourceHandle, }, ClientCapability, Device as DrmDevice, }; use std::{ fs::{self, File, OpenOptions}, os::unix::io::{AsFd, BorrowedFd}, path::Path, }; struct Card(File); impl AsFd for Card { fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() } } impl ControlDevice for Card {} impl DrmDevice for Card {} impl Card { fn open(path: &Path) -> Result { let mut options = OpenOptions::new(); options.read(true); options.write(true); Ok(Card(options.open(path)?)) } } pub struct DrmBackend { card: Card, mode: Mode, db: DumbBuffer, fb: framebuffer::Handle, } impl Drop for DrmBackend { fn drop(&mut self) { self.card.destroy_framebuffer(self.fb).unwrap(); self.card.destroy_dumb_buffer(self.db).unwrap(); } } fn find_prop_id( card: &Card, handle: T, name: &'static str, ) -> Result { let props = card.get_properties(handle)?; for id in props.as_props_and_values().0 { let info = card.get_property(*id)?; if info.name().to_str()? == name { return Ok(*id); } } Err(anyhow!("Property not found")) } fn try_open_card(path: &Path) -> Result { let card = Card::open(path)?; card.set_client_capability(ClientCapability::UniversalPlanes, true)?; card.set_client_capability(ClientCapability::Atomic, true)?; card.acquire_master_lock()?; let res = card.resource_handles()?; let coninfo = res .connectors() .iter() .flat_map(|con| card.get_connector(*con, true)) .collect::>(); let crtcinfo = res .crtcs() .iter() .flat_map(|crtc| card.get_crtc(*crtc)) .collect::>(); let con = coninfo .iter() .find(|&i| i.state() == connector::State::Connected) .ok_or(anyhow!("No connected connectors found"))?; let &mode = con.modes().first().ok_or(anyhow!("No modes found"))?; let (disp_width, disp_height) = mode.size(); if disp_height / disp_width < 30 { return Err(anyhow!("This does not look like a touchbar")); } let crtc = crtcinfo.first().ok_or(anyhow!("No crtcs found"))?; let fmt = DrmFourcc::Xrgb8888; let db = card.create_dumb_buffer((64, disp_height.into()), fmt, 32)?; let fb = card.add_framebuffer(&db, 24, 32)?; let plane = *card .plane_handles()? .first() .ok_or(anyhow!("No planes found"))?; let mut atomic_req = atomic::AtomicModeReq::new(); atomic_req.add_property( con.handle(), find_prop_id(&card, con.handle(), "CRTC_ID")?, property::Value::CRTC(Some(crtc.handle())), ); let blob = card.create_property_blob(&mode)?; atomic_req.add_property( crtc.handle(), find_prop_id(&card, crtc.handle(), "MODE_ID")?, blob, ); atomic_req.add_property( crtc.handle(), find_prop_id(&card, crtc.handle(), "ACTIVE")?, property::Value::Boolean(true), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "FB_ID")?, property::Value::Framebuffer(Some(fb)), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "CRTC_ID")?, property::Value::CRTC(Some(crtc.handle())), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "SRC_X")?, property::Value::UnsignedRange(0), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "SRC_Y")?, property::Value::UnsignedRange(0), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "SRC_W")?, property::Value::UnsignedRange((mode.size().0 as u64) << 16), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "SRC_H")?, property::Value::UnsignedRange((mode.size().1 as u64) << 16), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "CRTC_X")?, property::Value::SignedRange(0), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "CRTC_Y")?, property::Value::SignedRange(0), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "CRTC_W")?, property::Value::UnsignedRange(mode.size().0 as u64), ); atomic_req.add_property( plane, find_prop_id(&card, plane, "CRTC_H")?, property::Value::UnsignedRange(mode.size().1 as u64), ); card.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, atomic_req)?; Ok(DrmBackend { card, mode, db, fb }) } impl DrmBackend { pub fn open_card() -> Result { let mut errors = Vec::new(); for entry in fs::read_dir("/dev/dri/")? { let entry = entry?; if !entry.file_name().to_string_lossy().starts_with("card") { continue; } match try_open_card(&entry.path()) { Ok(card) => return Ok(card), Err(err) => errors.push(format!( "{}: {}", entry.path().as_os_str().to_string_lossy(), err )), } } Err(anyhow!( "No touchbar device found, attempted: [\n {}\n]", errors.join(",\n ") )) } pub fn mode(&self) -> Mode { self.mode } pub fn fb_info(&self) -> Result { Ok(self.card.get_framebuffer(self.fb)?) } pub fn dirty(&self, clips: &[ClipRect]) -> Result<()> { Ok(self.card.dirty_framebuffer(self.fb, clips)?) } pub fn map(&mut self) -> Result { Ok(self.card.map_dumb_buffer(&mut self.db)?) } } tiny-dfr-0.3.5/src/fonts.rs000064400000000000000000000073661046102023000136670ustar 00000000000000#![allow(non_upper_case_globals)] use std::ffi::{c_char, c_int, CStr, CString}; use std::ptr; #[repr(C)] struct FcPattern { _data: [u8; 0], } #[repr(C)] struct FcConfig { _data: [u8; 0], } type FcResult = c_int; const FcResultMatch: FcResult = 0; const FcResultNoMatch: FcResult = 1; const FcResultTypeMismatch: FcResult = 2; const FcResultNoId: FcResult = 3; const FcResultOutOfMemory: FcResult = 4; type FcMatchKind = c_int; const FcMatchPattern: FcMatchKind = 0; pub enum FontConfigError { FontNotFound, } pub struct FontConfig { config: *const FcConfig, } impl FontConfig { pub fn new() -> FontConfig { let config = unsafe { FcInitLoadConfigAndFonts() }; FontConfig { config } } pub fn match_pattern(&self, pattern: &Pattern) -> Result { let mut result: FcResult = 0; let match_ = unsafe { FcFontMatch(self.config, pattern.pattern, &mut result) }; if match_.is_null() { return Err(FontConfigError::FontNotFound); } Ok(Pattern { pattern: match_ }) } pub fn perform_substitutions(&self, pattern: &mut Pattern) { unsafe { if (FcConfigSubstitute(self.config, pattern.pattern, FcMatchPattern)) == 0 { panic!("Allocation error while loading fontconfig data"); } FcDefaultSubstitute(pattern.pattern); } } } impl Drop for FontConfig { fn drop(&mut self) { unsafe { FcConfigDestroy(self.config) } } } fn throw_on_fcpattern_result(res: FcResult) { match res { FcResultMatch => {} FcResultNoMatch => { panic!("NULL pattern"); } FcResultTypeMismatch => { panic!("Wrong type for pattern element"); } FcResultNoId => { panic!("Unknown pattern element"); } FcResultOutOfMemory => { panic!("Out of memory"); } r => { panic!("Unknown fontconfig return value {:?}", r) } } } pub struct Pattern { pattern: *const FcPattern, } impl Pattern { pub fn new(st: &str) -> Pattern { let cstr = CString::new(st).unwrap(); let pattern = unsafe { FcNameParse(cstr.as_ptr()) }; Pattern { pattern } } pub fn get_file_name(&self) -> &str { let name = CString::new("file").unwrap(); unsafe { let mut file_name = ptr::null(); let res = FcPatternGetString(self.pattern, name.as_ptr(), 0, &mut file_name); throw_on_fcpattern_result(res); CStr::from_ptr(file_name).to_str().unwrap() } } pub fn get_font_index(&self) -> isize { let name = CString::new("index").unwrap(); unsafe { let mut index = 0; let res = FcPatternGetInteger(self.pattern, name.as_ptr(), 0, &mut index); throw_on_fcpattern_result(res); index as isize } } } impl Drop for Pattern { fn drop(&mut self) { unsafe { FcPatternDestroy(self.pattern) } } } extern "C" { fn FcInitLoadConfigAndFonts() -> *const FcConfig; fn FcConfigDestroy(_: *const FcConfig); fn FcNameParse(_: *const c_char) -> *const FcPattern; fn FcPatternDestroy(_: *const FcPattern); fn FcFontMatch(_: *const FcConfig, _: *const FcPattern, _: *mut FcResult) -> *mut FcPattern; fn FcPatternGetString( _: *const FcPattern, _: *const c_char, _: c_int, _: *mut *const c_char, ) -> FcResult; fn FcPatternGetInteger( _: *const FcPattern, _: *const c_char, _: c_int, _: *mut c_int, ) -> FcResult; fn FcConfigSubstitute(_: *const FcConfig, _: *const FcPattern, _: FcMatchKind) -> c_int; fn FcDefaultSubstitute(_: *const FcPattern); } tiny-dfr-0.3.5/src/main.rs000064400000000000000000000617531046102023000134620ustar 00000000000000use anyhow::{anyhow, Result}; use cairo::{Antialias, Context, Format, ImageSurface, Surface}; use chrono::{Local, Locale, Timelike}; use drm::control::ClipRect; use freedesktop_icons::lookup; use input::{ event::{ device::DeviceEvent, keyboard::{KeyState, KeyboardEvent, KeyboardEventTrait}, touch::{TouchEvent, TouchEventPosition, TouchEventSlot}, Event, EventTrait, }, Device as InputDevice, Libinput, LibinputInterface, }; use input_linux::{uinput::UInputHandle, EventKind, Key, SynchronizeKind}; use input_linux_sys::{input_event, input_id, timeval, uinput_setup}; use libc::{c_char, O_ACCMODE, O_RDONLY, O_RDWR, O_WRONLY}; use librsvg_rebind::{prelude::HandleExt, Handle, Rectangle}; use nix::{ errno::Errno, sys::{ epoll::{Epoll, EpollCreateFlags, EpollEvent, EpollFlags}, signal::{SigSet, Signal}, }, }; use privdrop::PrivDrop; use std::{ cmp::min, collections::HashMap, fs::{File, OpenOptions}, os::{ fd::{AsFd, AsRawFd}, unix::{fs::OpenOptionsExt, io::OwnedFd}, }, panic::{self, AssertUnwindSafe}, path::{Path, PathBuf}, }; mod backlight; mod config; mod display; mod fonts; mod pixel_shift; use crate::config::ConfigManager; use backlight::BacklightManager; use config::{ButtonConfig, Config}; use display::DrmBackend; use pixel_shift::{PixelShiftManager, PIXEL_SHIFT_WIDTH_PX}; const BUTTON_SPACING_PX: i32 = 16; const BUTTON_COLOR_INACTIVE: f64 = 0.200; const BUTTON_COLOR_ACTIVE: f64 = 0.400; const ICON_SIZE: i32 = 48; const TIMEOUT_MS: i32 = 10 * 1000; enum ButtonImage { Text(String), Svg(Handle), Bitmap(ImageSurface), Time(String, String), } struct Button { image: ButtonImage, changed: bool, active: bool, action: Key, } fn try_load_svg(path: &str) -> Result { Ok(ButtonImage::Svg( Handle::from_file(path)?.ok_or(anyhow!("failed to load image"))?, )) } fn try_load_png(path: impl AsRef) -> Result { let mut file = File::open(path)?; let surf = ImageSurface::create_from_png(&mut file)?; if surf.height() == ICON_SIZE && surf.width() == ICON_SIZE { return Ok(ButtonImage::Bitmap(surf)); } let resized = ImageSurface::create(Format::ARgb32, ICON_SIZE, ICON_SIZE).unwrap(); let c = Context::new(&resized).unwrap(); c.scale( ICON_SIZE as f64 / surf.width() as f64, ICON_SIZE as f64 / surf.height() as f64, ); c.set_source_surface(surf, 0.0, 0.0).unwrap(); c.set_antialias(Antialias::Best); c.paint().unwrap(); Ok(ButtonImage::Bitmap(resized)) } fn try_load_image(name: impl AsRef, theme: Option>) -> Result { let name = name.as_ref(); let locations; // Load list of candidate locations if let Some(theme) = theme { // Freedesktop icons let theme = theme.as_ref(); let candidates = vec![ lookup(name) .with_cache() .with_theme(theme) .with_size(ICON_SIZE as u16) .force_svg() .find(), lookup(name) .with_cache() .with_theme(theme) .force_svg() .find(), ]; // .flatten() removes `None` and unwraps `Some` values locations = candidates.into_iter().flatten().collect(); } else { // Standard file icons locations = vec![ PathBuf::from(format!("/etc/tiny-dfr/{name}.svg")), PathBuf::from(format!("/etc/tiny-dfr/{name}.png")), PathBuf::from(format!("/usr/share/tiny-dfr/{name}.svg")), PathBuf::from(format!("/usr/share/tiny-dfr/{name}.png")), ]; }; // Try to load each candidate let mut last_err = anyhow!("no suitable icon path was found"); // in case locations is empty for location in locations { let result = match location.extension().and_then(|s| s.to_str()) { Some("png") => try_load_png(&location), Some("svg") => try_load_svg( location .to_str() .ok_or(anyhow!("image path is not unicode"))?, ), _ => Err(anyhow!("invalid file extension")), }; match result { Ok(image) => return Ok(image), Err(err) => { last_err = err.context(format!("while loading path {}", location.display())); } }; } // if function hasn't returned by now, all sources have been exhausted Err(last_err.context(format!("failed loading all possible paths for icon {name}"))) } impl Button { fn with_config(cfg: ButtonConfig) -> Button { if let Some(text) = cfg.text { Button::new_text(text, cfg.action) } else if let Some(icon) = cfg.icon { Button::new_icon(&icon, cfg.theme, cfg.action) } else if let Some(time) = cfg.time { let locale = match cfg.locale { Some(l) => l, None => "POSIX".to_string() }; Button::new_time(cfg.action, time, locale) } else { panic!("Invalid config, a button must have either Text, Icon or Time") } } fn new_text(text: String, action: Key) -> Button { Button { action, active: false, changed: false, image: ButtonImage::Text(text), } } fn new_icon(path: impl AsRef, theme: Option>, action: Key) -> Button { let image = try_load_image(path, theme).expect("failed to load icon"); Button { action, image, active: false, changed: false, } } fn new_time(action: Key, format: String, locale: String) -> Button { Button { action, active: false, changed: false, image: ButtonImage::Time(format, locale), } } fn render( &self, c: &Context, height: i32, button_left_edge: f64, button_width: u64, y_shift: f64, ) { match &self.image { ButtonImage::Text(text) => { let extents = c.text_extents(text).unwrap(); c.move_to( button_left_edge + (button_width as f64 / 2.0 - extents.width() / 2.0).round(), y_shift + (height as f64 / 2.0 + extents.height() / 2.0).round(), ); c.show_text(text).unwrap(); } ButtonImage::Svg(svg) => { let x = button_left_edge + (button_width as f64 / 2.0 - (ICON_SIZE / 2) as f64).round(); let y = y_shift + ((height as f64 - ICON_SIZE as f64) / 2.0).round(); svg.render_document(c, &Rectangle::new(x, y, ICON_SIZE as f64, ICON_SIZE as f64)) .unwrap(); } ButtonImage::Bitmap(surf) => { let x = button_left_edge + (button_width as f64 / 2.0 - (ICON_SIZE / 2) as f64).round(); let y = y_shift + ((height as f64 - ICON_SIZE as f64) / 2.0).round(); c.set_source_surface(surf, x, y).unwrap(); c.rectangle(x, y, ICON_SIZE as f64, ICON_SIZE as f64); c.fill().unwrap(); } ButtonImage::Time(format, locale) => { let current_time = Local::now(); let current_locale = Locale::try_from(locale.as_str()).unwrap_or(Locale::POSIX); let formatted_time; if format == "24hr" { formatted_time = format!( "{}:{} {} {} {}", current_time.format_localized("%H", current_locale), current_time.format_localized("%M", current_locale), current_time.format_localized("%a", current_locale), current_time.format_localized("%-e", current_locale), current_time.format_localized("%b", current_locale) ); } else { formatted_time = format!( "{}:{} {} {} {} {}", current_time.format_localized("%-l", current_locale), current_time.format_localized("%M", current_locale), current_time.format_localized("%p", current_locale), current_time.format_localized("%a", current_locale), current_time.format_localized("%-e", current_locale), current_time.format_localized("%b", current_locale) ); } let time_extents = c.text_extents(&formatted_time).unwrap(); c.move_to( button_left_edge + (button_width as f64 / 2.0 - time_extents.width() / 2.0).round(), y_shift + (height as f64 / 2.0 + time_extents.height() / 2.0).round() ); c.show_text(&formatted_time).unwrap(); } } } fn set_active(&mut self, uinput: &mut UInputHandle, active: bool) where F: AsRawFd, { if self.active != active { self.active = active; self.changed = true; toggle_key(uinput, self.action, active as i32); } } } #[derive(Default)] pub struct FunctionLayer { buttons: Vec<(usize, Button)>, virtual_button_count: usize, } impl FunctionLayer { fn with_config(cfg: Vec) -> FunctionLayer { if cfg.is_empty() { panic!("Invalid configuration, layer has 0 buttons"); } let mut virtual_button_count = 0; FunctionLayer { buttons: cfg .into_iter() .scan(&mut virtual_button_count, |state, cfg| { let i = **state; let mut stretch = cfg.stretch.unwrap_or(1); if stretch < 1 { println!("Stretch value must be at least 1, setting to 1."); stretch = 1; } if cfg.time.is_some() { stretch = stretch * 3; } **state += stretch; Some((i, Button::with_config(cfg))) }) .collect(), virtual_button_count, } } fn draw( &mut self, config: &Config, width: i32, height: i32, surface: &Surface, pixel_shift: (f64, f64), complete_redraw: bool, ) -> Vec { let c = Context::new(surface).unwrap(); let mut modified_regions = if complete_redraw { vec![ClipRect::new(0, 0, height as u16, width as u16)] } else { Vec::new() }; c.translate(height as f64, 0.0); c.rotate((90.0f64).to_radians()); let pixel_shift_width = if config.enable_pixel_shift { PIXEL_SHIFT_WIDTH_PX } else { 0 }; let virtual_button_width = ((width - pixel_shift_width as i32) - (BUTTON_SPACING_PX * (self.virtual_button_count - 1) as i32)) as f64 / self.virtual_button_count as f64; let radius = 8.0f64; let bot = (height as f64) * 0.15; let top = (height as f64) * 0.85; let (pixel_shift_x, pixel_shift_y) = pixel_shift; if complete_redraw { c.set_source_rgb(0.0, 0.0, 0.0); c.paint().unwrap(); } c.set_font_face(&config.font_face); c.set_font_size(32.0); for i in 0..self.buttons.len() { let end = if i + 1 < self.buttons.len() { self.buttons[i + 1].0 } else { self.virtual_button_count }; let (start, button) = &mut self.buttons[i]; let start = *start; if !button.changed && !complete_redraw { continue; }; let left_edge = (start as f64 * (virtual_button_width + BUTTON_SPACING_PX as f64)) .floor() + pixel_shift_x + (pixel_shift_width / 2) as f64; let button_width = virtual_button_width + ((end - start - 1) as f64 * (virtual_button_width + BUTTON_SPACING_PX as f64)) .floor(); let color = if button.active { BUTTON_COLOR_ACTIVE } else if config.show_button_outlines { BUTTON_COLOR_INACTIVE } else { 0.0 }; if !complete_redraw { c.set_source_rgb(0.0, 0.0, 0.0); c.rectangle( left_edge, bot - radius, button_width, top - bot + radius * 2.0, ); c.fill().unwrap(); } c.set_source_rgb(color, color, color); // draw box with rounded corners c.new_sub_path(); let left = left_edge + radius; let right = (left_edge + button_width.ceil()) - radius; c.arc( right, bot, radius, (-90.0f64).to_radians(), (0.0f64).to_radians(), ); c.arc( right, top, radius, (0.0f64).to_radians(), (90.0f64).to_radians(), ); c.arc( left, top, radius, (90.0f64).to_radians(), (180.0f64).to_radians(), ); c.arc( left, bot, radius, (180.0f64).to_radians(), (270.0f64).to_radians(), ); c.close_path(); c.fill().unwrap(); c.set_source_rgb(1.0, 1.0, 1.0); button.render( &c, height, left_edge, button_width.ceil() as u64, pixel_shift_y, ); button.changed = false; if !complete_redraw { modified_regions.push(ClipRect::new( height as u16 - top as u16 - radius as u16, left_edge as u16, height as u16 - bot as u16 + radius as u16, left_edge as u16 + button_width as u16, )); } } modified_regions } fn hit(&self, width: u16, height: u16, x: f64, y: f64, i: Option) -> Option { let virtual_button_width = (width as i32 - (BUTTON_SPACING_PX * (self.virtual_button_count - 1) as i32)) as f64 / self.virtual_button_count as f64; let i = i.unwrap_or_else(|| { let virtual_i = (x / (width as f64 / self.virtual_button_count as f64)) as usize; self.buttons .iter() .position(|(start, _)| *start > virtual_i) .unwrap_or(self.buttons.len()) - 1 }); if i >= self.buttons.len() { return None; } let start = self.buttons[i].0; let end = if i + 1 < self.buttons.len() { self.buttons[i + 1].0 } else { self.virtual_button_count }; let left_edge = (start as f64 * (virtual_button_width + BUTTON_SPACING_PX as f64)).floor(); let button_width = virtual_button_width + ((end - start - 1) as f64 * (virtual_button_width + BUTTON_SPACING_PX as f64)) .floor(); if x < left_edge || x > (left_edge + button_width) || y < 0.1 * height as f64 || y > 0.9 * height as f64 { return None; } Some(i) } } struct Interface; impl LibinputInterface for Interface { fn open_restricted(&mut self, path: &Path, flags: i32) -> Result { let mode = flags & O_ACCMODE; OpenOptions::new() .custom_flags(flags) .read(mode == O_RDONLY || mode == O_RDWR) .write(mode == O_WRONLY || mode == O_RDWR) .open(path) .map(|file| file.into()) .map_err(|err| err.raw_os_error().unwrap()) } fn close_restricted(&mut self, fd: OwnedFd) { _ = File::from(fd); } } fn emit(uinput: &mut UInputHandle, ty: EventKind, code: u16, value: i32) where F: AsRawFd, { uinput .write(&[input_event { value, type_: ty as u16, code, time: timeval { tv_sec: 0, tv_usec: 0, }, }]) .unwrap(); } fn toggle_key(uinput: &mut UInputHandle, code: Key, value: i32) where F: AsRawFd, { emit(uinput, EventKind::Key, code as u16, value); emit( uinput, EventKind::Synchronize, SynchronizeKind::Report as u16, 0, ); } fn main() { let mut drm = DrmBackend::open_card().unwrap(); let (height, width) = drm.mode().size(); let _ = panic::catch_unwind(AssertUnwindSafe(|| real_main(&mut drm))); let crash_bitmap = include_bytes!("crash_bitmap.raw"); let mut map = drm.map().unwrap(); let data = map.as_mut(); let mut wptr = 0; for byte in crash_bitmap { for i in 0..8 { let bit = ((byte >> i) & 0x1) == 0; let color = if bit { 0xFF } else { 0x0 }; data[wptr] = color; data[wptr + 1] = color; data[wptr + 2] = color; data[wptr + 3] = color; wptr += 4; } } drop(map); drm.dirty(&[ClipRect::new(0, 0, height, width)]).unwrap(); let mut sigset = SigSet::empty(); sigset.add(Signal::SIGTERM); sigset.wait().unwrap(); } fn real_main(drm: &mut DrmBackend) { let (height, width) = drm.mode().size(); let (db_width, db_height) = drm.fb_info().unwrap().size(); let mut uinput = UInputHandle::new(OpenOptions::new().write(true).open("/dev/uinput").unwrap()); let mut backlight = BacklightManager::new(); let mut last_redraw_minute = Local::now().minute(); let mut cfg_mgr = ConfigManager::new(); let (mut cfg, mut layers) = cfg_mgr.load_config(width); let mut pixel_shift = PixelShiftManager::new(); // drop privileges to input and video group let groups = ["input", "video"]; PrivDrop::default() .user("nobody") .group_list(&groups) .apply() .unwrap_or_else(|e| panic!("Failed to drop privileges: {}", e)); let mut surface = ImageSurface::create(Format::ARgb32, db_width as i32, db_height as i32).unwrap(); let mut active_layer = 0; let mut needs_complete_redraw = true; let mut input_tb = Libinput::new_with_udev(Interface); let mut input_main = Libinput::new_with_udev(Interface); input_tb.udev_assign_seat("seat-touchbar").unwrap(); input_main.udev_assign_seat("seat0").unwrap(); let epoll = Epoll::new(EpollCreateFlags::empty()).unwrap(); epoll .add(input_main.as_fd(), EpollEvent::new(EpollFlags::EPOLLIN, 0)) .unwrap(); epoll .add(input_tb.as_fd(), EpollEvent::new(EpollFlags::EPOLLIN, 1)) .unwrap(); epoll .add(cfg_mgr.fd(), EpollEvent::new(EpollFlags::EPOLLIN, 2)) .unwrap(); uinput.set_evbit(EventKind::Key).unwrap(); for layer in &layers { for button in &layer.buttons { uinput.set_keybit(button.1.action).unwrap(); } } let mut dev_name_c = [0 as c_char; 80]; let dev_name = "Dynamic Function Row Virtual Input Device".as_bytes(); for i in 0..dev_name.len() { dev_name_c[i] = dev_name[i] as c_char; } uinput .dev_setup(&uinput_setup { id: input_id { bustype: 0x19, vendor: 0x1209, product: 0x316E, version: 1, }, ff_effects_max: 0, name: dev_name_c, }) .unwrap(); uinput.dev_create().unwrap(); let mut digitizer: Option = None; let mut touches = HashMap::new(); loop { if cfg_mgr.update_config(&mut cfg, &mut layers, width) { active_layer = 0; needs_complete_redraw = true; } let now = Local::now(); let ms_left = ((60 - now.second()) * 1000) as i32; let mut next_timeout_ms = min(ms_left, TIMEOUT_MS); if cfg.enable_pixel_shift { let (pixel_shift_needs_redraw, pixel_shift_next_timeout_ms) = pixel_shift.update(); if pixel_shift_needs_redraw { needs_complete_redraw = true; } next_timeout_ms = min(next_timeout_ms, pixel_shift_next_timeout_ms); } let current_minute = now.minute(); for button in &mut layers[active_layer].buttons { if matches!(button.1.image, ButtonImage::Time(_, _)) && (current_minute != last_redraw_minute) { needs_complete_redraw = true; last_redraw_minute = current_minute; } } if needs_complete_redraw || layers[active_layer].buttons.iter().any(|b| b.1.changed) { let shift = if cfg.enable_pixel_shift { pixel_shift.get() } else { (0.0, 0.0) }; let clips = layers[active_layer].draw( &cfg, width as i32, height as i32, &surface, shift, needs_complete_redraw, ); let data = surface.data().unwrap(); drm.map().unwrap().as_mut()[..data.len()].copy_from_slice(&data); drm.dirty(&clips).unwrap(); needs_complete_redraw = false; } match epoll.wait( &mut [EpollEvent::new(EpollFlags::EPOLLIN, 0)], next_timeout_ms as u16, ) { Err(Errno::EINTR) | Ok(_) => 0, e => e.unwrap(), }; input_tb.dispatch().unwrap(); input_main.dispatch().unwrap(); for event in &mut input_tb.clone().chain(input_main.clone()) { backlight.process_event(&event); match event { Event::Device(DeviceEvent::Added(evt)) => { let dev = evt.device(); if dev.name().contains(" Touch Bar") { digitizer = Some(dev); } } Event::Keyboard(KeyboardEvent::Key(key)) => { if key.key() == Key::Fn as u32 { let new_layer = match key.key_state() { KeyState::Pressed => 1, KeyState::Released => 0, }; if active_layer != new_layer { active_layer = new_layer; needs_complete_redraw = true; } } } Event::Touch(te) => { if Some(te.device()) != digitizer || backlight.current_bl() == 0 { continue; } match te { TouchEvent::Down(dn) => { let x = dn.x_transformed(width as u32); let y = dn.y_transformed(height as u32); if let Some(btn) = layers[active_layer].hit(width, height, x, y, None) { touches.insert(dn.seat_slot(), (active_layer, btn)); layers[active_layer].buttons[btn] .1 .set_active(&mut uinput, true); } } TouchEvent::Motion(mtn) => { if !touches.contains_key(&mtn.seat_slot()) { continue; } let x = mtn.x_transformed(width as u32); let y = mtn.y_transformed(height as u32); let (layer, btn) = *touches.get(&mtn.seat_slot()).unwrap(); let hit = layers[active_layer] .hit(width, height, x, y, Some(btn)) .is_some(); layers[layer].buttons[btn].1.set_active(&mut uinput, hit); } TouchEvent::Up(up) => { if !touches.contains_key(&up.seat_slot()) { continue; } let (layer, btn) = *touches.get(&up.seat_slot()).unwrap(); layers[layer].buttons[btn].1.set_active(&mut uinput, false); } _ => {} } } _ => {} } } backlight.update_backlight(&cfg); } } tiny-dfr-0.3.5/src/pixel_shift.rs000064400000000000000000000103301046102023000150350ustar 00000000000000use crate::TIMEOUT_MS; use rand::Rng; use std::time::Instant; const INTERVAL_MS: i32 = TIMEOUT_MS; // should be a multiple of TIMEOUT_MS const PROLONGED_INTERVAL_MS: i32 = TIMEOUT_MS * 5; // should be a multiple of TIMEOUT_MS and more than INTERVAL_MS const ANIMATION_INTERVAL_MS: i32 = 200; // should be less than TIMEOUT_MS const ANIMATION_DURATION_MS: i32 = 4000; // should be a multiple of ANIMATION_INTERVAL_MS // This is the total range on the x-axis that pixels will shift by over time, ie. they will shift by // PIXEL_SHIFT_WIDTH_PX / 2 to the right and to the left. // To make sure that no pixel ends up being always on, the minimum value to be safe here is the // size of the largest continuous colored line in the x-direction. The higher this value, the less // strain is put on the panel. pub const PIXEL_SHIFT_WIDTH_PX: u64 = 22; // should be divisible by 2 // in y direction we can't really shift by a lot since icons still need to appear centered, // 2 pixels in each direction seems to be the maximum before it gets really visible. const PIXEL_SHIFT_HEIGHT_PX: u64 = 4; // should be divisible by 2 #[derive(Clone, Copy)] enum ShiftState { WaitingAtEnd, ShiftingSubpixel, Normal, } pub struct PixelShiftManager { last_active: Instant, y_constant: f64, pixel_progress: u64, subpixel_progress: f64, direction: i64, state: ShiftState, } fn wait_for_state(state: ShiftState) -> i32 { match state { ShiftState::ShiftingSubpixel => ANIMATION_INTERVAL_MS, ShiftState::Normal => INTERVAL_MS, ShiftState::WaitingAtEnd => PROLONGED_INTERVAL_MS, } } impl PixelShiftManager { pub fn new() -> PixelShiftManager { let pixel_progress = rand::thread_rng().gen_range(0..PIXEL_SHIFT_WIDTH_PX); // add some randomness to the relationship between shifting on the x and y axis // so that pixel shifting doesn't follow the same 2d pattern every time let y_constant: f64 = rand::thread_rng().gen_range(0..PIXEL_SHIFT_HEIGHT_PX * 2) as f64; PixelShiftManager { last_active: Instant::now(), y_constant, state: ShiftState::Normal, pixel_progress, subpixel_progress: 0.0, direction: 1, } } pub fn update(&mut self) -> (bool, i32) { let time_now = Instant::now(); let since_last_pixel_shift = (time_now - self.last_active).as_millis() as i32; if since_last_pixel_shift < wait_for_state(self.state) { return (false, i32::MAX); } self.last_active = time_now; match self.state { ShiftState::Normal => { self.state = ShiftState::ShiftingSubpixel; } ShiftState::ShiftingSubpixel => { let shift_by = ANIMATION_INTERVAL_MS as f64 / ANIMATION_DURATION_MS as f64; self.subpixel_progress += shift_by * self.direction as f64; if self.subpixel_progress <= -0.99 || self.subpixel_progress >= 0.99 { self.pixel_progress = (self.direction + self.pixel_progress as i64) as u64; self.state = ShiftState::Normal; self.subpixel_progress = 0.0; if self.pixel_progress == 0 || self.pixel_progress >= PIXEL_SHIFT_WIDTH_PX { self.state = ShiftState::WaitingAtEnd; self.direction = -self.direction; } } } ShiftState::WaitingAtEnd => { self.state = ShiftState::Normal; self.subpixel_progress = 0.0; } } (true, wait_for_state(self.state)) } pub fn get(&self) -> (f64, f64) { let x_progress = self.pixel_progress as f64 + self.subpixel_progress; let mut y_progress = (x_progress + self.y_constant) % (PIXEL_SHIFT_HEIGHT_PX * 2) as f64; if y_progress > PIXEL_SHIFT_HEIGHT_PX as f64 { y_progress = (PIXEL_SHIFT_HEIGHT_PX * 2) as f64 - y_progress; } ( x_progress - (PIXEL_SHIFT_WIDTH_PX / 2) as f64, y_progress - (PIXEL_SHIFT_HEIGHT_PX / 2) as f64, ) } }