Initial commit (very late, most of this was done in June 2023)

This commit is contained in:
Olivier 'reivilibre 2024-05-21 17:11:14 +01:00
commit d89de7edb9
14 changed files with 1863 additions and 0 deletions

2
.envrc Normal file
View File

@ -0,0 +1,2 @@
use flake .

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.direnv

844
Cargo.lock generated Normal file
View File

@ -0,0 +1,844 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bit_field"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "bumpalo"
version = "3.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
name = "bytemuck"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "color_quant"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "crunchy"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "exr"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1e481eb11a482815d3e9d618db8c42a93207134662873809335a92327440c18"
dependencies = [
"bit_field",
"flume",
"half",
"lebe",
"miniz_oxide",
"rayon-core",
"smallvec",
"zune-inflate",
]
[[package]]
name = "eyre"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "fdeflate"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
dependencies = [
"simd-adler32",
]
[[package]]
name = "flate2"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "flume"
version = "0.10.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577"
dependencies = [
"futures-core",
"futures-sink",
"nanorand",
"pin-project",
"spin",
]
[[package]]
name = "futures-core"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-sink"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e"
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
name = "gif"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
dependencies = [
"color_quant",
"weezl",
]
[[package]]
name = "half"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
dependencies = [
"crunchy",
]
[[package]]
name = "hermit-abi"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "image"
version = "0.24.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"exr",
"gif",
"jpeg-decoder",
"num-rational",
"num-traits",
"png",
"qoi",
"tiff",
]
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "jpeg-decoder"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
dependencies = [
"rayon",
]
[[package]]
name = "js-sys"
version = "0.3.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "labelo_p710bt"
version = "0.1.0"
dependencies = [
"bitflags 2.3.3",
"eyre",
"image",
"num-derive",
"num-traits",
"rusb",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lebe"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "libc"
version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libusb1-sys"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9d0e2afce4245f2c9a418511e5af8718bcaf2fa408aefb259504d1a9cb25f27"
dependencies = [
"cc",
"libc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "matchers"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
dependencies = [
"regex-automata",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
"simd-adler32",
]
[[package]]
name = "nanorand"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
dependencies = [
"getrandom",
]
[[package]]
name = "nu-ansi-term"
version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
dependencies = [
"overload",
"winapi",
]
[[package]]
name = "num-derive"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "once_cell"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
[[package]]
name = "overload"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "pin-project"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.22",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pkg-config"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "png"
version = "0.17.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide",
]
[[package]]
name = "proc-macro2"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb"
dependencies = [
"unicode-ident",
]
[[package]]
name = "qoi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
dependencies = [
"bytemuck",
]
[[package]]
name = "quote"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "regex"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
dependencies = [
"regex-syntax 0.7.2",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
dependencies = [
"regex-syntax 0.6.29",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
[[package]]
name = "rusb"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44a8c36914f9b1a3be712c1dfa48c9b397131f9a75707e570a391735f785c5d1"
dependencies = [
"libc",
"libusb1-sys",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sharded-slab"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
dependencies = [
"lazy_static",
]
[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "spin"
version = "0.9.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
dependencies = [
"lock_api",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2efbeae7acf4eabd6bcdcbd11c92f45231ddda7539edc7806bd1a04a03b24616"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thread_local"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "tiff"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
dependencies = [
"flate2",
"jpeg-decoder",
"weezl",
]
[[package]]
name = "tracing"
version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
dependencies = [
"cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.22",
]
[[package]]
name = "tracing-core"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-log"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
dependencies = [
"lazy_static",
"log",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
"regex",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "valuable"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[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.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.22",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.22",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.87"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
[[package]]
name = "weezl"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "zune-inflate"
version = "0.2.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
dependencies = [
"simd-adler32",
]

4
Cargo.toml Normal file
View File

@ -0,0 +1,4 @@
[workspace]
members = [
"p710bt"
]

131
flake.lock Normal file
View File

@ -0,0 +1,131 @@
{
"nodes": {
"fenix": {
"inputs": {
"nixpkgs": [
"nixpkgs"
],
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1687933502,
"narHash": "sha256-6AjeSY3ld3keACkmOB5x8+yglyhT3dk0EvfV1wnHKQw=",
"owner": "nix-community",
"repo": "fenix",
"rev": "303533333b5c215686f1230d0672646166fca055",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "fenix",
"type": "github"
}
},
"naersk": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1687852486,
"narHash": "sha256-2rXkhKUVQxbVaC+TITPpILiy/dSbordOLs87eoWHYxA=",
"owner": "nix-community",
"repo": "naersk",
"rev": "df10963b956962913b693a638746a95d6c506404",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "naersk",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1685865905,
"narHash": "sha256-XJZ/o17eOd2sEsGif+/MQBnfa2DKmndWgJyc7CWajFc=",
"path": "/nix/store/vcj90ksw5vk6ia960b73vpncxa9pw826-source",
"rev": "e7603eba51f2c7820c0a182c6bbb351181caa8e7",
"type": "path"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1687829761,
"narHash": "sha256-QRe1Y8SS3M4GeC58F/6ajz6V0ZLUVWX3ZAMgov2N3/g=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "9790f3242da2152d5aa1976e3e4b8b414f4dd206",
"type": "github"
},
"original": {
"id": "nixpkgs",
"ref": "nixos-23.05",
"type": "indirect"
}
},
"root": {
"inputs": {
"fenix": "fenix",
"naersk": "naersk",
"nixpkgs": "nixpkgs_2",
"utils": "utils"
}
},
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1687885966,
"narHash": "sha256-pzi4sfi+cy8gcS4TmL8gnadxvgPj7+j+Z+5XfIwhWpA=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "7c8ae35648099189e550c873c0dae3a4ca6c50a9",
"type": "github"
},
"original": {
"owner": "rust-lang",
"ref": "nightly",
"repo": "rust-analyzer",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1687709756,
"narHash": "sha256-Y5wKlQSkgEK2weWdOu4J3riRd+kV/VCgHsqLNTTWQ/0=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "dbabf0ca0c0c4bce6ea5eaf65af5cb694d2082c7",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

105
flake.nix Normal file
View File

@ -0,0 +1,105 @@
{
description = "Labelo";
inputs = {
utils.url = "github:numtide/flake-utils";
naersk.url = "github:nix-community/naersk";
# Current Rust in nixpkgs is too old unfortunately — let's use the Fenix overlay's packages...
fenix = {
url = "github:nix-community/fenix";
inputs.nixpkgs.follows = "nixpkgs";
};
nixpkgs.url = "nixpkgs/nixos-23.05";
};
outputs = { self, nixpkgs, utils, naersk, fenix }:
utils.lib.eachDefaultSystem (system: let
pkgs = nixpkgs.legacyPackages."${system}";
#fenixRustToolchain = fenix.packages."${system}".minimal.toolchain
# fenixRustToolchain =
# fenix."${system}".complete.withComponents [
# "cargo"
# "clippy"
# "rust-src"
# "rustc"
# "rustfmt"
# ];
# fenixRustToolchain = fenix.packages."${system}".stable.toolchain;
fenixRustToolchain =
fenix.packages."${system}".stable.withComponents [
"cargo"
"clippy"
"rust-src"
"rustc"
"rustfmt"
"rust-analyzer"
];
# rust-toolchain = pkgs.symlinkJoin {
# name = "rust-toolchain";
# paths = [fenixRustToolchain.rustc fenixRustToolchain.cargo fenixRustToolchain.clippy fenixRustToolchain.rustfmt fenixRustToolchain.rustPlatform.rustcSrc];
# };
naersk-lib = naersk.lib."${system}";
labelo = naersk-lib.buildPackage {
pname = "labelo";
root = ./.;
buildInputs = with pkgs; [
openssl
pkgconfig
sqlite
];
};
in rec {
# `nix build`
packages.labelo = labelo;
defaultPackage = packages.labelo;
# NixOS Modules
# nixosModules = {
# yama = import ./nixos_modules/yama.nix self;
# };
# `nix run`
apps.labelo = utils.lib.mkApp {
drv = labelo;
};
defaultApp = apps.labelo;
# `nix develop`
devShell = pkgs.mkShell {
buildInputs = [
fenixRustToolchain
#rust-toolchain
pkgs.pkg-config
pkgs.libusb
];
nativeBuildInputs = [
];
# Don't know if this var does anything by itself, but you need to feed this value in to IntelliJ IDEA and it's probably easier to pull out of an env var than look it up each time.
RUST_SRC_PATH = "${fenixRustToolchain}/lib/rustlib/src/rust/library";
# Cargo culted:
# Add to rustc search path
RUSTFLAGS = (builtins.map (a: ''-L ${a}/lib'') [
]);
# Add to bindgen search path
BINDGEN_EXTRA_CLANG_ARGS =
# Includes with normal include path
(builtins.map (a: ''-I"${a}/include"'') [
pkgs.glibc.dev
])
# Includes with special directory paths
++ [
''-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
];
#nativeBuildInputs = with pkgs; [ rustc cargo ];
};
});
}

2
p710bt/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
/Cargo.lock

21
p710bt/Cargo.toml Normal file
View File

@ -0,0 +1,21 @@
[package]
name = "labelo_p710bt"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rusb = { version = "0.9.2", optional = true }
bitflags = "2.3.3"
eyre = "0.6.8"
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
num-traits = "0.2.15"
num-derive = "0.3.3"
image = { version = "0.24.6", features = ["png"] }
[features]
usb = ["rusb"]
default = ["usb"]

94
p710bt/examples/p710bt.rs Normal file
View File

@ -0,0 +1,94 @@
use eyre::{bail, Context};
use labelo_p710bt::commander::P710btCommander;
use labelo_p710bt::protocol::{AdvancedModeSettings, VariousModeSettings};
use labelo_p710bt::usb::UsbP710bt;
use tracing::info;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
pub fn main() -> eyre::Result<()> {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "p710bt=debug,info".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let mut devices = UsbP710bt::list_devices()?;
if devices.len() != 1 {
bail!("Found {} P710BT devices, aborting.", devices.len());
}
let device = UsbP710bt::open(devices.remove(0))?;
let mut printer = P710btCommander::new(device)?;
printer.invalidate_and_initialise()?;
let status = printer.status()?;
info!("Printer status: {status:?}");
printer.set_raster_mode_and_automatic_notification()?;
// printer.print_information(status.media_type.unwrap(), status.media_width_mm, 128, true)
// .context("PIC")?;
//
// printer.set_mode_settings(
// VariousModeSettings::AUTO_CUT,
// AdvancedModeSettings::HIGH_RES_PRINTING,
// 4
// )?;
// for i in 0u16..128u16 {
// let mut line = [0u8; 16];
// for bit in 0..16 {
// if (i >> bit) & 1 == 1 {
// line[bit as usize] = 0xFF;
// }
// }
//
// //eprintln!("ln = {line:?}");
//
// printer.raster_line(line)
// .with_context(|| format!("line {line:?}"))?;
// }
//
// printer.finish_printing(true)?;
for page in 0..3 {
printer
.print_information(
status.media_type.unwrap(),
status.media_width_mm,
512,
page == 0,
)
.context("PIC")?;
printer.set_mode_settings(
VariousModeSettings::AUTO_CUT,
AdvancedModeSettings::HIGH_RES_PRINTING
| AdvancedModeSettings::NO_BUFFER_CLEARING_WHEN_PRINTING,
4,
)?;
for i in 0u16..512u16 {
let mut line = [0u8; 16];
for bit in 0..16 {
if ((i + page * 512) >> bit) & 1 == 1 {
line[bit as usize] = 0xFF;
}
}
//eprintln!("ln = {line:?}");
printer
.raster_line(line)
.with_context(|| format!("line {line:?}"))?;
}
printer.finish_printing(page == 3)?;
}
Ok(())
}

View File

@ -0,0 +1,122 @@
use eyre::{bail, Context};
use labelo_p710bt::commander::P710btCommander;
use labelo_p710bt::protocol::{AdvancedModeSettings, VariousModeSettings, PIXELS_ON_TAPE_SIZES};
use labelo_p710bt::usb::UsbP710bt;
use std::env::args;
use std::path::Path;
use tracing::{info, warn};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
pub fn main() -> eyre::Result<()> {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "p710bt=debug,info".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let mut images = Vec::new();
for arg in args().skip(1) {
let image_path = Path::new(&arg);
let image = image::open(image_path)
.with_context(|| format!("failed to load image {image_path:?}"))?;
images.push(image.into_rgba8());
}
let mut devices = UsbP710bt::list_devices()?;
if devices.len() != 1 {
bail!("Found {} P710BT devices, aborting.", devices.len());
}
let device = UsbP710bt::open(devices.remove(0))?;
let mut printer = P710btCommander::new(device)?;
printer.invalidate_and_initialise()?;
let status = printer.status()?;
info!("Printer status: {status:?}");
let media_width_mm = status.media_width_mm as u32;
if media_width_mm == 0 {
bail!("Media width is 0; is there tape loaded?");
}
let Some(media_width_info) = PIXELS_ON_TAPE_SIZES.iter().find(|info| info.mm == media_width_mm) else {
bail!("Unrecognised tape width: this usually means support is needed in labelo ({media_width_mm} mm).");
};
info!("Media width information: {media_width_info:?}");
printer.set_raster_mode_and_automatic_notification()?;
for (page, image) in images.iter().enumerate() {
if image.height() > media_width_info.practical_px_limit {
bail!(
"Image/page {page} exceeds the practical print height limit of {} px ({} px).",
media_width_info.practical_px_limit,
image.height()
);
}
if image.height() > media_width_info.datasheet_px_limit {
warn!("Image/page {page} is {} px high which exceeds the datasheet print height of {} px but fits within the empirical print height limit of {} px.", image.height(), media_width_info.datasheet_px_limit, media_width_info.practical_px_limit);
}
}
for page in 0..images.len() {
let image = &images[page];
let lines = image.width();
printer
.print_information(
status.media_type.unwrap(),
status.media_width_mm,
lines,
page == 0,
)
.context("PIC")?;
let mut ams = AdvancedModeSettings::NO_BUFFER_CLEARING_WHEN_PRINTING;
if page == images.len() - 1 {
// TODO not always desirable, but for testing it is handy to have it cut on the last piece.
ams |= AdvancedModeSettings::NO_CHAIN_PRINTING;
}
// TODO Double-resolution printing in the x axis.
// ams |= AdvancedModeSettings::HIGH_RES_PRINTING;
printer.set_mode_settings(VariousModeSettings::AUTO_CUT, ams, 4)?;
// TODO this depends based on what tape is in use
// 128 lines (printer size)
// minus '84 dots' (1/width in manual 2.3.2)
// plus '7 dots' (5/width offset in manual 2.3.2).
// then halved......?
// let yoff = (128 - 84 + 7) >> 1;
// maybe you just center the printing?
let yoff = (128 - image.height()) >> 1;
for x in 0..lines {
let mut line = [0u8; 16];
for y in 0..(128 - yoff) {
let pix = y < image.height() && (image.get_pixel(x, y).0 == [0, 0, 0, 255]);
if pix {
let real_y = y + yoff;
line[(real_y >> 3) as usize] |= 128 >> (real_y & 0b0000_0111);
}
}
printer
.raster_line(line)
.with_context(|| format!("line {line:?}"))?;
}
printer.finish_printing(page == images.len() - 1)?;
}
Ok(())
}

155
p710bt/src/commander.rs Normal file
View File

@ -0,0 +1,155 @@
use crate::protocol::{
cmd_advanced_mode_settings, cmd_print_information_command, cmd_raster_graphics_transfer_line,
cmd_specify_margin_amount, cmd_switch_automatic_status_notification_code,
cmd_various_mode_settings, parse_status_response, AdvancedModeSettings, MediaType,
StatusResponse, StatusType, VariousModeSettings, CMD_INITIALISE, CMD_INVALIDATE, CMD_PRINT,
CMD_PRINT_WITH_FEEDING, CMD_SELECT_COMPRESSION_MODE_UNCOMPRESSED,
CMD_STATUS_INFORMATION_REQUEST, CMD_SWITCH_DYNAMIC_COMMAND_MODE_RASTER,
};
use eyre::Context;
use std::time::Duration;
use tracing::debug;
pub trait P710btDevice {
/// Transmits a frame to the device.
fn transmit(&mut self, bytes: &[u8]) -> eyre::Result<()>;
/// Receives a frame from the device if possible, but will not block to read it.
fn receive_nonblock(&mut self, max: usize) -> eyre::Result<Option<Vec<u8>>>;
/// Receives a frame from the device, blocking to wait for one if necessary.
fn receive_block(&mut self, max: usize) -> eyre::Result<Vec<u8>>;
}
pub struct P710btCommander<D: P710btDevice> {
device: D,
last_status: Option<StatusResponse>,
}
impl<D: P710btDevice> P710btCommander<D> {
pub fn new(device: D) -> eyre::Result<Self> {
Ok(Self {
device,
last_status: None,
})
}
pub fn invalidate_and_initialise(&mut self) -> eyre::Result<()> {
self.device.transmit(CMD_INVALIDATE).context("invalidate")?;
self.device.transmit(CMD_INITIALISE).context("initialise")?;
Ok(())
}
pub fn status(&mut self) -> eyre::Result<StatusResponse> {
self.device
.transmit(CMD_STATUS_INFORMATION_REQUEST)
.context("status")?;
let response = self.device.receive_block(32).context("status (R)")?;
let status_response =
parse_status_response(&response).context("failed to parse status response")?;
self.last_status = Some(status_response.clone());
debug!("polled status: {status_response:?}");
Ok(status_response)
}
pub fn print_information(
&mut self,
media_type: MediaType,
media_width_mm: u8,
raster_number_lines: u32,
first_page: bool,
) -> eyre::Result<()> {
self.device
.transmit(&cmd_print_information_command(
media_type,
media_width_mm,
raster_number_lines,
first_page,
))
.context("PIC")?;
Ok(())
}
pub fn set_raster_mode_and_automatic_notification(&mut self) -> eyre::Result<()> {
self.device
.transmit(CMD_SWITCH_DYNAMIC_COMMAND_MODE_RASTER)
.context("set raster mode")?;
self.device
.transmit(&cmd_switch_automatic_status_notification_code(true))
.context("set notify mode")?;
self.device
.transmit(CMD_SELECT_COMPRESSION_MODE_UNCOMPRESSED)
.context("set uncompressed")?;
Ok(())
}
pub fn set_mode_settings(
&mut self,
vms: VariousModeSettings,
ams: AdvancedModeSettings,
margin_dots: u16,
) -> eyre::Result<()> {
self.device
.transmit(&cmd_various_mode_settings(vms))
.context("various mode settings")?;
self.device
.transmit(&cmd_advanced_mode_settings(ams))
.context("advanced mode settings")?;
self.device
.transmit(&cmd_specify_margin_amount(margin_dots))
.context("margins")?;
Ok(())
}
pub fn raster_line(&mut self, line: [u8; 16]) -> eyre::Result<Option<StatusResponse>> {
self.device
.transmit(&cmd_raster_graphics_transfer_line(line))
.context("raster line")?;
if let Some(status_notification) = self
.device
.receive_nonblock(32)
.context("raster line read (opt)")?
{
let status_response = parse_status_response(&status_notification)
.context("failed to parse notification")?;
debug!("status notification (L): {status_response:?}");
self.last_status = Some(status_response.clone());
return Ok(Some(status_response));
}
Ok(None)
}
pub fn finish_printing(&mut self, last_page: bool) -> eyre::Result<()> {
let tx = if last_page {
CMD_PRINT_WITH_FEEDING
} else {
CMD_PRINT
};
self.device.transmit(tx).context("finish page")?;
loop {
if let Some(ref status) = self.last_status {
if status.status_type == Some(StatusType::PhaseChange) && status.reception_possible
{
break;
}
}
std::thread::sleep(Duration::from_millis(50));
if let Some(status_notification) = self
.device
.receive_nonblock(32)
.context("wait for finish")?
{
let status_response = parse_status_response(&status_notification)
.context("failed to parse notification")?;
debug!("status notification (F): {status_response:?}");
self.last_status = Some(status_response.clone());
}
}
Ok(())
}
}

5
p710bt/src/lib.rs Normal file
View File

@ -0,0 +1,5 @@
pub mod commander;
pub mod protocol;
#[cfg(feature = "usb")]
pub mod usb;

232
p710bt/src/protocol.rs Normal file
View File

@ -0,0 +1,232 @@
use bitflags::bitflags;
use eyre::bail;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
//// Invalidate
pub const CMD_INVALIDATE: &[u8] = &[0x00; 100];
//// Initialise
pub const CMD_INITIALISE: &[u8] = &[0x1B, 0x40];
//// Status information request
pub const CMD_STATUS_INFORMATION_REQUEST: &[u8] = &[0x1B, 0x69, 0x53];
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ErrorInformation: u16 {
const NO_MEDIA = 0x0001;
const CUTTER_JAM = 0x0004;
const WEAK_BATTERIES = 0x0008;
const HIGH_VOLTAGE_ADAPTER = 0x0040;
const REPLACE_MEDIA = 0x0100;
const COVER_OPEN = 0x1000;
const OVERHEATING = 0x2000;
}
}
#[derive(FromPrimitive, Copy, Clone, Debug)]
pub enum MediaType {
LaminatedTape = 0x01,
NonLaminatedTape = 0x03,
HeatShrinkTube2To1 = 0x11,
HeatShrinkTube3To1 = 0x17,
Incompatible = 0xFF,
}
#[derive(FromPrimitive, Copy, Clone, Debug, Eq, PartialEq)]
pub enum StatusType {
ReplyToStatusRequest = 0x00,
PrintingCompleted = 0x01,
ErrorOccurred = 0x02,
ExitIfModeUnused = 0x03,
TurnedOff = 0x04,
Notification = 0x05,
PhaseChange = 0x06,
}
#[derive(FromPrimitive, Copy, Clone, Debug)]
pub enum TapeColour {
White = 0x01,
Other = 0x02,
// TODO
}
#[derive(FromPrimitive, Copy, Clone, Debug)]
pub enum TextColour {
White = 0x01,
Red = 0x04,
Blue = 0x05,
Black = 0x08,
// TODO
}
#[derive(Copy, Clone, Debug)]
pub struct TapeSizeInfo {
/// 'Width' of the tape in millimetres
pub mm: u32,
/// Practical print height limit on the tape.
/// Based on empirical measurements.
pub practical_px_limit: u32,
/// Specified print height in dots from the protocol datasheet.
pub datasheet_px_limit: u32,
}
/// Tape size (mm) to pixels high that can be printed in practice, then the number that can be printed according to the datasheet
pub const PIXELS_ON_TAPE_SIZES: &[TapeSizeInfo] = &[
TapeSizeInfo {
mm: 12,
// Actually 81 but it seems like an awkward number!
practical_px_limit: 80,
// 70 dots is way too conservative?
datasheet_px_limit: 70,
},
TapeSizeInfo {
mm: 24,
// the printer can only print 128 dots; can't push our luck
practical_px_limit: 128,
datasheet_px_limit: 128,
},
];
#[derive(Clone, Debug)]
pub struct StatusResponse {
pub error_information: ErrorInformation,
/// Media width in mm. 0 = no media. 4 = 3.5 mm.
pub media_width_mm: u8,
pub media_type: Option<MediaType>,
pub status_type: Option<StatusType>,
pub tape_colour: Option<TapeColour>,
pub text_colour: Option<TextColour>,
pub reception_possible: bool,
}
pub fn parse_status_response(input: &[u8]) -> eyre::Result<StatusResponse> {
if input.len() != 32 {
bail!("status response should be 32 bytes");
}
let error_information =
ErrorInformation::from_bits_truncate(((input[9] as u16) << 8) | (input[8] as u16));
let media_type = FromPrimitive::from_u8(input[11]);
let status_type = FromPrimitive::from_u8(input[18]);
let tape_colour = FromPrimitive::from_u8(input[24]);
let text_colour = FromPrimitive::from_u8(input[25]);
let reception_possible = input[19] == 0;
Ok(StatusResponse {
error_information,
media_width_mm: input[10],
media_type,
status_type,
tape_colour,
text_colour,
reception_possible,
})
}
// Switch dynamic command mode
// Don't bother implementing the other modes: as the manual says, 'be sure to switch to this mode'.
pub const CMD_SWITCH_DYNAMIC_COMMAND_MODE_RASTER: &[u8] = &[0x1B, 0x69, 0x61, 0x01];
//// Switch automatic status notification code
pub fn cmd_switch_automatic_status_notification_code(notify: bool) -> [u8; 4] {
[0x1B, 0x69, 0x21, if notify { 0x00 } else { 0x01 }]
}
//// Print information command
/// Generate the 'print information command'.
/// `raster_number` is poorly documented but seems to be the number of raster commands to be issued
/// (?) or the number of lines. I'm not entirely sure which.
pub fn cmd_print_information_command(
media_type: MediaType,
media_width_mm: u8,
raster_number: u32,
first_page: bool,
) -> [u8; 13] {
let mut out = [
0x1B,
0x69,
0x7A,
0x02 | 0x04 | 0x80,
media_type as u8,
media_width_mm,
0,
0,
0,
0,
0,
if first_page { 0 } else { 1 },
0,
];
out[7..11].copy_from_slice(&raster_number.to_le_bytes());
out
}
//// Various mode settings
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct VariousModeSettings: u8 {
const AUTO_CUT = 0x40;
const MIRROR_PRINTING = 0x80;
}
}
pub fn cmd_various_mode_settings(n1: VariousModeSettings) -> [u8; 4] {
[0x1B, 0x69, 0x4D, n1.bits()]
}
//// Specify the page number (NOT FOR P710BT)
// Not implemented: not for P710BT.
//// Advanced mode settings
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AdvancedModeSettings: u8 {
// 0x01, 0x02 unused
// 0x04: half cut (not supported by P710BT)
const NO_CHAIN_PRINTING = 0x08;
const SPECIAL_TAPE_NO_CUTTING = 0x10;
// 0x20 unused.
const HIGH_RES_PRINTING = 0x40;
const NO_BUFFER_CLEARING_WHEN_PRINTING = 0x80;
}
}
pub fn cmd_advanced_mode_settings(n1: AdvancedModeSettings) -> [u8; 4] {
[0x1B, 0x69, 0x4B, n1.bits()]
}
//// Specify margin amount (feed amount)
pub fn cmd_specify_margin_amount(num_dots: u16) -> [u8; 5] {
let num_dots_le = num_dots.to_le_bytes();
[0x1B, 0x69, 0x64, num_dots_le[0], num_dots_le[1]]
}
//// Select compression mode
// Don't bother with the other modes for now.
pub const CMD_SELECT_COMPRESSION_MODE_UNCOMPRESSED: &[u8] = &[0x4D, 0x00];
//// Raster graphics transfer
pub fn cmd_raster_graphics_transfer_line(line: [u8; 16]) -> [u8; 19] {
let mut out = [0; 19];
// manual suggests n=16 hardcoded when using uncompressed mode
out[0..3].copy_from_slice(&[0x47, 16, 0x00]);
out[3..].copy_from_slice(&line);
out
}
//// Zero raster graphics
// Not bothering for now.
//// Print command
pub const CMD_PRINT: &[u8] = &[0x0C];
//// Print command with feeding
pub const CMD_PRINT_WITH_FEEDING: &[u8] = &[0x1A];

144
p710bt/src/usb.rs Normal file
View File

@ -0,0 +1,144 @@
use crate::commander::P710btDevice;
use eyre::{bail, Context};
use rusb::{Device, DeviceHandle, DeviceList, GlobalContext};
use std::time::Duration;
use tracing::{debug, info, warn};
pub const VENDOR_BROTHER: u16 = 0x04F9;
pub const PRODUCT_P710BT: u16 = 0x20AF;
pub struct UsbP710bt {
pub device_handle: DeviceHandle<GlobalContext>,
}
impl UsbP710bt {
pub fn list_devices() -> eyre::Result<Vec<Device<GlobalContext>>> {
let mut result = Vec::new();
let device_list = DeviceList::new()?;
for device in device_list.iter() {
match device.device_descriptor() {
Ok(descriptor) => {
debug!(
"found USB device {:04x}:{:04x}",
descriptor.vendor_id(),
descriptor.product_id()
);
if descriptor.product_id() == PRODUCT_P710BT
&& descriptor.vendor_id() == VENDOR_BROTHER
{
info!("found P710BT");
result.push(device);
}
}
Err(err) => {
warn!(
"Ignoring USB device {device:?} as can't read device descriptor: {err:?}."
);
}
}
}
Ok(result)
}
pub fn open(device: Device<GlobalContext>) -> eyre::Result<Self> {
let mut device_handle = device.open().context("failed to open device")?;
if device_handle
.kernel_driver_active(0)
.context("check kernel driver active")?
{
device_handle
.detach_kernel_driver(0)
.context("detach kernel driver")?;
}
device_handle
.claim_interface(0)
.context("claim usb interface")?;
Ok(UsbP710bt { device_handle })
}
}
// n.b. the top bit is set because it's an IN endpoint. This seems to be a USB standard.
// The manual calls this endpoint 1.
const PRINTER_TO_HOST_ENDPOINT: u8 = 0x81;
const HOST_TO_PRINTER_ENDPOINT: u8 = 0x02;
impl P710btDevice for UsbP710bt {
fn transmit(&mut self, bytes: &[u8]) -> eyre::Result<()> {
let written = self
.device_handle
.write_bulk(HOST_TO_PRINTER_ENDPOINT, bytes, Duration::from_secs(0))
.context("usb write")?;
if written < bytes.len() {
bail!(
"failed to write whole buffer ({} written of {}) to usb",
written,
bytes.len()
);
}
Ok(())
}
fn receive_nonblock(&mut self, max: usize) -> eyre::Result<Option<Vec<u8>>> {
let mut buf = vec![0u8; max];
let mut read_tot = 0;
while read_tot < max {
match self.device_handle.read_bulk(
PRINTER_TO_HOST_ENDPOINT,
&mut buf[read_tot..],
Duration::from_millis(if read_tot == 0 { 1 } else { 0 }),
) {
Ok(just_read) => {
if just_read == 0 {
if read_tot > 0 {
bail!("partial usb read; have {} of {} bytes read", read_tot, max);
} else {
return Ok(None);
}
}
read_tot += just_read;
}
Err(rusb::Error::Timeout) => {
if read_tot > 0 {
bail!(
"UNREACHABLE TIMEOUT after partial usb read; have {} of {} bytes read",
read_tot,
max
);
} else {
return Ok(None);
}
}
Err(other) => {
bail!("usb read: {other:?}");
}
}
}
Ok(Some(buf))
}
fn receive_block(&mut self, max: usize) -> eyre::Result<Vec<u8>> {
let mut buf = vec![0u8; max];
let mut read_tot = 0;
while read_tot < max {
let just_read = self
.device_handle
.read_bulk(
PRINTER_TO_HOST_ENDPOINT,
&mut buf[read_tot..],
Duration::from_millis(0),
)
.context("usb read")?;
if just_read == 0 {
bail!("empty usb read; have {} of {} bytes read", read_tot, max);
}
read_tot += just_read;
}
Ok(buf)
}
}