drm-0.11.1/.cargo_vcs_info.json0000644000000001360000000000100116760ustar { "git": { "sha1": "328742fddc675b3370057b382eb54acbc9b48c79" }, "path_in_vcs": "" }drm-0.11.1/Cargo.lock0000644000000474770000000000100076740ustar # 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 = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bindgen" version = "0.69.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ffcebc3849946a7170a05992aac39da343a90676ab392c51a4280981d6379c2" dependencies = [ "bitflags 2.4.1", "cexpr", "clang-sys", "lazy_static", "lazycell", "log", "peeking_take_while", "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", "which", ] [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[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 = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "clipboard-win" version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c57002a5d9be777c1ef967e33674dac9ebd310d8893e4e3437b14d5f0f6372cc" dependencies = [ "error-code", ] [[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 = "drm" version = "0.11.1" dependencies = [ "bitflags 2.4.1", "bytemuck", "drm-ffi", "drm-fourcc", "image", "rustix", "rustyline", ] [[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 = [ "bindgen", "libc", "linux-raw-sys 0.6.3", "pkg-config", ] [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "endian-type" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" [[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 = "error-code" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "281e452d3bad4005426416cdba5ccfd4f5c1280e10099e21db27f7c1c28347fc" [[package]] name = "fd-lock" version = "4.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" dependencies = [ "cfg-if", "rustix", "windows-sys 0.52.0", ] [[package]] name = "fdeflate" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209098dd6dfc4445aa6111f0e98653ac323eaa4dfd212c9ca3931bf9955c31bd" dependencies = [ "simd-adler32", ] [[package]] name = "flate2" version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" dependencies = [ "crc32fast", "miniz_oxide", ] [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "home" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ "windows-sys 0.52.0", ] [[package]] name = "image" version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711" dependencies = [ "bytemuck", "byteorder", "color_quant", "num-rational", "num-traits", "png", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" [[package]] name = "libloading" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161" dependencies = [ "cfg-if", "windows-sys 0.48.0", ] [[package]] name = "linux-raw-sys" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "linux-raw-sys" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ab96045f1fabcc9fe043d9cb6900c5e1cba5c13f6aaa3d2295b496661924464" [[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.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[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 = "nibble_vec" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" dependencies = [ "smallvec", ] [[package]] name = "nix" version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ "bitflags 2.4.1", "cfg-if", "libc", ] [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[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.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "peeking_take_while" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "pkg-config" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "png" version = "0.17.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd75bf2d8dd3702b9707cdbc56a5b9ef42cec752eb8b3bafc01234558442aa64" dependencies = [ "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", "miniz_oxide", ] [[package]] name = "prettyplease" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "radix_trie" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" dependencies = [ "endian-type", "nibble_vec", ] [[package]] name = "regex" version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys 0.4.12", "windows-sys 0.52.0", ] [[package]] name = "rustyline" version = "13.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02a2d683a4ac90aeef5b1013933f6d977bd37d51ff3f4dad829d4931a7e6be86" dependencies = [ "bitflags 2.4.1", "cfg-if", "clipboard-win", "fd-lock", "home", "libc", "log", "memchr", "nix", "radix_trie", "unicode-segmentation", "unicode-width", "utf8parse", "winapi", ] [[package]] name = "shlex" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7cee0529a6d40f580e7a5e6c495c8fbfe21b7b52795ed4bb5e62cdf92bc6380" [[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.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "syn" version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "utf8parse" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "which" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", "home", "once_cell", "rustix", ] [[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 = "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" drm-0.11.1/Cargo.toml0000644000000026400000000000100076760ustar # 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" rust-version = "1.65" name = "drm" version = "0.11.1" authors = [ "Tyler Slabinski ", "Victoria Brekenfeld ", ] exclude = [ ".gitignore", ".github", ] description = "Safe, low-level bindings to the Direct Rendering Manager API" readme = "README.md" license = "MIT" repository = "https://github.com/Smithay/drm-rs" [dependencies.bitflags] version = "2" [dependencies.bytemuck] version = "1.12" features = [ "extern_crate_alloc", "derive", ] [dependencies.drm-ffi] version = "0.7.1" [dependencies.drm-fourcc] version = "^2.2.0" [dependencies.rustix] version = "0.38.22" features = [ "mm", "fs", ] [dev-dependencies.image] version = "0.24" features = ["png"] default-features = false [dev-dependencies.rustix] version = "0.38.22" features = [ "event", "mm", ] [dev-dependencies.rustyline] version = "13" [features] use_bindgen = ["drm-ffi/use_bindgen"] drm-0.11.1/Cargo.toml.orig000064400000000000000000000015671046102023000133660ustar 00000000000000[package] name = "drm" description = "Safe, low-level bindings to the Direct Rendering Manager API" repository = "https://github.com/Smithay/drm-rs" version = "0.11.1" license = "MIT" authors = ["Tyler Slabinski ", "Victoria Brekenfeld "] exclude = [".gitignore", ".github"] rust-version = "1.65" edition = "2021" [dependencies] bitflags = "2" bytemuck = { version = "1.12", features = ["extern_crate_alloc", "derive"] } drm-ffi = { path = "drm-ffi", version = "0.7.1" } drm-fourcc = "^2.2.0" rustix = { version = "0.38.22", features = ["mm", "fs"] } [dev-dependencies] image = { version = "0.24", default-features = false, features = ["png"] } rustix = { version = "0.38.22", features = ["event", "mm"] } rustyline = "13" [features] use_bindgen = ["drm-ffi/use_bindgen"] [workspace] members = [ "drm-ffi", "drm-ffi/drm-sys", ] drm-0.11.1/LICENSE000064400000000000000000000017771046102023000115070ustar 00000000000000Permission 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. drm-0.11.1/README.md000064400000000000000000000057151046102023000117550ustar 00000000000000# drm-rs [![Crates.io](https://img.shields.io/crates/v/drm.svg)](https://crates.io/crates/drm) [![docs.rs](https://docs.rs/drm/badge.svg)](https://docs.rs/drm) [![Build Status](https://github.com/Smithay/drm-rs/actions/workflows/ci.yml/badge.svg)](https://github.com/Smithay/drm-rs/actions/workflows/ci.yml) A safe interface to the Direct Rendering Manager. ## Direct Rendering Manager The Direct Rendering Manager is a subsystem found on multiple Unix-based operating systems that provides a userspace API to graphics hardware. See the [Wikipedia article](https://en.wikipedia.org/wiki/Direct_Rendering_Manager) for more details. ## Usage ### Basic The DRM is accessed using [ioctls](https://en.wikipedia.org/wiki/Ioctl) on a file representing a graphics card. These can normally be found in `/dev/dri`, but can also be opened in other ways (ex. udev). This crate does not provide a method of opening these files. Instead, the user program must provide a way to access the file descriptor representing the device through the [AsFd](https://doc.rust-lang.org/std/os/fd/trait.AsFd.html) trait. Here is a basic example using `File` as a backend: ```rust /// A simple wrapper for a device node. pub struct Card(std::fs::File); /// Implementing [`AsFd`] is a prerequisite to implementing the traits found /// in this crate. Here, we are just calling [`File::as_fd()`] on the inner /// [`File`]. impl AsFd for Card { fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() } } /// Simple helper methods for opening a `Card`. impl Card { pub fn open(path: &str) -> Self { let mut options = std::fs::OpenOptions::new(); options.read(true); options.write(true); Card(options.open(path).unwrap()) } } ``` Finally, you can implement `drm::Device` to gain access to the basic DRM functionality: ```rust impl drm::Device for Card {} fn main() { let gpu = Card::open("/dev/dri/card0"); println!("{:#?}", gpu.get_driver().unwrap()); } ``` ### Control (modesetting) See [`drm::control::Device`](https://docs.rs/drm/*/drm/control/trait.Device.html) as well as our mode-setting examples: [`atomic_modeset`](https://github.com/Smithay/drm-rs/blob/develop/examples/atomic_modeset.rs) and [`legacy_modeset`](https://github.com/Smithay/drm-rs/blob/develop/examples/legacy_modeset.rs) ### Rendering Rendering is done by [creating](https://docs.rs/drm/*/drm/control/trait.Device.html#method.add_framebuffer) and [attaching](https://docs.rs/drm/*/drm/control/trait.Device.html#method.page_flip) [framebuffers](https://docs.rs/drm/*/drm/control/framebuffer/index.html) to [crtcs](https://docs.rs/drm/*/drm/control/crtc/index.html). A framebuffer is created from anything implementing [`Buffer`](https://docs.rs/drm/*/drm/buffer/trait.Buffer.html) like the always available, but very limited, [`DumbBuffer`](https://docs.rs/drm/*/drm/control/dumbbuffer/struct.DumbBuffer.html). For faster hardware-backed buffers, checkout [gbm.rs](https://github.com/Smithay/gbm.rs). drm-0.11.1/examples/atomic_modeset.rs000064400000000000000000000141461046102023000156540ustar 00000000000000mod utils; use crate::utils::*; use drm::control::Device as ControlDevice; use drm::Device as BasicDevice; use drm::buffer::DrmFourcc; use drm::control::{self, atomic, connector, crtc, property, AtomicCommitFlags}; pub fn main() { let card = Card::open_global(); card.set_client_capability(drm::ClientCapability::UniversalPlanes, true) .expect("Unable to request UniversalPlanes capability"); card.set_client_capability(drm::ClientCapability::Atomic, true) .expect("Unable to request Atomic capability"); // Load the information. let res = card .resource_handles() .expect("Could not load normal resource ids."); let coninfo: Vec = res .connectors() .iter() .flat_map(|con| card.get_connector(*con, true)) .collect(); let crtcinfo: Vec = res .crtcs() .iter() .flat_map(|crtc| card.get_crtc(*crtc)) .collect(); // Filter each connector until we find one that's connected. let con = coninfo .iter() .find(|&i| i.state() == connector::State::Connected) .expect("No connected connectors"); // Get the first (usually best) mode let &mode = con.modes().first().expect("No modes found on connector"); let (disp_width, disp_height) = mode.size(); // Find a crtc and FB let crtc = crtcinfo.first().expect("No crtcs found"); // Select the pixel format let fmt = DrmFourcc::Xrgb8888; // Create a DB // If buffer resolution is above display resolution, a ENOSPC (not enough GPU memory) error may // occur let mut db = card .create_dumb_buffer((disp_width.into(), disp_height.into()), fmt, 32) .expect("Could not create dumb buffer"); // Map it and grey it out. { let mut map = card .map_dumb_buffer(&mut db) .expect("Could not map dumbbuffer"); for b in map.as_mut() { *b = 128; } } // Create an FB: let fb = card .add_framebuffer(&db, 24, 32) .expect("Could not create FB"); let planes = card.plane_handles().expect("Could not list planes"); let (better_planes, compatible_planes): ( Vec, Vec, ) = planes .iter() .filter(|&&plane| { card.get_plane(plane) .map(|plane_info| { let compatible_crtcs = res.filter_crtcs(plane_info.possible_crtcs()); compatible_crtcs.contains(&crtc.handle()) }) .unwrap_or(false) }) .partition(|&&plane| { if let Ok(props) = card.get_properties(plane) { for (&id, &val) in props.iter() { if let Ok(info) = card.get_property(id) { if info.name().to_str().map(|x| x == "type").unwrap_or(false) { return val == (drm::control::PlaneType::Primary as u32).into(); } } } } false }); let plane = *better_planes.first().unwrap_or(&compatible_planes[0]); println!("{:#?}", mode); println!("{:#?}", fb); println!("{:#?}", db); println!("{:#?}", plane); let con_props = card .get_properties(con.handle()) .expect("Could not get props of connector") .as_hashmap(&card) .expect("Could not get a prop from connector"); let crtc_props = card .get_properties(crtc.handle()) .expect("Could not get props of crtc") .as_hashmap(&card) .expect("Could not get a prop from crtc"); let plane_props = card .get_properties(plane) .expect("Could not get props of plane") .as_hashmap(&card) .expect("Could not get a prop from plane"); let mut atomic_req = atomic::AtomicModeReq::new(); atomic_req.add_property( con.handle(), con_props["CRTC_ID"].handle(), property::Value::CRTC(Some(crtc.handle())), ); let blob = card .create_property_blob(&mode) .expect("Failed to create blob"); atomic_req.add_property(crtc.handle(), crtc_props["MODE_ID"].handle(), blob); atomic_req.add_property( crtc.handle(), crtc_props["ACTIVE"].handle(), property::Value::Boolean(true), ); atomic_req.add_property( plane, plane_props["FB_ID"].handle(), property::Value::Framebuffer(Some(fb)), ); atomic_req.add_property( plane, plane_props["CRTC_ID"].handle(), property::Value::CRTC(Some(crtc.handle())), ); atomic_req.add_property( plane, plane_props["SRC_X"].handle(), property::Value::UnsignedRange(0), ); atomic_req.add_property( plane, plane_props["SRC_Y"].handle(), property::Value::UnsignedRange(0), ); atomic_req.add_property( plane, plane_props["SRC_W"].handle(), property::Value::UnsignedRange((mode.size().0 as u64) << 16), ); atomic_req.add_property( plane, plane_props["SRC_H"].handle(), property::Value::UnsignedRange((mode.size().1 as u64) << 16), ); atomic_req.add_property( plane, plane_props["CRTC_X"].handle(), property::Value::SignedRange(0), ); atomic_req.add_property( plane, plane_props["CRTC_Y"].handle(), property::Value::SignedRange(0), ); atomic_req.add_property( plane, plane_props["CRTC_W"].handle(), property::Value::UnsignedRange(mode.size().0 as u64), ); atomic_req.add_property( plane, plane_props["CRTC_H"].handle(), property::Value::UnsignedRange(mode.size().1 as u64), ); // Set the crtc // On many setups, this requires root access. card.atomic_commit(AtomicCommitFlags::ALLOW_MODESET, atomic_req) .expect("Failed to set mode"); let five_seconds = ::std::time::Duration::from_millis(5000); ::std::thread::sleep(five_seconds); card.destroy_framebuffer(fb).unwrap(); card.destroy_dumb_buffer(db).unwrap(); } drm-0.11.1/examples/basic.rs000064400000000000000000000021721046102023000137350ustar 00000000000000/// Check the `util` module to see how the `Card` structure is implemented. pub mod utils; use crate::utils::*; pub fn main() { let card = Card::open_global(); // Attempt to acquire and release master lock println!("Get Master lock: {:?}", card.acquire_master_lock()); println!("Release Master lock: {:?}", card.release_master_lock()); // Get the Bus ID of the device println!("Getting Bus ID: {:?}", card.get_bus_id().unwrap()); // Figure out driver in use println!("Getting driver info"); let driver = card.get_driver().unwrap(); println!("\tName: {:?}", driver.name()); println!("\tDate: {:?}", driver.date()); println!("\tDesc: {:?}", driver.description()); // Enable all possible client capabilities println!("Setting client capabilities"); for &cap in capabilities::CLIENT_CAP_ENUMS { println!("\t{:?}: {:?}", cap, card.set_client_capability(cap, true)); } // Get driver capabilities println!("Getting driver capabilities"); for &cap in capabilities::DRIVER_CAP_ENUMS { println!("\t{:?}: {:?}", cap, card.get_driver_capability(cap)); } } drm-0.11.1/examples/ffi.rs000064400000000000000000000033011046102023000134130ustar 00000000000000use drm_ffi as ffi; use std::fs::{File, OpenOptions}; use std::os::unix::io::{AsFd, BorrowedFd}; #[derive(Debug)] // This is our customized struct that implements the traits in drm. struct Card(File); // Need to implement AsRawFd before we can implement drm::Device impl AsFd for Card { fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() } } impl Card { fn open(path: &str) -> Self { let mut options = OpenOptions::new(); options.read(true); options.write(true); Card(options.open(path).unwrap()) } fn open_global() -> Self { Self::open("/dev/dri/card0") } } fn print_busid(fd: BorrowedFd<'_>) { let mut buffer = Vec::new(); let busid = ffi::get_bus_id(fd, Some(&mut buffer)); println!("{:#?}", busid); } fn print_client(fd: BorrowedFd<'_>) { let client = ffi::get_client(fd, 0); println!("{:#?}", client); } fn print_version(fd: BorrowedFd<'_>) { let mut name = Vec::new(); let mut date = Vec::new(); let mut desc = Vec::new(); let version = ffi::get_version(fd, Some(&mut name), Some(&mut date), Some(&mut desc)); println!("{:#?}", version); } fn print_capabilities(fd: BorrowedFd<'_>) { for cty in 1.. { let cap = ffi::get_capability(fd, cty); match cap { Ok(_) => println!("{:#?}", cap), Err(_) => break, } } } fn print_token(fd: BorrowedFd<'_>) { let token = ffi::auth::get_magic_token(fd); println!("{:#?}", token); } fn main() { let card = Card::open_global(); let fd = card.as_fd(); print_busid(fd); print_client(fd); print_version(fd); print_capabilities(fd); print_token(fd); //print_stats(fd); } drm-0.11.1/examples/images/1.png000064400000000000000000000036211046102023000144210ustar 00000000000000PNG  IHDRH>PLTEpppۓɷ pHYs+IDATxMnтؙ me 0;w@1L1Lpsm4`.n$vǀv0Ž9#)pp.} )]~ppxЎǀv0>c `|mCWOn=ȳ0.;#)0vxǀv0Ż.=#)0Ճ,Lqǀv0oa/!){mn@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq@'8 Nq/m\QIENDB`drm-0.11.1/examples/images/2.png000064400000000000000000000055571046102023000144340ustar 00000000000000PNG  IHDRH>PLTEpppeC pHYs+ IDATxϗ\Ed&K2ƀt<`#qi+'lTK9#6HߪIf>=ԫV;pK<|GOO?}{gG\ѧ{GO衱soC1]^?%qOi@|߆GiO!3+?g0zLW|2zL͵3//F;u3 vG_8p͛?/#@Bv_k8`OGn}>pU7֣WIm2v߿/ {?tW ^ =' ]?? >\~;K8߹j#@6pLu}v2d}-r2Lˇ5Yv$Ǟ&^&ΦLd'w;6Ajw;)׾lw7b'jg~P@68͡E@UiwpfanlVٚ@45֣j2jAH.0Nm pi`&^ j )[7CZiz-4i=%`fh fUpa iWףļ:^40LL*i@i$=`6 = ti:BXc{hiu4MX0L[r+TvX:HEk@@i @(L;hԁ%´u`@aLLVMgin Ĭ @( [;(i' w'P\8 [;h n4dis&0&l&p1v te1m61d33dklLLLa+LY;ô1nv= f. ̱ Z{= 4`nXZ&0m`n 1D{GĜ*KAe`@vp-xvQ2GĬ*N,]X+x Z e<`Y$F(]XH. ="%>(]'2p6 K+gi;Be.RTt9 U,]h=zD̩er@Q> I,Q,]Q> 9,할Y`.Ҟ kҶe䀢t[@Y69(P(Kme鶀#bNa(-P~ (-`~ =$mDAD鷀tQdYFjȲltfQ"CbNw1)" p ' p ' p ' p ' p 't8r_QlIENDB`drm-0.11.1/examples/images/3.png000064400000000000000000000070121046102023000144210ustar 00000000000000PNG  IHDRH>PLTEpppeC pHYs+ IDATxյ eZqcRX֐@²[Hg?SFg4-ifX{=3v ={yųߛn >;?}~h\7oɽ1Қ;kJJ v/3{qhi/=|Z:btxOQӐ[+  g<u \yLKz^~_4p ZoMnݏ?|ˀjuC/fnPӰ3/yXNSkiO *v&?6hn_J*.2ZXЕp޿qcwo6YK4.2㿠wpܿ3 sMwp|{Y?b'w;O.{smDAλ/쿨:'}jGMS%oInV;+=up&M}%nVW߼'\.XiA5gHiE%]ݚ@iGs 04pYGz>ˀ6r]0M=7 7 \-po7|{n8i4u4%W@5N[k1\mku}@_2ޮ$ְ:mtFW%@}{[:Kv^TNzx_Zr*8i']EU0i1NZGT\א;ák9`R:V}IpZ Ng\KGC"@WP:q`NOH?΅0|*bA! O΅bt4t@)0<* }jBIh; P r϶-txD<m0IN%@n 9 Hn6IfaPB# l΅ = %@n!JGCBGC =й5aC&so͌D$t{&b@B$D%48?QIh\(΅:Jp>efw@ *8@ O8 Qvgt0t.m olx>M'sa 0- ] (9M@Mn|oOJC ` N`Z:@s%M`: m3@jqhH't> 0-ڎ I&x9X6l?N@j@2`ѩ7>@.ΤtHt;Ne~d2?cM䃹P:&|5?&<Qѕ4tTtd}_N >9 g iݟk4tdooaoS=\7nwd7?gMpGϞ`_t4ᝋuͰb4)8X)Lcx9O7cAK'et4kt4m LLJK+.M[r8&`,_x"6*@)AV; z2)7 Y4ЖpSDhC6 0:)=k['F;ᳮ@kշ^x6`8wKtдƝ<$6e@y:` Ͻt|旋s) k0\x$_8hP::еX 8* ?ݶ -Vй6) ih\΅mIh[ 0|>P! t JPrP: йxX>4- lx4 P <* -!'ԂOJGC{#  ܖ-$| ~=* Hn rɅ;@rI$@r - %@n:{[+8w$@nɠй$@n`s{6JGC{?@> 0- /|g`R::7;O|B;Nh\8 |Y|Q|B%ip>xR::@Gs,(PpO(: @'t~4N(p'8 4s`[X vv :|mRe t@Kp:p'*5`:րă7:ft %<8(eSXds AYd^UlOdvr {h{¬ I[׮kҩƓkmΥ$5+Wf^>/<DyzP+[^~z3tSݫ5s٬7zkv O_M]f7\s0 [fK#l~=W_Wu3]7ޚ[\ԍ@o֛j7>IWucu 0=tګiXZ,@-HK^i+)/O˿Dp  r)0h= wo'_bd O;͠s>Gޛ?2\ ջ{?{g9wWZzf]*^|y񸿦M XpyWGF,8YY3tW#-87i5TZ Z]Y,}KX:2x_>+u775x^d~rz ÿT)=wwq07/tO.w~PiAҪEI0~w?᳟ߔ^x݃!ƙI äϻIENDB`drm-0.11.1/examples/images/4.png000064400000000000000000000051631046102023000144270ustar 00000000000000PNG  IHDRH>PLTEpppۥɁf pHYs+ IDATxn[ain]*w@J "pץ z(tFH(@~ 3y:.dߞ GrCA~~(?|~(Eݡٮ=?@~ Ł1p!>H},OAsA> ض>H% E`2ls| {`V߶ 1p!>/GH@g탼?߳cB۶gɇ}J;R `z}fEЮ={ `r>ܱ1p!u?܎]>ܞ0m{O_] LmCN"ى1p!0# ur \`^E1p!y0 _>-Lk1p!Y w}M:R `VdA*L<<k1p!9 ό >H%90"ع `J獁 Li}J3:R `FA*hӞcB:w ]a}J:c|}_|Y s>H%t Gf` \`6E1p!l}vd0nd6>"pޮ=# r>H%,Jwxr \`&T fmOD~#t g Ld}JX<k1p!y,Ƣ}JXR ` ̢H%]R `| Xûh0]{TE F,cB/o^.  Y8.0].۶= v>H%u io.9  fמޢ}Jb!]yHf0m{v  d \`X>EcBڴGwVFٵ'wVԕ 0kA*t \`HWT m7 `DTQr6]|H1޻0]{hW g1p!t׌ `8>ݕopζ=k&ѬR `4Tͦ=x}">H%u0U_ޭF e}JCYmHVIwxHi]?.0~~w ` +TK/_ڳr0V0UA* \e \`TEwx>H%QtV/W{탬4.0M{P\>9b|v95.0ˌ  a}J#X^h \`/4.000m{H+ |^bׯu}N~Ke$@_'p'p'p'p'p'p'p'p'p'pKjogc/ fUIENDB`drm-0.11.1/examples/kms_interactive.rs000064400000000000000000000210351046102023000160420ustar 00000000000000/// Check the `util` module to see how the `Card` structure is implemented. pub mod utils; use crate::utils::*; use drm::control::{from_u32, RawResourceHandle}; pub fn main() { let card = Card::open_global(); // Enable all possible client capabilities for &cap in capabilities::CLIENT_CAP_ENUMS { if let Err(e) = card.set_client_capability(cap, true) { eprintln!("Unable to activate capability {:?}: {}", cap, e); return; } } run_repl(&card); } fn run_repl(card: &Card) { // Load a set of numbered images let images = [ images::load_image("1.png"), images::load_image("2.png"), images::load_image("3.png"), images::load_image("4.png"), ]; for image in &images { // Create the Dumbbuffer let fmt = drm::buffer::DrmFourcc::Xrgb8888; let mut db = card .create_dumb_buffer(image.dimensions(), fmt, 32) .unwrap(); // Create a Framebuffer to represent it let _fb = card.add_framebuffer(&db, 24, 32).unwrap(); // Load the image into the buffer { let mut mapping = card.map_dumb_buffer(&mut db).unwrap(); let buffer = mapping.as_mut(); for (img_px, map_px) in image.pixels().zip(buffer.chunks_exact_mut(4)) { // Assuming little endian, it's BGRA map_px[0] = img_px[0]; // Blue map_px[1] = img_px[1]; // Green map_px[2] = img_px[2]; // Red map_px[3] = img_px[3]; // Alpha } }; } // Using rustyline to create the interactive prompt. let editor_config = rustyline::config::Builder::new() .max_history_size(256) .unwrap() .completion_type(rustyline::config::CompletionType::List) .edit_mode(rustyline::config::EditMode::Vi) .auto_add_history(true) .build(); let mut kms_editor = rustyline::Editor::<(), _>::with_config(editor_config).unwrap(); let mut atomic_editor = rustyline::Editor::<(), _>::with_config(editor_config).unwrap(); for line in kms_editor.iter("KMS>> ").map(|x| x.unwrap()) { let args: Vec<_> = line.split_whitespace().collect(); match &args[..] { ["CreateAtomicSet"] => { for line in atomic_editor.iter("Atomic>> ").map(|x| x.unwrap()) { let args: Vec<_> = line.split_whitespace().collect(); match &args[..] { ["Quit"] => break, args => println!("{:?}", args), } } } // Destroying a framebuffer ["DestroyFramebuffer", handle] => { let handle: u32 = str::parse(handle).unwrap(); let handle: drm::control::framebuffer::Handle = from_u32(handle).unwrap(); if let Err(err) = card.destroy_framebuffer(handle) { println!("Unable to destroy framebuffer ({:?}): {}", handle, err); } } // Print out all resources ["GetResources"] => { let resources = card.resource_handles().unwrap(); println!("\tConnectors: {:?}", resources.connectors()); println!("\tEncoders: {:?}", resources.encoders()); println!("\tCRTCS: {:?}", resources.crtcs()); println!("\tFramebuffers: {:?}", resources.framebuffers()); let planes = card.plane_handles().unwrap(); println!("\tPlanes: {:?}", planes); } // Print out the values of a specific property ["GetProperty", handle] => { let handle: u32 = str::parse(handle).unwrap(); let handle: drm::control::property::Handle = from_u32(handle).unwrap(); let property = card.get_property(handle).unwrap(); println!("\tName: {:?}", property.name()); println!("\tMutable: {:?}", property.mutable()); println!("\tAtomic: {:?}", property.atomic()); println!("\tValue: {:#?}", property.value_type()); } // Get the property-value pairs of a single resource ["GetProperties", handle] => match HandleWithProperties::from_str(card, handle) { Ok(handle) => { let props = match handle { HandleWithProperties::Connector(handle) => { card.get_properties(handle).unwrap() } HandleWithProperties::CRTC(handle) => card.get_properties(handle).unwrap(), HandleWithProperties::Plane(handle) => card.get_properties(handle).unwrap(), }; for (id, val) in props.iter() { println!("\tProperty: {:?}\tValue: {:?}", id, val); } } Err(_) => println!("Unknown handle or handle has no properties"), }, // Set a property's value on a resource ["SetProperty", handle, property, value] => { let property: u32 = str::parse(property).unwrap(); let property: drm::control::property::Handle = from_u32(property).unwrap(); let value: u64 = str::parse(value).unwrap(); match HandleWithProperties::from_str(card, handle) { Ok(handle) => { match handle { HandleWithProperties::Connector(handle) => { println!("\t{:?}", card.set_property(handle, property, value)); } HandleWithProperties::CRTC(handle) => { println!("\t{:?}", card.set_property(handle, property, value)); } HandleWithProperties::Plane(handle) => { println!("\t{:?}", card.set_property(handle, property, value)); } }; } Err(_) => println!("Unknown handle or handle has no properties"), }; } ["GetModes", handle] => match HandleWithProperties::from_str(card, handle) { Ok(HandleWithProperties::Connector(handle)) => { let modes = card.get_modes(handle).unwrap(); for mode in modes { println!("\tName:\t{:?}", mode.name()); println!("\t\tSize:\t{:?}", mode.size()); println!("\t\tRefresh:\t{:?}", mode.vrefresh()); } } _ => println!("Unknown handle or handle is not a connector"), }, ["help"] => { println!("CreateAtomicSet"); println!("DestroyFramebuffer "); println!("GetResources"); println!("GetProperty "); println!("GetProperties "); println!("SetProperty "); println!("GetModes "); } ["quit"] => break, [] => (), _ => { println!("Unknown command"); } } } } #[allow(clippy::upper_case_acronyms)] enum HandleWithProperties { Connector(drm::control::connector::Handle), CRTC(drm::control::crtc::Handle), Plane(drm::control::plane::Handle), } impl HandleWithProperties { // This is a helper command that will take a string of a number and lookup // the corresponding resource. fn from_str(card: &Card, handle: &str) -> Result { let handle: u32 = str::parse(handle).unwrap(); let handle = RawResourceHandle::new(handle).unwrap(); let rhandles = card.resource_handles().unwrap(); for connector in rhandles.connectors().iter().map(|h| (*h).into()) { if handle == connector { return Ok(HandleWithProperties::Connector(handle.into())); } } for crtc in rhandles.crtcs().iter().map(|h| (*h).into()) { if handle == crtc { return Ok(HandleWithProperties::CRTC(handle.into())); } } let phandles = card.plane_handles().unwrap(); for plane in phandles.iter().map(|h| (*h).into()) { if handle == plane { return Ok(HandleWithProperties::Plane(handle.into())); } } Err(()) } } drm-0.11.1/examples/legacy_modeset.rs000064400000000000000000000043171046102023000156430ustar 00000000000000mod utils; use crate::utils::*; use drm::control::Device as ControlDevice; use drm::buffer::DrmFourcc; use drm::control::{connector, crtc}; pub fn main() { let card = Card::open_global(); // Load the information. let res = card .resource_handles() .expect("Could not load normal resource ids."); let coninfo: Vec = res .connectors() .iter() .flat_map(|con| card.get_connector(*con, true)) .collect(); let crtcinfo: Vec = res .crtcs() .iter() .flat_map(|crtc| card.get_crtc(*crtc)) .collect(); // Filter each connector until we find one that's connected. let con = coninfo .iter() .find(|&i| i.state() == connector::State::Connected) .expect("No connected connectors"); // Get the first (usually best) mode let &mode = con.modes().first().expect("No modes found on connector"); let (disp_width, disp_height) = mode.size(); // Find a crtc and FB let crtc = crtcinfo.first().expect("No crtcs found"); // Select the pixel format let fmt = DrmFourcc::Xrgb8888; // Create a DB // If buffer resolution is larger than display resolution, an ENOSPC (not enough video memory) // error may occur let mut db = card .create_dumb_buffer((disp_width.into(), disp_height.into()), fmt, 32) .expect("Could not create dumb buffer"); // Map it and grey it out. { let mut map = card .map_dumb_buffer(&mut db) .expect("Could not map dumbbuffer"); for b in map.as_mut() { *b = 128; } } // Create an FB: let fb = card .add_framebuffer(&db, 24, 32) .expect("Could not create FB"); println!("{:#?}", mode); println!("{:#?}", fb); println!("{:#?}", db); // Set the crtc // On many setups, this requires root access. card.set_crtc(crtc.handle(), Some(fb), (0, 0), &[con.handle()], Some(mode)) .expect("Could not set CRTC"); let five_seconds = ::std::time::Duration::from_millis(5000); ::std::thread::sleep(five_seconds); card.destroy_framebuffer(fb).unwrap(); card.destroy_dumb_buffer(db).unwrap(); } drm-0.11.1/examples/list_modes.rs000064400000000000000000000010571046102023000150170ustar 00000000000000/// Check the `util` module to see how the `Card` structure is implemented. pub mod utils; use crate::utils::*; pub fn main() { let card = Card::open_global(); let resources = card.resource_handles().unwrap(); for connector in resources.connectors().iter() { let info = card.get_connector(*connector, false).unwrap(); println!("Connector {:?}: {:?}", info.interface(), info.state()); if info.state() == drm::control::connector::State::Connected { println!("\t Modes:\n{:#?}", info.modes()); } } } drm-0.11.1/examples/properties.rs000064400000000000000000000026361046102023000150550ustar 00000000000000/// Check the `util` module to see how the `Card` structure is implemented. pub mod utils; use crate::utils::*; fn print_properties(card: &Card, handle: T) { let props = card.get_properties(handle).unwrap(); for (&id, &val) in props.iter() { println!("Property: {:?}", id); let info = card.get_property(id).unwrap(); println!("{:?}", info.name()); println!("{:#?}", info.value_type()); println!("Mutable: {}", info.mutable()); println!("Atomic: {}", info.atomic()); println!("Value: {:?}", info.value_type().convert_value(val)); println!(); } } pub fn main() { let card = Card::open_global(); // Enable all possible client capabilities for &cap in capabilities::CLIENT_CAP_ENUMS { if let Err(e) = card.set_client_capability(cap, true) { eprintln!("Unable to activate capability {:?}: {}", cap, e); return; } } let resources = card.resource_handles().unwrap(); let plane_res = card.plane_handles().unwrap(); for &handle in resources.connectors() { print_properties(&card, handle); } for &handle in resources.framebuffers() { print_properties(&card, handle); } for &handle in resources.crtcs() { print_properties(&card, handle); } for handle in plane_res { print_properties(&card, handle); } } drm-0.11.1/examples/resources.rs000064400000000000000000000051701046102023000146670ustar 00000000000000/// Check the `util` module to see how the `Card` structure is implemented. pub mod utils; use crate::utils::*; pub fn main() { let card = Card::open_global(); // Enable all possible client capabilities for &cap in capabilities::CLIENT_CAP_ENUMS { if let Err(e) = card.set_client_capability(cap, true) { eprintln!("Unable to activate capability {:?}: {}", cap, e); return; } } let resources = card.resource_handles().unwrap(); let plane_res = card.plane_handles().unwrap(); // Print out all card resource handles println!("Connectors:\t{:?}", resources.connectors()); println!("Encoders:\t{:?}", resources.encoders()); println!("CRTCs:\t\t{:?}", resources.crtcs()); println!("Framebuffers:\t{:?}", resources.framebuffers()); println!("Planes:\t\t{:?}", plane_res); for &handle in resources.connectors() { let info = card.get_connector(handle, false).unwrap(); println!("Connector: {:?}", handle); println!("\t{:?}-{}", info.interface(), info.interface_id()); println!("\t{:?}", info.state()); println!("\t{:?}", info.size()); println!("\t{:?}", info.encoders()); println!("\t{:?}", info.current_encoder()); for mode in card.get_modes(handle).unwrap() { println!("{:?}", mode); } } println!("\n"); for &handle in resources.encoders() { let info = card.get_encoder(handle).unwrap(); println!("Encoder: {:?}", handle); println!("\t{:?}", info.kind()); println!("\t{:?}", info.crtc()); } println!("\n"); for &handle in resources.crtcs() { let info = card.get_crtc(handle).unwrap(); println!("CRTC: {:?}", handle); println!("\tPosition: {:?}", info.position()); println!("\tMode: {:?}", info.mode()); println!("\tFramebuffer: {:?}", info.framebuffer()); println!("\tGamma Length: {:?}", info.gamma_length()); } println!("\n"); for &handle in resources.framebuffers() { let info = card.get_framebuffer(handle).unwrap(); println!("Framebuffer: {:?}", handle); println!("\tSize: {:?}", info.size()); println!("\tPitch: {:?}", info.pitch()); println!("\tBPP: {:?}", info.bpp()); println!("\tDepth: {:?}", info.depth()); } println!("\n"); for handle in plane_res { let info = card.get_plane(handle).unwrap(); println!("Plane: {:?}", handle); println!("\tCRTC: {:?}", info.crtc()); println!("\tFramebuffer: {:?}", info.framebuffer()); println!("\tFormats: {:?}", info.formats()); } } drm-0.11.1/examples/syncobj.rs000064400000000000000000000037141046102023000143260ustar 00000000000000/// Check the `util` module to see how the `Card` structure is implemented. pub mod utils; use crate::utils::*; use rustix::event::PollFlags; use std::{ io, os::unix::io::{AsFd, OwnedFd}, }; impl Card { fn simulate_command_submission(&self) -> io::Result { // Create a temporary syncobj to receive the command fence. let syncobj = self.create_syncobj(false)?; let sync_file = { // Fake a command submission by signalling the syncobj immediately. The kernel // attaches a null fence object which is always signalled. Other than this, there // isn't a good way to create and signal a fence object from user-mode, so an actual // device is required to test this properly. // // For a real device, the syncobj handle should be passed to a command submission // which is expected to set a fence to be signalled upon completion. self.syncobj_signal(&[syncobj])?; // Export fence set by previous ioctl to file descriptor. self.syncobj_to_fd(syncobj, true) }; // The sync file descriptor constitutes ownership of the fence, so the syncobj can be // safely destroyed. self.destroy_syncobj(syncobj)?; sync_file } } fn main() { let card = Card::open_global(); let sync_file = card.simulate_command_submission().unwrap(); let fd = sync_file.as_fd(); // Poll for readability. The DRM fence object will directly wake the thread when signalled. // // Alternatively, Tokio's AsyncFd may be used like so: // // use tokio::io::{Interest, unix::AsyncFd}; // let afd = AsyncFd::with_interest(sync_file, Interest::READABLE).unwrap(); // let future = async move { afd.readable().await.unwrap().retain_ready() }; // future.await; let mut poll_fds = [rustix::event::PollFd::new(&fd, PollFlags::IN)]; rustix::event::poll(&mut poll_fds, -1).unwrap(); } drm-0.11.1/examples/utils/mod.rs000064400000000000000000000033171046102023000145750ustar 00000000000000#![allow(dead_code)] pub use drm::control::Device as ControlDevice; pub use drm::Device; #[derive(Debug)] /// A simple wrapper for a device node. pub struct Card(std::fs::File); /// Implementing `AsFd` is a prerequisite to implementing the traits found /// in this crate. Here, we are just calling `as_fd()` on the inner File. impl std::os::unix::io::AsFd for Card { fn as_fd(&self) -> std::os::unix::io::BorrowedFd<'_> { self.0.as_fd() } } /// With `AsFd` implemented, we can now implement `drm::Device`. impl Device for Card {} impl ControlDevice for Card {} /// Simple helper methods for opening a `Card`. impl Card { pub fn open(path: &str) -> Self { let mut options = std::fs::OpenOptions::new(); options.read(true); options.write(true); Card(options.open(path).unwrap()) } pub fn open_global() -> Self { Self::open("/dev/dri/card0") } } pub mod capabilities { use drm::ClientCapability as CC; pub const CLIENT_CAP_ENUMS: &[CC] = &[CC::Stereo3D, CC::UniversalPlanes, CC::Atomic]; use drm::DriverCapability as DC; pub const DRIVER_CAP_ENUMS: &[DC] = &[ DC::DumbBuffer, DC::VBlankHighCRTC, DC::DumbPreferredDepth, DC::DumbPreferShadow, DC::Prime, DC::MonotonicTimestamp, DC::ASyncPageFlip, DC::CursorWidth, DC::CursorHeight, DC::AddFB2Modifiers, DC::PageFlipTarget, DC::CRTCInVBlankEvent, DC::SyncObj, DC::TimelineSyncObj, ]; } pub mod images { use image; pub fn load_image(name: &str) -> image::RgbaImage { let path = format!("examples/images/{}", name); image::open(path).unwrap().to_rgba8() } } drm-0.11.1/src/buffer/mod.rs000064400000000000000000000100071046102023000136510ustar 00000000000000//! Memory management and buffer functionality that the DRM subsystem exposes. //! //! # Summary //! //! The DRM subsystem exposes functionality for managing memory on modern GPU //! devices using a system called the Graphics Execution Manager (GEM). This //! system manages GPU buffers and exposes them to userspace using 32-bit //! handles. These handles are automatically reference counted in the kernel. //! //! GEM provides a small API for sharing buffers between processes. However, it //! does not provide any generic API for creating these. Instead, each driver //! provides its own method of creating these buffers. The `libgbm` library //! (part of the mesa project) provides a driver agnostic method of creating //! these buffers. //! //! There are two methods of sharing a GEM handle between processes: //! //! 1. Using `Flink` to globally publish a handle using a 32-bit 'name'. This //! requires either holding the DRM Master lock or having the process' //! [`AuthToken`](struct@crate::AuthToken) authenticated. However, any process can //! open these handles if they know (or even guess) the global name. //! //! 2. Converting the GEM handle into a PRIME file descriptor, and passing it //! like a regular one. This allows better control and security, and is the //! recommended method of sharing buffers. use crate::control; pub use drm_fourcc::{DrmFourcc, DrmModifier, DrmVendor, UnrecognizedFourcc, UnrecognizedVendor}; /// A handle to a GEM buffer /// /// # Notes /// /// There are no guarantees that this handle is valid. It is up to the user /// to make sure this handle does not outlive the underlying buffer, and to /// prevent buffers from leaking by properly closing them after they are done. #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(control::RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} impl From for control::RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: control::RawResourceHandle) -> Self { Handle(handle) } } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("buffer::Handle").field(&self.0).finish() } } /// The name of a GEM buffer. /// /// # Notes /// /// There are no guarantees that this name is valid. It is up to the user /// to make sure this name does not outlive the underlying buffer, and to /// prevent buffers from leaking by properly closing them after they are done. #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Name(u32); impl From for u32 { fn from(name: Name) -> u32 { name.0 } } impl std::fmt::Debug for Name { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("buffer::Name").field(&self.0).finish() } } /// Common functionality of all regular buffers. pub trait Buffer { /// The width and height of the buffer. fn size(&self) -> (u32, u32); /// The format of the buffer. fn format(&self) -> DrmFourcc; /// The pitch of the buffer. fn pitch(&self) -> u32; /// The handle to the buffer. fn handle(&self) -> Handle; } /// Planar buffers are buffers where each channel/plane is in its own buffer. /// /// Each plane has their own handle, pitch, and offsets. pub trait PlanarBuffer { /// The width and height of the buffer. fn size(&self) -> (u32, u32); /// The format of the buffer. fn format(&self) -> DrmFourcc; /// The modifier of the buffer. fn modifier(&self) -> Option; /// The pitches of the buffer. fn pitches(&self) -> [u32; 4]; /// The handles to the buffer. fn handles(&self) -> [Option; 4]; /// The offsets of the buffer. fn offsets(&self) -> [u32; 4]; } drm-0.11.1/src/control/atomic.rs000064400000000000000000000046551046102023000145710ustar 00000000000000//! Helpers for atomic modesetting. use crate::control; /// Helper struct to construct atomic commit requests #[derive(Debug, Clone, Default)] pub struct AtomicModeReq { pub(super) objects: Vec, pub(super) count_props_per_object: Vec, pub(super) props: Vec, pub(super) values: Vec, } impl AtomicModeReq { /// Create a new and empty atomic commit request pub fn new() -> AtomicModeReq { Self::default() } /// Add a property and value pair for a given raw resource to the request pub fn add_raw_property( &mut self, obj_id: control::RawResourceHandle, prop_id: control::property::Handle, value: control::property::RawValue, ) { // add object if missing (also to count_props_per_object) let (idx, prop_count) = match self.objects.binary_search(&obj_id) { Ok(idx) => (idx, self.count_props_per_object[idx]), Err(new_idx) => { self.objects.insert(new_idx, obj_id); self.count_props_per_object.insert(new_idx, 0); (new_idx, 0) } }; // get start of our objects props let prop_slice_start = self.count_props_per_object.iter().take(idx).sum::() as usize; // get end let prop_slice_end = prop_slice_start + prop_count as usize; // search for existing prop entry match self.props[prop_slice_start..prop_slice_end] .binary_search_by_key(&Into::::into(prop_id), |x| (*x).into()) { // prop exists, override Ok(prop_idx) => { self.values[prop_slice_start + prop_idx] = value; } Err(prop_idx) => { // increase prop count self.count_props_per_object[idx] += 1; // insert prop, insert value self.props.insert(prop_slice_start + prop_idx, prop_id); self.values.insert(prop_slice_start + prop_idx, value); } } } /// Add a property and value pair for a given handle to the request pub fn add_property( &mut self, handle: H, property: control::property::Handle, value: control::property::Value, ) where H: control::ResourceHandle, { self.add_raw_property(handle.into(), property, value.into()) } } drm-0.11.1/src/control/connector.rs000064400000000000000000000224121046102023000152760ustar 00000000000000//! # Connector //! //! Represents the physical output, such as a DisplayPort or VGA connector. //! //! A Connector is the physical connection between the display controller and //! a display. These objects keep track of connection information and state, //! including the modes that the current display supports. use crate::control; use drm_ffi as ffi; /// A handle to a connector #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(control::RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} impl From for control::RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: control::RawResourceHandle) -> Self { Handle(handle) } } impl control::ResourceHandle for Handle { const FFI_TYPE: u32 = ffi::DRM_MODE_OBJECT_CONNECTOR; } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("connector::Handle").field(&self.0).finish() } } /// Information about a connector #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Info { pub(crate) handle: Handle, pub(crate) interface: Interface, pub(crate) interface_id: u32, pub(crate) connection: State, pub(crate) size: Option<(u32, u32)>, pub(crate) modes: Vec, pub(crate) encoders: Vec, pub(crate) curr_enc: Option, pub(crate) subpixel: SubPixel, } impl Info { /// Returns the handle to this connector. pub fn handle(&self) -> Handle { self.handle } /// Returns the type of `Interface` of this connector. pub fn interface(&self) -> Interface { self.interface } /// Returns the interface ID of this connector. /// /// When multiple connectors have the same `Interface`, they will have /// different interface IDs. pub fn interface_id(&self) -> u32 { self.interface_id } /// Returns the `State` of this connector. pub fn state(&self) -> State { self.connection } /// Returns the size of the display (in millimeters) if connected. pub fn size(&self) -> Option<(u32, u32)> { self.size } /// Returns a list of encoders that can be possibly used by this connector. pub fn encoders(&self) -> &[control::encoder::Handle] { &self.encoders } /// Returns a list of modes this connector reports as supported. pub fn modes(&self) -> &[control::Mode] { &self.modes } /// Returns the current encoder attached to this connector. pub fn current_encoder(&self) -> Option { self.curr_enc } /// Subpixel order of the connected sink pub fn subpixel(&self) -> SubPixel { self.subpixel } } /// A physical interface type. #[allow(missing_docs)] #[allow(clippy::upper_case_acronyms)] #[non_exhaustive] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum Interface { Unknown, VGA, DVII, DVID, DVIA, Composite, SVideo, LVDS, Component, NinePinDIN, DisplayPort, HDMIA, HDMIB, TV, EmbeddedDisplayPort, Virtual, DSI, DPI, Writeback, SPI, USB, } impl Interface { /// Get interface name as string pub fn as_str(&self) -> &'static str { // source: https://github.com/torvalds/linux/blob/489fa31ea873282b41046d412ec741f93946fc2d/drivers/gpu/drm/drm_connector.c#L89 match self { Interface::Unknown => "Unknown", Interface::VGA => "VGA", Interface::DVII => "DVI-I", Interface::DVID => "DVI-D", Interface::DVIA => "DVI-A", Interface::Composite => "Composite", Interface::SVideo => "SVIDEO", Interface::LVDS => "LVDS", Interface::Component => "Component", Interface::NinePinDIN => "DIN", Interface::DisplayPort => "DP", Interface::HDMIA => "HDMI-A", Interface::HDMIB => "HDMI-B", Interface::TV => "TV", Interface::EmbeddedDisplayPort => "eDP", Interface::Virtual => "Virtual", Interface::DSI => "DSI", Interface::DPI => "DPI", Interface::Writeback => "Writeback", Interface::SPI => "SPI", Interface::USB => "USB", } } } impl From for Interface { fn from(n: u32) -> Self { match n { ffi::DRM_MODE_CONNECTOR_Unknown => Interface::Unknown, ffi::DRM_MODE_CONNECTOR_VGA => Interface::VGA, ffi::DRM_MODE_CONNECTOR_DVII => Interface::DVII, ffi::DRM_MODE_CONNECTOR_DVID => Interface::DVID, ffi::DRM_MODE_CONNECTOR_DVIA => Interface::DVIA, ffi::DRM_MODE_CONNECTOR_Composite => Interface::Composite, ffi::DRM_MODE_CONNECTOR_SVIDEO => Interface::SVideo, ffi::DRM_MODE_CONNECTOR_LVDS => Interface::LVDS, ffi::DRM_MODE_CONNECTOR_Component => Interface::Component, ffi::DRM_MODE_CONNECTOR_9PinDIN => Interface::NinePinDIN, ffi::DRM_MODE_CONNECTOR_DisplayPort => Interface::DisplayPort, ffi::DRM_MODE_CONNECTOR_HDMIA => Interface::HDMIA, ffi::DRM_MODE_CONNECTOR_HDMIB => Interface::HDMIB, ffi::DRM_MODE_CONNECTOR_TV => Interface::TV, ffi::DRM_MODE_CONNECTOR_eDP => Interface::EmbeddedDisplayPort, ffi::DRM_MODE_CONNECTOR_VIRTUAL => Interface::Virtual, ffi::DRM_MODE_CONNECTOR_DSI => Interface::DSI, ffi::DRM_MODE_CONNECTOR_DPI => Interface::DPI, ffi::DRM_MODE_CONNECTOR_WRITEBACK => Interface::Writeback, ffi::DRM_MODE_CONNECTOR_SPI => Interface::SPI, ffi::DRM_MODE_CONNECTOR_USB => Interface::USB, _ => Interface::Unknown, } } } impl From for u32 { fn from(interface: Interface) -> Self { match interface { Interface::Unknown => ffi::DRM_MODE_CONNECTOR_Unknown, Interface::VGA => ffi::DRM_MODE_CONNECTOR_VGA, Interface::DVII => ffi::DRM_MODE_CONNECTOR_DVII, Interface::DVID => ffi::DRM_MODE_CONNECTOR_DVID, Interface::DVIA => ffi::DRM_MODE_CONNECTOR_DVIA, Interface::Composite => ffi::DRM_MODE_CONNECTOR_Composite, Interface::SVideo => ffi::DRM_MODE_CONNECTOR_SVIDEO, Interface::LVDS => ffi::DRM_MODE_CONNECTOR_LVDS, Interface::Component => ffi::DRM_MODE_CONNECTOR_Component, Interface::NinePinDIN => ffi::DRM_MODE_CONNECTOR_9PinDIN, Interface::DisplayPort => ffi::DRM_MODE_CONNECTOR_DisplayPort, Interface::HDMIA => ffi::DRM_MODE_CONNECTOR_HDMIA, Interface::HDMIB => ffi::DRM_MODE_CONNECTOR_HDMIB, Interface::TV => ffi::DRM_MODE_CONNECTOR_TV, Interface::EmbeddedDisplayPort => ffi::DRM_MODE_CONNECTOR_eDP, Interface::Virtual => ffi::DRM_MODE_CONNECTOR_VIRTUAL, Interface::DSI => ffi::DRM_MODE_CONNECTOR_DSI, Interface::DPI => ffi::DRM_MODE_CONNECTOR_DPI, Interface::Writeback => ffi::DRM_MODE_CONNECTOR_WRITEBACK, Interface::SPI => ffi::DRM_MODE_CONNECTOR_SPI, Interface::USB => ffi::DRM_MODE_CONNECTOR_USB, } } } /// The state of a connector. #[allow(missing_docs)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum State { Connected, Disconnected, Unknown, } impl From for State { fn from(n: u32) -> Self { // These variables are not defined in drm_mode.h for some reason. // They were copied from libdrm's xf86DrmMode.h match n { 1 => State::Connected, 2 => State::Disconnected, _ => State::Unknown, } } } impl From for u32 { fn from(state: State) -> Self { // These variables are not defined in drm_mode.h for some reason. // They were copied from libdrm's xf86DrmMode.h match state { State::Connected => 1, State::Disconnected => 2, State::Unknown => 3, } } } /// Subpixel order of the connected sink #[non_exhaustive] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum SubPixel { /// Unknown geometry Unknown, /// Horizontal RGB HorizontalRgb, /// Horizontal BGR HorizontalBgr, /// Vertical RGB VerticalRgb, /// Vertical BGR VerticalBgr, /// No geometry None, /// Encountered value not supported by drm-rs NotImplemented, } impl SubPixel { pub(super) fn from_raw(n: u32) -> Self { // These values are not defined in drm_mode.h // They were copied from linux's drm_connector.h // Don't mistake those for ones used in xf86DrmMode.h (libdrm offsets those by 1) match n { 0 => Self::Unknown, 1 => Self::HorizontalRgb, 2 => Self::HorizontalBgr, 3 => Self::VerticalRgb, 4 => Self::VerticalBgr, 5 => Self::None, _ => Self::NotImplemented, } } } drm-0.11.1/src/control/crtc.rs000064400000000000000000000047031046102023000142420ustar 00000000000000//! # CRTC //! //! A CRTC is a display controller provided by your device. It's primary job is //! to take pixel data and send it to a connector with the proper resolution and //! frequencies. //! //! Specific CRTCs can only be attached to connectors that have an encoder it //! supports. For example, you can have a CRTC that can not output to analog //! connectors. These are built in hardware limitations. //! //! Each CRTC has a built in plane, which can have a framebuffer attached to it, //! but they can also use pixel data from other planes to perform hardware //! compositing. use crate::control; use drm_ffi as ffi; /// A handle to a specific CRTC #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(control::RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} impl From for control::RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: control::RawResourceHandle) -> Self { Handle(handle) } } impl control::ResourceHandle for Handle { const FFI_TYPE: u32 = ffi::DRM_MODE_OBJECT_CRTC; } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("crtc::Handle").field(&self.0).finish() } } /// Information about a specific CRTC #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Info { pub(crate) handle: Handle, pub(crate) position: (u32, u32), pub(crate) mode: Option, pub(crate) fb: Option, pub(crate) gamma_length: u32, } impl Info { /// Returns the handle to this CRTC. pub fn handle(&self) -> Handle { self.handle } /// Returns the position of the CRTC. pub fn position(&self) -> (u32, u32) { self.position } /// Returns the current mode of the CRTC. pub fn mode(&self) -> Option { self.mode } /// Returns the framebuffer currently attached to this CRTC. pub fn framebuffer(&self) -> Option { self.fb } /// Returns the size of the gamma LUT. pub fn gamma_length(&self) -> u32 { self.gamma_length } } drm-0.11.1/src/control/dumbbuffer.rs000064400000000000000000000034231046102023000154260ustar 00000000000000//! //! # DumbBuffer //! //! Memory-supported, slow, but easy & cross-platform buffer implementation //! use crate::buffer; use std::borrow::{Borrow, BorrowMut}; use std::ops::{Deref, DerefMut}; #[derive(Debug, Copy, Clone, PartialEq, Eq)] /// Slow, but generic [`buffer::Buffer`] implementation pub struct DumbBuffer { pub(crate) size: (u32, u32), pub(crate) length: usize, pub(crate) format: buffer::DrmFourcc, pub(crate) pitch: u32, pub(crate) handle: buffer::Handle, } /// Mapping of a [`DumbBuffer`] pub struct DumbMapping<'a> { pub(crate) _phantom: core::marker::PhantomData<&'a ()>, pub(crate) map: &'a mut [u8], } impl AsRef<[u8]> for DumbMapping<'_> { fn as_ref(&self) -> &[u8] { self.map } } impl AsMut<[u8]> for DumbMapping<'_> { fn as_mut(&mut self) -> &mut [u8] { self.map } } impl Borrow<[u8]> for DumbMapping<'_> { fn borrow(&self) -> &[u8] { self.map } } impl BorrowMut<[u8]> for DumbMapping<'_> { fn borrow_mut(&mut self) -> &mut [u8] { self.map } } impl Deref for DumbMapping<'_> { type Target = [u8]; fn deref(&self) -> &Self::Target { self.map } } impl DerefMut for DumbMapping<'_> { fn deref_mut(&mut self) -> &mut Self::Target { self.map } } impl<'a> Drop for DumbMapping<'a> { fn drop(&mut self) { unsafe { rustix::mm::munmap(self.map.as_mut_ptr() as *mut _, self.map.len()) .expect("Unmap failed"); } } } impl buffer::Buffer for DumbBuffer { fn size(&self) -> (u32, u32) { self.size } fn format(&self) -> buffer::DrmFourcc { self.format } fn pitch(&self) -> u32 { self.pitch } fn handle(&self) -> buffer::Handle { self.handle } } drm-0.11.1/src/control/encoder.rs000064400000000000000000000071561046102023000147330ustar 00000000000000//! # Encoder //! //! An encoder is a bridge between a CRTC and a connector that takes the pixel //! data of the CRTC and encodes it into a format the connector understands. use crate::control; use drm_ffi as ffi; /// A handle to an encoder #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(control::RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} impl From for control::RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: control::RawResourceHandle) -> Self { Handle(handle) } } impl control::ResourceHandle for Handle { const FFI_TYPE: u32 = ffi::DRM_MODE_OBJECT_ENCODER; } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("encoder::Handle").field(&self.0).finish() } } /// Information about an encoder #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Info { pub(crate) handle: Handle, pub(crate) enc_type: Kind, pub(crate) crtc: Option, pub(crate) pos_crtcs: u32, pub(crate) pos_clones: u32, } impl Info { /// Returns the handle to this encoder. pub fn handle(&self) -> Handle { self.handle } /// Returns the `Kind` of encoder this is. pub fn kind(&self) -> Kind { self.enc_type } /// Returns a handle to the CRTC this encoder is attached to. pub fn crtc(&self) -> Option { self.crtc } /// Returns a filter for the possible CRTCs that can use this encoder. /// /// Use with [`control::ResourceHandles::filter_crtcs`] /// to receive a list of crtcs. pub fn possible_crtcs(&self) -> control::CrtcListFilter { control::CrtcListFilter(self.pos_crtcs) } /// Returns a filter for the possible encoders that clones this one. pub fn possible_clones(&self) { unimplemented!() } } /// The type of encoder. #[allow(missing_docs)] #[allow(clippy::upper_case_acronyms)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum Kind { None, DAC, TMDS, LVDS, TVDAC, Virtual, DSI, DPMST, DPI, } impl From for Kind { fn from(n: u32) -> Self { match n { ffi::DRM_MODE_ENCODER_NONE => Kind::None, ffi::DRM_MODE_ENCODER_DAC => Kind::DAC, ffi::DRM_MODE_ENCODER_TMDS => Kind::TMDS, ffi::DRM_MODE_ENCODER_LVDS => Kind::LVDS, ffi::DRM_MODE_ENCODER_TVDAC => Kind::TVDAC, ffi::DRM_MODE_ENCODER_VIRTUAL => Kind::Virtual, ffi::DRM_MODE_ENCODER_DSI => Kind::DSI, ffi::DRM_MODE_ENCODER_DPMST => Kind::DPMST, ffi::DRM_MODE_ENCODER_DPI => Kind::DPI, _ => Kind::None, } } } impl From for u32 { fn from(kind: Kind) -> Self { match kind { Kind::None => ffi::DRM_MODE_ENCODER_NONE, Kind::DAC => ffi::DRM_MODE_ENCODER_DAC, Kind::TMDS => ffi::DRM_MODE_ENCODER_TMDS, Kind::LVDS => ffi::DRM_MODE_ENCODER_LVDS, Kind::TVDAC => ffi::DRM_MODE_ENCODER_TVDAC, Kind::Virtual => ffi::DRM_MODE_ENCODER_VIRTUAL, Kind::DSI => ffi::DRM_MODE_ENCODER_DSI, Kind::DPMST => ffi::DRM_MODE_ENCODER_DPMST, Kind::DPI => ffi::DRM_MODE_ENCODER_DPI, } } } drm-0.11.1/src/control/framebuffer.rs000064400000000000000000000067561046102023000156050ustar 00000000000000//! # Framebuffer //! //! Process specific GPU buffers that can be attached to a plane. use crate::buffer; use crate::control; use drm_ffi as ffi; use drm_fourcc::{DrmFourcc, DrmModifier}; /// A handle to a framebuffer #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(control::RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} impl From for control::RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: control::RawResourceHandle) -> Self { Handle(handle) } } impl control::ResourceHandle for Handle { const FFI_TYPE: u32 = ffi::DRM_MODE_OBJECT_FB; } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("framebuffer::Handle").field(&self.0).finish() } } /// Information about a framebuffer #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct Info { pub(crate) handle: Handle, pub(crate) size: (u32, u32), pub(crate) pitch: u32, pub(crate) bpp: u32, pub(crate) depth: u32, pub(crate) buffer: Option, } impl Info { /// Returns the handle to this framebuffer. pub fn handle(&self) -> Handle { self.handle } /// Returns the size of this framebuffer. pub fn size(&self) -> (u32, u32) { self.size } /// Returns the pitch of this framebuffer. pub fn pitch(&self) -> u32 { self.pitch } /// Returns the bits-per-pixel of this framebuffer. pub fn bpp(&self) -> u32 { self.bpp } /// Returns the depth of this framebuffer. pub fn depth(&self) -> u32 { self.depth } /// Returns the buffer handle of this framebuffer. pub fn buffer(&self) -> Option { self.buffer } } /// Information about a framebuffer (with modifiers) #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct PlanarInfo { pub(crate) handle: Handle, pub(crate) size: (u32, u32), pub(crate) pixel_format: DrmFourcc, pub(crate) flags: control::FbCmd2Flags, pub(crate) buffers: [Option; 4], pub(crate) pitches: [u32; 4], pub(crate) offsets: [u32; 4], pub(crate) modifier: Option, } impl PlanarInfo { /// Returns the handle to this framebuffer. pub fn handle(&self) -> Handle { self.handle } /// Returns the size of this framebuffer. pub fn size(&self) -> (u32, u32) { self.size } /// Returns the pixel format of this framebuffer. pub fn pixel_format(&self) -> DrmFourcc { self.pixel_format } /// Returns the flags of this framebuffer. pub fn flags(&self) -> control::FbCmd2Flags { self.flags } /// Returns the buffer handles of this framebuffer. pub fn buffers(&self) -> [Option; 4] { self.buffers } /// Returns the pitches of this framebuffer. pub fn pitches(&self) -> [u32; 4] { self.pitches } /// Returns the offsets of this framebuffer. pub fn offsets(&self) -> [u32; 4] { self.offsets } /// Returns the modifier of this framebuffer. pub fn modifier(&self) -> Option { self.modifier } } drm-0.11.1/src/control/mod.rs000064400000000000000000001430661046102023000140740ustar 00000000000000//! Modesetting operations that the DRM subsystem exposes. //! //! # Summary //! //! The DRM subsystem provides Kernel Modesetting (KMS) functionality by //! exposing the following resource types: //! //! * FrameBuffer - Specific to an individual process, these wrap around generic //! GPU buffers so that they can be attached to a Plane. //! //! * Planes - Dedicated memory objects which contain a buffer that can then be //! scanned out by a CRTC. There exist a few different types of planes depending //! on the use case. //! //! * CRTC - Scanout engines that read pixel data from a Plane and sends it to //! a Connector. Each CRTC has at least one Primary Plane. //! //! * Connector - Represents the physical output, such as a DisplayPort or //! VGA connector. //! //! * Encoder - Encodes pixel data from a CRTC into something a Connector can //! understand. //! //! Further details on each resource can be found in their respective modules. //! //! # Usage //! //! To begin using modesetting functionality, the [`Device`] trait //! must be implemented on top of the basic [`super::Device`] trait. use drm_ffi as ffi; use drm_fourcc::{DrmFourcc, DrmModifier, UnrecognizedFourcc}; use bytemuck::allocation::TransparentWrapperAlloc; use rustix::io::Errno; pub mod atomic; pub mod connector; pub mod crtc; pub mod dumbbuffer; pub mod encoder; pub mod framebuffer; pub mod plane; pub mod syncobj; pub mod property; use self::dumbbuffer::*; use crate::buffer; use super::util::*; use std::collections::HashMap; use std::convert::TryFrom; use std::error; use std::fmt; use std::io; use std::iter::Zip; use std::mem; use std::ops::RangeBounds; use std::os::unix::io::{AsFd, BorrowedFd, FromRawFd, OwnedFd, RawFd}; use std::time::Duration; use core::num::NonZeroU32; /// Raw handle for a drm resource pub type RawResourceHandle = NonZeroU32; /// Id of a Lease pub type LeaseId = NonZeroU32; /// Handle for a drm resource pub trait ResourceHandle: From + Into + Into + Copy + Sized { /// Associated encoded object type const FFI_TYPE: u32; } /// Convert from a raw drm object value to a typed Handle /// /// Note: This does no verification on the validity of the original value pub fn from_u32>(raw: u32) -> Option { RawResourceHandle::new(raw).map(T::from) } /// Error from [`Device::get_planar_framebuffer`] #[derive(Debug)] pub enum GetPlanarFramebufferError { /// IO error Io(io::Error), /// Unrecognized fourcc format UnrecognizedFourcc(drm_fourcc::UnrecognizedFourcc), } impl fmt::Display for GetPlanarFramebufferError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Self::Io(err) => write!(f, "{}", err), Self::UnrecognizedFourcc(err) => write!(f, "{}", err), } } } impl error::Error for GetPlanarFramebufferError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { match self { Self::Io(err) => Some(err), Self::UnrecognizedFourcc(err) => Some(err), } } } impl From for GetPlanarFramebufferError { fn from(err: io::Error) -> Self { Self::Io(err) } } impl From for GetPlanarFramebufferError { fn from(err: UnrecognizedFourcc) -> Self { Self::UnrecognizedFourcc(err) } } /// This trait should be implemented by any object that acts as a DRM device and /// provides modesetting functionality. /// /// Like the parent [`super::Device`] trait, this crate does not /// provide a concrete object for this trait. /// /// # Example /// ```ignore /// use drm::control::Device as ControlDevice; /// /// /// Assuming the [`Card`] wrapper already implements [`drm::Device`] /// impl ControlDevice for Card {} /// ``` pub trait Device: super::Device { /// Gets the set of resource handles that this device currently controls fn resource_handles(&self) -> io::Result { let mut fbs = Vec::new(); let mut crtcs = Vec::new(); let mut connectors = Vec::new(); let mut encoders = Vec::new(); let ffi_res = ffi::mode::get_resources( self.as_fd(), Some(&mut fbs), Some(&mut crtcs), Some(&mut connectors), Some(&mut encoders), )?; let res = unsafe { ResourceHandles { fbs: transmute_vec_from_u32(fbs), crtcs: transmute_vec_from_u32(crtcs), connectors: transmute_vec_from_u32(connectors), encoders: transmute_vec_from_u32(encoders), width: (ffi_res.min_width, ffi_res.max_width), height: (ffi_res.min_height, ffi_res.max_height), } }; Ok(res) } /// Gets the set of plane handles that this device currently has fn plane_handles(&self) -> io::Result> { let mut planes = Vec::new(); let _ = ffi::mode::get_plane_resources(self.as_fd(), Some(&mut planes))?; Ok(unsafe { transmute_vec_from_u32(planes) }) } /// Returns information about a specific connector /// /// ## Force-probing /// /// If `force_probe` is set to `true` and the DRM client is the current DRM master, /// the kernel will perform a forced probe on the connector to refresh the connector status, modes and EDID. /// A forced-probe can be slow, might cause flickering and the ioctl will block. /// /// - User needs to force-probe connectors to ensure their metadata is up-to-date at startup and after receiving a hot-plug event. /// - User may perform a forced-probe when the user explicitly requests it. /// - User shouldn’t perform a forced-probe in other situations. fn get_connector( &self, handle: connector::Handle, force_probe: bool, ) -> io::Result { // Maximum number of encoders is 3 due to kernel restrictions let mut encoders = Vec::new(); let mut modes = Vec::new(); let ffi_info = ffi::mode::get_connector( self.as_fd(), handle.into(), None, None, Some(&mut modes), Some(&mut encoders), force_probe, )?; let connector = connector::Info { handle, interface: connector::Interface::from(ffi_info.connector_type), interface_id: ffi_info.connector_type_id, connection: connector::State::from(ffi_info.connection), size: match (ffi_info.mm_width, ffi_info.mm_height) { (0, 0) => None, (x, y) => Some((x, y)), }, modes: Mode::wrap_vec(modes), encoders: unsafe { transmute_vec_from_u32(encoders) }, curr_enc: unsafe { mem::transmute(ffi_info.encoder_id) }, subpixel: connector::SubPixel::from_raw(ffi_info.subpixel), }; Ok(connector) } /// Returns information about a specific encoder fn get_encoder(&self, handle: encoder::Handle) -> io::Result { let info = ffi::mode::get_encoder(self.as_fd(), handle.into())?; let enc = encoder::Info { handle, enc_type: encoder::Kind::from(info.encoder_type), crtc: from_u32(info.crtc_id), pos_crtcs: info.possible_crtcs, pos_clones: info.possible_clones, }; Ok(enc) } /// Returns information about a specific CRTC fn get_crtc(&self, handle: crtc::Handle) -> io::Result { let info = ffi::mode::get_crtc(self.as_fd(), handle.into())?; let crtc = crtc::Info { handle, position: (info.x, info.y), mode: match info.mode_valid { 0 => None, _ => Some(Mode::from(info.mode)), }, fb: from_u32(info.fb_id), gamma_length: info.gamma_size, }; Ok(crtc) } /// Set CRTC state fn set_crtc( &self, handle: crtc::Handle, framebuffer: Option, pos: (u32, u32), conns: &[connector::Handle], mode: Option, ) -> io::Result<()> { let _info = ffi::mode::set_crtc( self.as_fd(), handle.into(), framebuffer.map(Into::into).unwrap_or(0), pos.0, pos.1, unsafe { &*(conns as *const _ as *const [u32]) }, mode.map(|m| m.into()), )?; Ok(()) } /// Returns information about a specific framebuffer fn get_framebuffer(&self, handle: framebuffer::Handle) -> io::Result { let info = ffi::mode::get_framebuffer(self.as_fd(), handle.into())?; let fb = framebuffer::Info { handle, size: (info.width, info.height), pitch: info.pitch, bpp: info.bpp, depth: info.depth, buffer: from_u32(info.handle), }; Ok(fb) } /// Returns information about a specific framebuffer (with modifiers) fn get_planar_framebuffer( &self, handle: framebuffer::Handle, ) -> Result { let info = ffi::mode::get_framebuffer2(self.as_fd(), handle.into())?; let pixel_format = DrmFourcc::try_from(info.pixel_format)?; let flags = FbCmd2Flags::from_bits_truncate(info.flags); let modifier = flags .contains(FbCmd2Flags::MODIFIERS) .then(|| DrmModifier::from(info.modifier[0])); let fb = framebuffer::PlanarInfo { handle, size: (info.width, info.height), pixel_format, flags, buffers: bytemuck::cast(info.handles), pitches: info.pitches, offsets: info.offsets, modifier, }; Ok(fb) } /// Add a new framebuffer fn add_framebuffer( &self, buffer: &B, depth: u32, bpp: u32, ) -> io::Result where B: buffer::Buffer + ?Sized, { let (w, h) = buffer.size(); let info = ffi::mode::add_fb( self.as_fd(), w, h, buffer.pitch(), bpp, depth, buffer.handle().into(), )?; Ok(from_u32(info.fb_id).unwrap()) } /// Add framebuffer (with modifiers) fn add_planar_framebuffer( &self, planar_buffer: &B, flags: FbCmd2Flags, ) -> io::Result where B: buffer::PlanarBuffer + ?Sized, { let modifier = planar_buffer.modifier(); let has_modifier = flags.contains(FbCmd2Flags::MODIFIERS); assert!((has_modifier && modifier.is_some()) || (!has_modifier && modifier.is_none())); let modifier = if let Some(modifier) = modifier { u64::from(modifier) } else { 0 }; let (w, h) = planar_buffer.size(); let opt_handles = planar_buffer.handles(); let handles = bytemuck::cast(opt_handles); let mods = [ opt_handles[0].map_or(0, |_| modifier), opt_handles[1].map_or(0, |_| modifier), opt_handles[2].map_or(0, |_| modifier), opt_handles[3].map_or(0, |_| modifier), ]; let info = ffi::mode::add_fb2( self.as_fd(), w, h, planar_buffer.format() as u32, &handles, &planar_buffer.pitches(), &planar_buffer.offsets(), &mods, flags.bits(), )?; Ok(from_u32(info.fb_id).unwrap()) } /// Mark parts of a framebuffer dirty fn dirty_framebuffer(&self, handle: framebuffer::Handle, clips: &[ClipRect]) -> io::Result<()> { ffi::mode::dirty_fb(self.as_fd(), handle.into(), unsafe { // SAFETY: ClipRect is repr(transparent) for drm_clip_rect core::slice::from_raw_parts(clips.as_ptr() as *const ffi::drm_clip_rect, clips.len()) })?; Ok(()) } /// Destroy a framebuffer fn destroy_framebuffer(&self, handle: framebuffer::Handle) -> io::Result<()> { ffi::mode::rm_fb(self.as_fd(), handle.into()) } /// Returns information about a specific plane fn get_plane(&self, handle: plane::Handle) -> io::Result { let mut formats = Vec::new(); let info = ffi::mode::get_plane(self.as_fd(), handle.into(), Some(&mut formats))?; let plane = plane::Info { handle, crtc: from_u32(info.crtc_id), fb: from_u32(info.fb_id), pos_crtcs: info.possible_crtcs, formats: unsafe { transmute_vec_from_u32(formats) }, }; Ok(plane) } /// Set plane state. /// /// Providing no framebuffer clears the plane. fn set_plane( &self, handle: plane::Handle, crtc: crtc::Handle, framebuffer: Option, flags: u32, crtc_rect: (i32, i32, u32, u32), src_rect: (u32, u32, u32, u32), ) -> io::Result<()> { let _info = ffi::mode::set_plane( self.as_fd(), handle.into(), crtc.into(), framebuffer.map(Into::into).unwrap_or(0), flags, crtc_rect.0, crtc_rect.1, crtc_rect.2, crtc_rect.3, src_rect.0, src_rect.1, src_rect.2, src_rect.3, )?; Ok(()) } /// Returns information about a specific property. fn get_property(&self, handle: property::Handle) -> io::Result { let mut values = Vec::new(); let mut enums = Vec::new(); let info = ffi::mode::get_property( self.as_fd(), handle.into(), Some(&mut values), Some(&mut enums), )?; let flags = ModePropFlags::from_bits_truncate(info.flags); let val_type = { use self::property::ValueType; if flags.contains(ModePropFlags::RANGE) { let min = values[0]; let max = values[1]; match (min, max) { (0, 1) => ValueType::Boolean, (min, max) => ValueType::UnsignedRange(min, max), } } else if flags.contains(ModePropFlags::SIGNED_RANGE) { let min = values[0]; let max = values[1]; ValueType::SignedRange(min as i64, max as i64) } else if flags.contains(ModePropFlags::ENUM) { let enum_values = self::property::EnumValues { values, enums: property::EnumValue::wrap_vec(enums), }; ValueType::Enum(enum_values) } else if flags.contains(ModePropFlags::BLOB) { ValueType::Blob } else if flags.contains(ModePropFlags::BITMASK) { ValueType::Bitmask } else if flags.contains(ModePropFlags::OBJECT) { match values[0] as u32 { ffi::DRM_MODE_OBJECT_CRTC => ValueType::CRTC, ffi::DRM_MODE_OBJECT_CONNECTOR => ValueType::Connector, ffi::DRM_MODE_OBJECT_ENCODER => ValueType::Encoder, ffi::DRM_MODE_OBJECT_FB => ValueType::Framebuffer, ffi::DRM_MODE_OBJECT_PLANE => ValueType::Plane, ffi::DRM_MODE_OBJECT_PROPERTY => ValueType::Property, ffi::DRM_MODE_OBJECT_BLOB => ValueType::Blob, ffi::DRM_MODE_OBJECT_ANY => ValueType::Object, _ => ValueType::Unknown, } } else { ValueType::Unknown } }; let property = property::Info { handle, val_type, mutable: !flags.contains(ModePropFlags::IMMUTABLE), atomic: flags.contains(ModePropFlags::ATOMIC), info, }; Ok(property) } /// Sets a property for a specific resource. fn set_property( &self, handle: T, prop: property::Handle, value: property::RawValue, ) -> io::Result<()> { ffi::mode::set_property(self.as_fd(), prop.into(), handle.into(), T::FFI_TYPE, value)?; Ok(()) } /// Create a property blob value from a given data blob fn create_property_blob(&self, data: &T) -> io::Result> { let data = unsafe { std::slice::from_raw_parts_mut(data as *const _ as *mut u8, mem::size_of::()) }; let blob = ffi::mode::create_property_blob(self.as_fd(), data)?; Ok(property::Value::Blob(blob.blob_id.into())) } /// Get a property blob's data fn get_property_blob(&self, blob: u64) -> io::Result> { let mut data = Vec::new(); let _ = ffi::mode::get_property_blob(self.as_fd(), blob as u32, Some(&mut data))?; Ok(data) } /// Destroy a given property blob value fn destroy_property_blob(&self, blob: u64) -> io::Result<()> { ffi::mode::destroy_property_blob(self.as_fd(), blob as u32)?; Ok(()) } /// Returns the set of [`Mode`]s that a particular connector supports. fn get_modes(&self, handle: connector::Handle) -> io::Result> { let mut modes = Vec::new(); let _ffi_info = ffi::mode::get_connector( self.as_fd(), handle.into(), None, None, Some(&mut modes), None, false, )?; Ok(Mode::wrap_vec(modes)) } /// Gets a list of property handles and values for this resource. fn get_properties(&self, handle: T) -> io::Result { let mut prop_ids = Vec::new(); let mut prop_vals = Vec::new(); ffi::mode::get_properties( self.as_fd(), handle.into(), T::FFI_TYPE, Some(&mut prop_ids), Some(&mut prop_vals), )?; let prop_val_set = PropertyValueSet { prop_ids: unsafe { transmute_vec_from_u32(prop_ids) }, prop_vals, }; Ok(prop_val_set) } /// Receive the currently set gamma ramp of a crtc fn get_gamma( &self, crtc: crtc::Handle, red: &mut [u16], green: &mut [u16], blue: &mut [u16], ) -> io::Result<()> { let crtc_info = self.get_crtc(crtc)?; if crtc_info.gamma_length as usize > red.len() || crtc_info.gamma_length as usize > green.len() || crtc_info.gamma_length as usize > blue.len() { return Err(Errno::INVAL.into()); } ffi::mode::get_gamma( self.as_fd(), crtc.into(), crtc_info.gamma_length as usize, red, green, blue, )?; Ok(()) } /// Set a gamma ramp for the given crtc fn set_gamma( &self, crtc: crtc::Handle, red: &[u16], green: &[u16], blue: &[u16], ) -> io::Result<()> { let crtc_info = self.get_crtc(crtc)?; if crtc_info.gamma_length as usize > red.len() || crtc_info.gamma_length as usize > green.len() || crtc_info.gamma_length as usize > blue.len() { return Err(Errno::INVAL.into()); } ffi::mode::set_gamma( self.as_fd(), crtc.into(), crtc_info.gamma_length as usize, red, green, blue, )?; Ok(()) } /// Open a GEM buffer handle by name fn open_buffer(&self, name: buffer::Name) -> io::Result { let info = drm_ffi::gem::open(self.as_fd(), name.into())?; Ok(from_u32(info.handle).unwrap()) } /// Close a GEM buffer handle fn close_buffer(&self, handle: buffer::Handle) -> io::Result<()> { let _info = drm_ffi::gem::close(self.as_fd(), handle.into())?; Ok(()) } /// Create a new dumb buffer with a given size and pixel format fn create_dumb_buffer( &self, size: (u32, u32), format: buffer::DrmFourcc, bpp: u32, ) -> io::Result { let info = drm_ffi::mode::dumbbuffer::create(self.as_fd(), size.0, size.1, bpp, 0)?; let dumb = DumbBuffer { size: (info.width, info.height), length: info.size as usize, format, pitch: info.pitch, handle: from_u32(info.handle).unwrap(), }; Ok(dumb) } /// Map the buffer for access fn map_dumb_buffer<'a>(&self, buffer: &'a mut DumbBuffer) -> io::Result> { let info = drm_ffi::mode::dumbbuffer::map(self.as_fd(), buffer.handle.into(), 0, 0)?; let map = { use rustix::mm; let prot = mm::ProtFlags::READ | mm::ProtFlags::WRITE; let flags = mm::MapFlags::SHARED; let fd = self.as_fd(); let offset = info.offset as _; unsafe { mm::mmap(std::ptr::null_mut(), buffer.length, prot, flags, fd, offset)? } }; let mapping = DumbMapping { _phantom: std::marker::PhantomData, map: unsafe { std::slice::from_raw_parts_mut(map as *mut _, buffer.length) }, }; Ok(mapping) } /// Free the memory resources of a dumb buffer fn destroy_dumb_buffer(&self, buffer: DumbBuffer) -> io::Result<()> { let _info = drm_ffi::mode::dumbbuffer::destroy(self.as_fd(), buffer.handle.into())?; Ok(()) } /// Sets a hardware-cursor on the given crtc with the image of a given buffer /// /// A buffer argument of [`None`] will clear the cursor. #[deprecated(note = "Usage of deprecated ioctl set_cursor: use a cursor plane instead")] #[allow(deprecated)] fn set_cursor(&self, crtc: crtc::Handle, buffer: Option<&B>) -> io::Result<()> where B: buffer::Buffer + ?Sized, { let (id, w, h) = buffer .map(|buf| { let (w, h) = buf.size(); (buf.handle().into(), w, h) }) .unwrap_or((0, 0, 0)); drm_ffi::mode::set_cursor(self.as_fd(), crtc.into(), id, w, h)?; Ok(()) } /// Sets a hardware-cursor on the given crtc with the image of a given buffer /// and a hotspot marking the click point of the cursor. /// /// A buffer argument of [`None`] will clear the cursor. #[deprecated(note = "Usage of deprecated ioctl set_cursor2: use a cursor plane instead")] #[allow(deprecated)] fn set_cursor2( &self, crtc: crtc::Handle, buffer: Option<&B>, hotspot: (i32, i32), ) -> io::Result<()> where B: buffer::Buffer + ?Sized, { let (id, w, h) = buffer .map(|buf| { let (w, h) = buf.size(); (buf.handle().into(), w, h) }) .unwrap_or((0, 0, 0)); drm_ffi::mode::set_cursor2(self.as_fd(), crtc.into(), id, w, h, hotspot.0, hotspot.1)?; Ok(()) } /// Moves a set cursor on a given crtc #[deprecated(note = "Usage of deprecated ioctl move_cursor: use a cursor plane instead")] #[allow(deprecated)] fn move_cursor(&self, crtc: crtc::Handle, pos: (i32, i32)) -> io::Result<()> { drm_ffi::mode::move_cursor(self.as_fd(), crtc.into(), pos.0, pos.1)?; Ok(()) } /// Request an atomic commit with given flags and property-value pair for a list of objects. fn atomic_commit( &self, flags: AtomicCommitFlags, mut req: atomic::AtomicModeReq, ) -> io::Result<()> { drm_ffi::mode::atomic_commit( self.as_fd(), flags.bits(), unsafe { &mut *(&mut *req.objects as *mut _ as *mut [u32]) }, &mut req.count_props_per_object, unsafe { &mut *(&mut *req.props as *mut _ as *mut [u32]) }, &mut req.values, ) } /// Convert a prime file descriptor to a GEM buffer handle fn prime_fd_to_buffer(&self, fd: BorrowedFd<'_>) -> io::Result { let info = ffi::gem::fd_to_handle(self.as_fd(), fd)?; Ok(from_u32(info.handle).unwrap()) } /// Convert a GEM buffer handle to a prime file descriptor fn buffer_to_prime_fd(&self, handle: buffer::Handle, flags: u32) -> io::Result { let info = ffi::gem::handle_to_fd(self.as_fd(), handle.into(), flags)?; Ok(unsafe { OwnedFd::from_raw_fd(info.fd) }) } /// Queue a page flip on the given crtc fn page_flip( &self, handle: crtc::Handle, framebuffer: framebuffer::Handle, flags: PageFlipFlags, target_sequence: Option, ) -> io::Result<()> { let mut flags = flags.bits(); let sequence = match target_sequence { Some(PageFlipTarget::Absolute(n)) => { flags |= ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE; n } Some(PageFlipTarget::Relative(n)) => { flags |= ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET_RELATIVE; n } None => 0, }; ffi::mode::page_flip( self.as_fd(), handle.into(), framebuffer.into(), flags, sequence, )?; Ok(()) } /// Creates a syncobj. fn create_syncobj(&self, signalled: bool) -> io::Result { let info = ffi::syncobj::create(self.as_fd(), signalled)?; Ok(from_u32(info.handle).unwrap()) } /// Destroys a syncobj. fn destroy_syncobj(&self, handle: syncobj::Handle) -> io::Result<()> { ffi::syncobj::destroy(self.as_fd(), handle.into())?; Ok(()) } /// Exports a syncobj as an inter-process file descriptor or as a poll()-able sync file. fn syncobj_to_fd( &self, handle: syncobj::Handle, export_sync_file: bool, ) -> io::Result { let info = ffi::syncobj::handle_to_fd(self.as_fd(), handle.into(), export_sync_file)?; Ok(unsafe { OwnedFd::from_raw_fd(info.fd) }) } /// Imports a file descriptor exported by [`Self::syncobj_to_fd`] back into a process-local handle. fn fd_to_syncobj( &self, fd: BorrowedFd<'_>, import_sync_file: bool, ) -> io::Result { let info = ffi::syncobj::fd_to_handle(self.as_fd(), fd, import_sync_file)?; Ok(from_u32(info.handle).unwrap()) } /// Waits for one or more syncobjs to become signalled. fn syncobj_wait( &self, handles: &[syncobj::Handle], timeout_nsec: i64, wait_all: bool, wait_for_submit: bool, ) -> io::Result { let info = ffi::syncobj::wait( self.as_fd(), bytemuck::cast_slice(handles), timeout_nsec, wait_all, wait_for_submit, )?; Ok(info.first_signaled) } /// Resets (un-signals) one or more syncobjs. fn syncobj_reset(&self, handles: &[syncobj::Handle]) -> io::Result<()> { ffi::syncobj::reset(self.as_fd(), bytemuck::cast_slice(handles))?; Ok(()) } /// Signals one or more syncobjs. fn syncobj_signal(&self, handles: &[syncobj::Handle]) -> io::Result<()> { ffi::syncobj::signal(self.as_fd(), bytemuck::cast_slice(handles))?; Ok(()) } /// Waits for one or more specific timeline syncobj points. fn syncobj_timeline_wait( &self, handles: &[syncobj::Handle], points: &[u64], timeout_nsec: i64, wait_all: bool, wait_for_submit: bool, wait_available: bool, ) -> io::Result { let info = ffi::syncobj::timeline_wait( self.as_fd(), bytemuck::cast_slice(handles), points, timeout_nsec, wait_all, wait_for_submit, wait_available, )?; Ok(info.first_signaled) } /// Queries for state of one or more timeline syncobjs. fn syncobj_timeline_query( &self, handles: &[syncobj::Handle], points: &mut [u64], last_submitted: bool, ) -> io::Result<()> { ffi::syncobj::query( self.as_fd(), bytemuck::cast_slice(handles), points, last_submitted, )?; Ok(()) } /// Transfers one timeline syncobj point to another. fn syncobj_timeline_transfer( &self, src_handle: syncobj::Handle, dst_handle: syncobj::Handle, src_point: u64, dst_point: u64, ) -> io::Result<()> { ffi::syncobj::transfer( self.as_fd(), src_handle.into(), dst_handle.into(), src_point, dst_point, )?; Ok(()) } /// Signals one or more specific timeline syncobj points. fn syncobj_timeline_signal( &self, handles: &[syncobj::Handle], points: &[u64], ) -> io::Result<()> { ffi::syncobj::timeline_signal(self.as_fd(), bytemuck::cast_slice(handles), points)?; Ok(()) } /// Create a drm lease fn create_lease( &self, objects: &[RawResourceHandle], flags: u32, ) -> io::Result<(LeaseId, OwnedFd)> { let lease = ffi::mode::create_lease(self.as_fd(), bytemuck::cast_slice(objects), flags)?; Ok(( unsafe { NonZeroU32::new_unchecked(lease.lessee_id) }, unsafe { OwnedFd::from_raw_fd(lease.fd as RawFd) }, )) } /// List active lessees fn list_lessees(&self) -> io::Result> { let mut lessees = Vec::new(); ffi::mode::list_lessees(self.as_fd(), Some(&mut lessees))?; Ok(unsafe { transmute_vec_from_u32(lessees) }) } /// Revoke a previously issued drm lease fn revoke_lease(&self, lessee_id: LeaseId) -> io::Result<()> { ffi::mode::revoke_lease(self.as_fd(), lessee_id.get()) } /// Receive pending events fn receive_events(&self) -> io::Result where Self: Sized, { let mut event_buf: [u8; 1024] = [0; 1024]; let amount = rustix::io::read(self.as_fd(), &mut event_buf)?; Ok(Events::with_event_buf(event_buf, amount)) } } /// List of leased resources pub struct LeaseResources { /// leased crtcs pub crtcs: Vec, /// leased connectors pub connectors: Vec, /// leased planes pub planes: Vec, } /// Query lease resources pub fn get_lease(lease: D) -> io::Result { let mut crtcs = Vec::new(); let mut connectors = Vec::new(); let mut planes = Vec::new(); let mut objects = Vec::new(); ffi::mode::get_lease(lease.as_fd(), Some(&mut objects))?; let _ = ffi::mode::get_resources( lease.as_fd(), None, Some(&mut crtcs), Some(&mut connectors), None, )?; let _ = ffi::mode::get_plane_resources(lease.as_fd(), Some(&mut planes))?; unsafe { Ok(LeaseResources { crtcs: transmute_vec_from_u32::( crtcs .into_iter() .filter(|handle| objects.contains(handle)) .collect(), ), connectors: transmute_vec_from_u32::( connectors .into_iter() .filter(|handle| objects.contains(handle)) .collect(), ), planes: transmute_vec_from_u32::( planes .into_iter() .filter(|handle| objects.contains(handle)) .collect(), ), }) } } bitflags::bitflags! { /// Flags to alter the behaviour of a page flip /// /// Limited to the values in [`ffi::drm_sys::DRM_MODE_PAGE_FLIP_FLAGS`], /// minus [`ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET`] bits which are /// passed through [`PageFlipTarget`]. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct PageFlipFlags : u32 { /// Request a vblank event on page flip const EVENT = ffi::drm_sys::DRM_MODE_PAGE_FLIP_EVENT; /// Request page flip as soon as possible, not waiting for vblank const ASYNC = ffi::drm_sys::DRM_MODE_PAGE_FLIP_ASYNC; } } /// Target to alter the sequence of page flips /// /// These represent the [`ffi::drm_sys::DRM_MODE_PAGE_FLIP_TARGET`] bits /// of [`PageFlipFlags`] wrapped in a regular `enum` due to their /// mutual-exclusiveness. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum PageFlipTarget { /// Absolute Vblank Sequence Absolute(u32), /// Relative Vblank Sequence (to the current, when calling) Relative(u32), } /// Iterator over [`Event`]s of a device. Create via [`Device::receive_events()`]. pub struct Events { event_buf: [u8; 1024], amount: usize, i: usize, } impl Events { /// Create [`Event`]s iterator from buffer read using something other than /// [`Device::receive_events()`]. pub fn with_event_buf(event_buf: [u8; 1024], amount: usize) -> Self { Events { event_buf, amount, i: 0, } } } /// An event from a device. pub enum Event { /// A vblank happened Vblank(VblankEvent), /// A page flip happened PageFlip(PageFlipEvent), /// Unknown event, raw data provided Unknown(Vec), } /// Vblank event pub struct VblankEvent { /// sequence of the frame pub frame: u32, /// time at which the vblank occurred pub time: Duration, /// crtc that did throw the event pub crtc: crtc::Handle, /// user data that was passed to wait_vblank pub user_data: usize, } /// Page Flip event pub struct PageFlipEvent { /// sequence of the frame pub frame: u32, /// duration between events pub duration: Duration, /// crtc that did throw the event pub crtc: crtc::Handle, } impl Iterator for Events { type Item = Event; fn next(&mut self) -> Option { if self.amount > 0 && self.i < self.amount { let event = unsafe { &*(self.event_buf.as_ptr().add(self.i) as *const ffi::drm_event) }; self.i += event.length as usize; match event.type_ { ffi::DRM_EVENT_VBLANK => { let vblank_event = unsafe { &*(event as *const _ as *const ffi::drm_event_vblank) }; Some(Event::Vblank(VblankEvent { frame: vblank_event.sequence, time: Duration::new( vblank_event.tv_sec as u64, vblank_event.tv_usec * 1000, ), #[allow(clippy::unnecessary_cast)] crtc: from_u32(vblank_event.crtc_id as u32).unwrap(), user_data: vblank_event.user_data as usize, })) } ffi::DRM_EVENT_FLIP_COMPLETE => { let vblank_event = unsafe { &*(event as *const _ as *const ffi::drm_event_vblank) }; Some(Event::PageFlip(PageFlipEvent { frame: vblank_event.sequence, duration: Duration::new( vblank_event.tv_sec as u64, vblank_event.tv_usec * 1000, ), crtc: from_u32(if vblank_event.crtc_id != 0 { vblank_event.crtc_id } else { vblank_event.user_data as u32 }) .unwrap(), })) } _ => Some(Event::Unknown( self.event_buf[self.i - (event.length as usize)..self.i].to_vec(), )), } } else { None } } } /// The set of [`ResourceHandles`] that a /// [`Device`] exposes. Excluding Plane resources. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct ResourceHandles { /// Set of [`framebuffer::Handle`] pub fbs: Vec, /// Set of [`crtc::Handle`] pub crtcs: Vec, /// Set of [`connector::Handle`] pub connectors: Vec, /// Set of [`encoder::Handle`] pub encoders: Vec, width: (u32, u32), height: (u32, u32), } impl ResourceHandles { /// Returns the set of [`connector::Handle`] pub fn connectors(&self) -> &[connector::Handle] { &self.connectors } /// Returns the set of [`encoder::Handle`] pub fn encoders(&self) -> &[encoder::Handle] { &self.encoders } /// Returns the set of [`crtc::Handle`] pub fn crtcs(&self) -> &[crtc::Handle] { &self.crtcs } /// Returns the set of [`framebuffer::Handle`] pub fn framebuffers(&self) -> &[framebuffer::Handle] { &self.fbs } /// Returns the supported minimum and maximum width for framebuffers pub fn supported_fb_width(&self) -> impl RangeBounds { self.width.0..=self.width.1 } /// Returns the supported minimum and maximum height for framebuffers pub fn supported_fb_height(&self) -> impl RangeBounds { self.height.0..=self.height.1 } /// Apply a filter the all crtcs of these resources, resulting in a list of crtcs allowed. pub fn filter_crtcs(&self, filter: CrtcListFilter) -> Vec { self.crtcs .iter() .enumerate() .filter(|&(n, _)| (1 << n) & filter.0 != 0) .map(|(_, &e)| e) .collect() } } #[derive(Debug, Clone, Copy, PartialEq, Eq)] /// A filter that can be used with a [`ResourceHandles`] to determine the set of /// Crtcs that can attach to a specific encoder. pub struct CrtcListFilter(u32); /// Resolution and timing information for a display mode. #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq, bytemuck::TransparentWrapper)] pub struct Mode { // We're using the FFI struct because the DRM API expects it when giving it // to a CRTC or creating a blob from it. Rather than rearranging the fields // to convert to/from an abstracted type, just use the raw object. mode: ffi::drm_mode_modeinfo, } impl Mode { /// Returns the name of this mode. pub fn name(&self) -> &std::ffi::CStr { unsafe { std::ffi::CStr::from_ptr(&self.mode.name[0] as _) } } /// Returns the clock speed of this mode. pub fn clock(&self) -> u32 { self.mode.clock } /// Returns the size (resolution) of the mode. pub fn size(&self) -> (u16, u16) { (self.mode.hdisplay, self.mode.vdisplay) } /// Returns the horizontal sync start, end, and total. pub fn hsync(&self) -> (u16, u16, u16) { (self.mode.hsync_start, self.mode.hsync_end, self.mode.htotal) } /// Returns the vertical sync start, end, and total. pub fn vsync(&self) -> (u16, u16, u16) { (self.mode.vsync_start, self.mode.vsync_end, self.mode.vtotal) } /// Returns the horizontal skew of this mode. pub fn hskew(&self) -> u16 { self.mode.hskew } /// Returns the vertical scan of this mode. pub fn vscan(&self) -> u16 { self.mode.vscan } /// Returns the vertical refresh rate of this mode pub fn vrefresh(&self) -> u32 { self.mode.vrefresh } /// Returns the bitmask of this mode pub fn mode_type(&self) -> ModeTypeFlags { ModeTypeFlags::from_bits_truncate(self.mode.type_) } /// Returns the flags of this mode pub fn flags(&self) -> ModeFlags { ModeFlags::from_bits_truncate(self.mode.flags) } } impl From for Mode { fn from(raw: ffi::drm_mode_modeinfo) -> Mode { Mode { mode: raw } } } impl From for ffi::drm_mode_modeinfo { fn from(mode: Mode) -> Self { mode.mode } } impl fmt::Debug for Mode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Mode") .field("name", &self.name()) .field("clock", &self.clock()) .field("size", &self.size()) .field("hsync", &self.hsync()) .field("vsync", &self.vsync()) .field("hskew", &self.hskew()) .field("vscan", &self.vscan()) .field("vrefresh", &self.vrefresh()) .field("mode_type", &self.mode_type()) .finish() } } bitflags::bitflags! { /// Display mode type flags #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ModeTypeFlags : u32 { /// Builtin mode type #[deprecated] const BUILTIN = ffi::DRM_MODE_TYPE_BUILTIN; /// CLOCK_C mode type #[deprecated] const CLOCK_C = ffi::DRM_MODE_TYPE_CLOCK_C; /// CRTC_C mode type #[deprecated] const CRTC_C = ffi::DRM_MODE_TYPE_CRTC_C; /// Preferred mode const PREFERRED = ffi::DRM_MODE_TYPE_PREFERRED; /// Default mode #[deprecated] const DEFAULT = ffi::DRM_MODE_TYPE_DEFAULT; /// User defined mode type const USERDEF = ffi::DRM_MODE_TYPE_USERDEF; /// Mode created by driver const DRIVER = ffi::DRM_MODE_TYPE_DRIVER; /// Bitmask of all valid (non-deprecated) mode type flags const ALL = ffi::DRM_MODE_TYPE_ALL; } } bitflags::bitflags! { /// Display mode flags #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ModeFlags: u32 { /// PHSYNC flag const PHSYNC = ffi::DRM_MODE_FLAG_PHSYNC; /// NHSYNC flag const NHSYNC = ffi::DRM_MODE_FLAG_NHSYNC; /// PVSYNC flag const PVSYNC = ffi::DRM_MODE_FLAG_PVSYNC; /// NVSYNC flag const NVSYNC = ffi::DRM_MODE_FLAG_NVSYNC; /// Interlace flag const INTERLACE = ffi::DRM_MODE_FLAG_INTERLACE; /// DBLSCAN flag const DBLSCAN = ffi::DRM_MODE_FLAG_DBLSCAN; /// CSYNC flag const CSYNC = ffi::DRM_MODE_FLAG_CSYNC; /// PCSYNC flag const PCSYNC = ffi::DRM_MODE_FLAG_PCSYNC; /// NCSYNC flag const NCSYNC = ffi::DRM_MODE_FLAG_NCSYNC; /// HSKEW flag const HSKEW = ffi::DRM_MODE_FLAG_HSKEW; #[deprecated] /// BCAST flag const BCAST = ffi::DRM_MODE_FLAG_BCAST; #[deprecated] /// PIXMUX flag const PIXMUX = ffi::DRM_MODE_FLAG_PIXMUX; /// DBLCLK flag const DBLCLK = ffi::DRM_MODE_FLAG_DBLCLK; /// CLKDIV2 flag const CLKDIV2 = ffi::DRM_MODE_FLAG_CLKDIV2; /// Stereo 3D mode utilizing frame packing const _3D_FRAME_PACKING = ffi::DRM_MODE_FLAG_3D_FRAME_PACKING; /// Stereo 3D mode utilizing alternating fields const _3D_FIELD_ALTERNATIVE = ffi::DRM_MODE_FLAG_3D_FIELD_ALTERNATIVE; /// Stereo 3D mode utilizing alternating lines const _3D_LINE_ALTERNATIVE = ffi::DRM_MODE_FLAG_3D_LINE_ALTERNATIVE; /// Stereo 3D mode utilizing side by side full size image const _3D_SIDE_BY_SIDE_FULL = ffi::DRM_MODE_FLAG_3D_SIDE_BY_SIDE_FULL; /// Stereo 3D mode utilizing depth images const _3D_L_DEPTH = ffi::DRM_MODE_FLAG_3D_L_DEPTH; /// Stereo 3D mode utilizing depth images const _3D_L_DEPTH_GFX_GFX_DEPTH = ffi::DRM_MODE_FLAG_3D_L_DEPTH_GFX_GFX_DEPTH; /// Stereo 3D mode utilizing top and bottom images const _3D_TOP_AND_BOTTOM = ffi::DRM_MODE_FLAG_3D_TOP_AND_BOTTOM; /// Stereo 3D mode utilizing side by side half size image const _3D_SIDE_BY_SIDE_HALF = ffi::DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF; } } /// Type of a plane #[repr(u32)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum PlaneType { /// Overlay plane Overlay = ffi::DRM_PLANE_TYPE_OVERLAY, /// Primary plane Primary = ffi::DRM_PLANE_TYPE_PRIMARY, /// Cursor plane Cursor = ffi::DRM_PLANE_TYPE_CURSOR, } /// Wrapper around a set of property IDs and their raw values. #[derive(Debug, Clone)] pub struct PropertyValueSet { prop_ids: Vec, prop_vals: Vec, } impl PropertyValueSet { /// Returns a HashMap mapping property names to info pub fn as_hashmap(&self, device: &impl Device) -> io::Result> { let mut map = HashMap::new(); for id in self.prop_ids.iter() { let info = device.get_property(*id)?; let name = info.name().to_str().unwrap().to_owned(); map.insert(name, info); } Ok(map) } /// Returns a pair representing a set of [`property::Handle`] and their raw values pub fn as_props_and_values(&self) -> (&[property::Handle], &[property::RawValue]) { (&self.prop_ids, &self.prop_vals) } /// Returns iterator over pairs representing a set of [`property::Handle`] and their raw values pub fn iter(&self) -> impl Iterator { self.into_iter() } } impl<'a> IntoIterator for &'a PropertyValueSet { type Item = (&'a property::Handle, &'a property::RawValue); type IntoIter = Zip, std::slice::Iter<'a, property::RawValue>>; fn into_iter(self) -> Self::IntoIter { self.prop_ids.iter().zip(self.prop_vals.iter()) } } impl IntoIterator for PropertyValueSet { type Item = (property::Handle, property::RawValue); type IntoIter = Zip, std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.prop_ids.into_iter().zip(self.prop_vals) } } /// Describes a rectangular region of a buffer #[repr(transparent)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] pub struct ClipRect(ffi::drm_sys::drm_clip_rect); impl ClipRect { /// Create a new clipping rectangle. pub fn new(x1: u16, y1: u16, x2: u16, y2: u16) -> Self { Self(ffi::drm_sys::drm_clip_rect { x1, y1, x2, y2 }) } /// Get the X coordinate of the top left corner of the rectangle. pub fn x1(self) -> u16 { self.0.x1 } /// Get the Y coordinate of the top left corner of the rectangle. pub fn y1(self) -> u16 { self.0.y1 } /// Get the X coordinate of the bottom right corner of the rectangle pub fn x2(self) -> u16 { self.0.x2 } /// Get the Y coordinate of the bottom right corner of the rectangle. pub fn y2(self) -> u16 { self.0.y2 } } bitflags::bitflags! { /// Commit flags for atomic mode setting /// /// Limited to the values in [`ffi::drm_sys::DRM_MODE_ATOMIC_FLAGS`]. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct AtomicCommitFlags : u32 { /// Generate a page flip event, when the changes are applied const PAGE_FLIP_EVENT = ffi::drm_sys::DRM_MODE_PAGE_FLIP_EVENT; /// Request page flip when the changes are applied, not waiting for vblank const PAGE_FLIP_ASYNC = ffi::drm_sys::DRM_MODE_PAGE_FLIP_ASYNC; /// Test only validity of the request, do not actually apply the requested changes const TEST_ONLY = ffi::drm_sys::DRM_MODE_ATOMIC_TEST_ONLY; /// Do not block on the request and return early const NONBLOCK = ffi::drm_sys::DRM_MODE_ATOMIC_NONBLOCK; /// Allow the changes to trigger a modeset, if necessary /// /// Changes requiring a modeset are rejected otherwise. const ALLOW_MODESET = ffi::drm_sys::DRM_MODE_ATOMIC_ALLOW_MODESET; } } bitflags::bitflags! { /// Mode property flags #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ModePropFlags : u32 { /// Do not use #[deprecated] const PENDING = ffi::DRM_MODE_PROP_PENDING; /// Non-extended types: legacy bitmask, one bit per type: const LEGACY_TYPE = ffi::DRM_MODE_PROP_LEGACY_TYPE; /// An unsigned integer that has a min and max value const RANGE = ffi::DRM_MODE_PROP_RANGE; /// Set when this property is informational only and cannot be modified const IMMUTABLE = ffi::DRM_MODE_PROP_IMMUTABLE; /// Enumerated type with text strings const ENUM = ffi::DRM_MODE_PROP_ENUM; /// A chunk of binary data that must be acquired const BLOB = ffi::DRM_MODE_PROP_BLOB; /// Bitmask of enumerated types const BITMASK = ffi::DRM_MODE_PROP_BITMASK; /// Extended-types: rather than continue to consume a bit per type, /// grab a chunk of the bits to use as integer type id. const EXTENDED_TYPE = ffi::DRM_MODE_PROP_EXTENDED_TYPE; /// A DRM object that can have a specific type /// /// See `ffi::DRM_MODE_OBJECT_*` for specific types. const OBJECT = ffi::DRM_MODE_PROP_OBJECT; /// A signed integer that has a min and max value const SIGNED_RANGE = ffi::DRM_MODE_PROP_SIGNED_RANGE; /// the [`Self::ATOMIC`] flag is used to hide properties from userspace that /// is not aware of atomic properties. This is mostly to work around /// older userspace (DDX drivers) that read/write each prop they find, /// witout being aware that this could be triggering a lengthy modeset. const ATOMIC = ffi::DRM_MODE_PROP_ATOMIC; } } bitflags::bitflags! { /// Planar framebuffer flags #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct FbCmd2Flags : u32 { /// For interlaced framebuffers const INTERLACED = ffi::DRM_MODE_FB_INTERLACED; /// Enables .modifier const MODIFIERS = ffi::DRM_MODE_FB_MODIFIERS; } } drm-0.11.1/src/control/plane.rs000064400000000000000000000052051046102023000144040ustar 00000000000000//! # Plane //! //! Attachment point for a Framebuffer. //! //! A Plane is a resource that can have a framebuffer attached to it, either for //! hardware compositing or displaying directly to a screen. There are three //! types of planes available for use: //! //! * Primary - A CRTC's built-in plane. When attaching a framebuffer to a CRTC, //! it is actually being attached to this kind of plane. //! //! * Overlay - Can be overlaid on top of a primary plane, utilizing extremely //! fast hardware compositing. //! //! * Cursor - Similar to an overlay plane, these are typically used to display //! cursor type objects. use crate::control; use drm_ffi as ffi; /// A handle to a plane #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(control::RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} impl From for control::RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: control::RawResourceHandle) -> Self { Handle(handle) } } impl control::ResourceHandle for Handle { const FFI_TYPE: u32 = ffi::DRM_MODE_OBJECT_PLANE; } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("plane::Handle").field(&self.0).finish() } } /// Information about a plane #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Info { pub(crate) handle: Handle, pub(crate) crtc: Option, pub(crate) fb: Option, pub(crate) pos_crtcs: u32, pub(crate) formats: Vec, } impl Info { /// Returns the handle to this plane. pub fn handle(&self) -> Handle { self.handle } /// Returns the CRTC this plane is attached to. pub fn crtc(&self) -> Option { self.crtc } /// Returns a filter for supported crtcs of this plane. /// /// Use with [`control::ResourceHandles::filter_crtcs`] /// to receive a list of crtcs. pub fn possible_crtcs(&self) -> control::CrtcListFilter { control::CrtcListFilter(self.pos_crtcs) } /// Returns the framebuffer this plane is attached to. pub fn framebuffer(&self) -> Option { self.fb } /// Returns the formats this plane supports. pub fn formats(&self) -> &[u32] { &self.formats } } drm-0.11.1/src/control/property.rs000064400000000000000000000242511046102023000151730ustar 00000000000000//! # Property //! //! A property of a modesetting resource. //! //! All modesetting resources have a set of properties that have values that //! can be modified. These properties are modesetting resources themselves, and //! may even have their own set of properties. //! //! Properties may have mutable values attached to them. These can be changed by //! either changing the state of a resource (thereby affecting the property), //! directly changing the property value itself, or by batching property changes //! together and executing them all atomically. use crate::control::{RawResourceHandle, ResourceHandle}; use drm_ffi as ffi; /// A raw property value that does not have a specific property type pub type RawValue = u64; /// A handle to a property #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} impl From for RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: RawResourceHandle) -> Self { Handle(handle) } } impl ResourceHandle for Handle { const FFI_TYPE: u32 = ffi::DRM_MODE_OBJECT_PROPERTY; } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("property::Handle").field(&self.0).finish() } } /// Information about a property #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Info { pub(crate) handle: Handle, pub(crate) val_type: ValueType, pub(crate) mutable: bool, pub(crate) atomic: bool, pub(crate) info: ffi::drm_mode_get_property, } impl Info { /// Returns the handle to this property. pub fn handle(&self) -> Handle { self.handle } /// Returns the name of this property. pub fn name(&self) -> &std::ffi::CStr { unsafe { std::ffi::CStr::from_ptr(&self.info.name[0] as _) } } /// Returns the ValueType of this property. pub fn value_type(&self) -> ValueType { self.val_type.clone() } /// Returns whether this property is mutable. pub fn mutable(&self) -> bool { self.mutable } /// Returns whether this property can be atomically updated. pub fn atomic(&self) -> bool { self.atomic } } /// Describes the types of value that a property uses. #[allow(clippy::upper_case_acronyms)] #[allow(clippy::large_enum_variant)] #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum ValueType { /// A catch-all for any unknown types Unknown, /// A True or False type Boolean, /// An unsigned integer that has a min and max value UnsignedRange(u64, u64), /// A signed integer that has a min and max value SignedRange(i64, i64), /// A set of values that are mutually exclusive Enum(EnumValues), /// A set of values that can be combined Bitmask, /// A chunk of binary data that must be acquired Blob, /// A non-specific DRM object Object, /// A CRTC object CRTC, /// A Connector object Connector, /// An Encoder object Encoder, /// A Framebuffer object Framebuffer, /// A Plane object Plane, /// A Property object Property, } impl ValueType { /// Given a [`RawValue`], convert it into a specific [`Value`] pub fn convert_value(&self, value: RawValue) -> Value { match self { ValueType::Unknown => Value::Unknown(value), ValueType::Boolean => Value::Boolean(value != 0), ValueType::UnsignedRange(_, _) => Value::UnsignedRange(value), ValueType::SignedRange(_, _) => Value::SignedRange(value as i64), ValueType::Enum(values) => Value::Enum(values.get_value_from_raw_value(value)), ValueType::Bitmask => Value::Bitmask(value), ValueType::Blob => Value::Blob(value), ValueType::Object => Value::Object(bytemuck::cast(value as u32)), ValueType::CRTC => Value::CRTC(bytemuck::cast(value as u32)), ValueType::Connector => Value::Connector(bytemuck::cast(value as u32)), ValueType::Encoder => Value::Encoder(bytemuck::cast(value as u32)), ValueType::Framebuffer => Value::Framebuffer(bytemuck::cast(value as u32)), ValueType::Plane => Value::Plane(bytemuck::cast(value as u32)), ValueType::Property => Value::Property(bytemuck::cast(value as u32)), } } } /// The value of a property, in a typed format #[allow(missing_docs)] #[allow(clippy::upper_case_acronyms)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum Value<'a> { /// Unknown value Unknown(RawValue), /// Boolean value Boolean(bool), /// Unsigned range value UnsignedRange(u64), /// Signed range value SignedRange(i64), /// Enum Value Enum(Option<&'a EnumValue>), /// Bitmask value Bitmask(u64), /// Opaque (blob) value Blob(u64), /// Unknown object value Object(Option), /// Crtc object value CRTC(Option), /// Connector object value Connector(Option), /// Encoder object value Encoder(Option), /// Framebuffer object value Framebuffer(Option), /// Plane object value Plane(Option), /// Property object value Property(Option), } impl<'a> From> for RawValue { fn from(value: Value<'a>) -> Self { match value { Value::Unknown(x) => x, Value::Boolean(true) => 1, Value::Boolean(false) => 0, Value::UnsignedRange(x) => x, Value::SignedRange(x) => x as u64, Value::Enum(val) => val.map_or(0, EnumValue::value), Value::Bitmask(x) => x, Value::Blob(x) => x, Value::Object(x) => bytemuck::cast::<_, u32>(x) as u64, Value::CRTC(x) => bytemuck::cast::<_, u32>(x) as u64, Value::Connector(x) => bytemuck::cast::<_, u32>(x) as u64, Value::Encoder(x) => bytemuck::cast::<_, u32>(x) as u64, Value::Framebuffer(x) => bytemuck::cast::<_, u32>(x) as u64, Value::Plane(x) => bytemuck::cast::<_, u32>(x) as u64, Value::Property(x) => bytemuck::cast::<_, u32>(x) as u64, } } } macro_rules! match_variant { ($this:ident, $variant:ident) => { if let Self::$variant(v) = *$this { Some(v) } else { None } }; } impl<'a> Value<'a> { /// Boolean value pub fn as_boolean(&self) -> Option { match_variant!(self, Boolean) } /// Unsigned range value pub fn as_unsigned_range(&self) -> Option { match_variant!(self, UnsignedRange) } /// Signed range value pub fn as_signed_range(&self) -> Option { match_variant!(self, SignedRange) } /// Enum Value pub fn as_enum(&self) -> Option<&'a EnumValue> { match_variant!(self, Enum).flatten() } /// Bitmask value pub fn as_bitmask(&self) -> Option { match_variant!(self, Bitmask) } /// Opaque (blob) value pub fn as_blob(&self) -> Option { match_variant!(self, Blob) } /// Unknown object value pub fn as_object(&self) -> Option { match_variant!(self, Object).flatten() } /// Crtc object value pub fn as_crtc(&self) -> Option { match_variant!(self, CRTC).flatten() } /// Connector object value pub fn as_connector(&self) -> Option { match_variant!(self, Connector).flatten() } /// Encoder object value pub fn as_encoder(&self) -> Option { match_variant!(self, Encoder).flatten() } /// Framebuffer object value pub fn as_framebuffer(&self) -> Option { match_variant!(self, Framebuffer).flatten() } /// Plane object value pub fn as_plane(&self) -> Option { match_variant!(self, Plane).flatten() } /// Property object value pub fn as_property(&self) -> Option { match_variant!(self, Property).flatten() } } /// A single value of [`ValueType::Enum`] type #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq, bytemuck::TransparentWrapper)] pub struct EnumValue(ffi::drm_mode_property_enum); impl EnumValue { /// Returns the [`RawValue`] of this value pub fn value(&self) -> RawValue { self.0.value } /// Returns the name of this value pub fn name(&self) -> &std::ffi::CStr { unsafe { std::ffi::CStr::from_ptr(&self.0.name[0] as _) } } } impl From for EnumValue { fn from(inner: ffi::drm_mode_property_enum) -> Self { EnumValue(inner) } } impl std::fmt::Debug for EnumValue { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_struct("EnumValue") .field("value", &self.value()) .field("name", &self.name()) .finish() } } /// A set of [`EnumValue`]s for a single property #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct EnumValues { pub(crate) values: Vec, pub(crate) enums: Vec, } impl EnumValues { /// Returns a tuple containing slices to the [`RawValue`]s and the [`EnumValue`]s pub fn values(&self) -> (&[RawValue], &[EnumValue]) { (&self.values, &self.enums) } /// Returns an [`EnumValue`] for a [`RawValue`], or [`None`] if `value` is /// not part of this [`EnumValues`]. pub fn get_value_from_raw_value(&self, value: RawValue) -> Option<&EnumValue> { let (values, enums) = self.values(); let index = if values.get(value as usize) == Some(&value) { // Early-out: indices match values value as usize } else { values.iter().position(|&v| v == value)? }; Some(&enums[index]) } } drm-0.11.1/src/control/syncobj.rs000064400000000000000000000031651046102023000147570ustar 00000000000000//! # SyncObj //! //! A SyncObj is a binding point for the DRM subsystem to attach single-use fences which are //! signalled when a device task completes. They are typically provided as optional arguments to //! device-specific command submission IOCTLs. In practice, they are used to implement Vulkan //! fence objects. //! //! After a submission IOCTL sets a fence into a SyncObj, it may be exported as a sync file //! descriptor. This sync file may be epoll()'d for EPOLLIN to implement asynchronous waiting on //! multiple events. This file descriptor is also compatible with [`tokio::io::unix::AsyncFd`] for //! Rust async/await integration. //! //! [`tokio::io::unix::AsyncFd`]: use crate::control; /// A handle to a specific syncobj #[repr(transparent)] #[derive(Copy, Clone, Hash, PartialEq, Eq)] pub struct Handle(control::RawResourceHandle); // Safety: Handle is repr(transparent) over NonZeroU32 unsafe impl bytemuck::ZeroableInOption for Handle {} unsafe impl bytemuck::PodInOption for Handle {} unsafe impl bytemuck::NoUninit for Handle {} impl From for control::RawResourceHandle { fn from(handle: Handle) -> Self { handle.0 } } impl From for u32 { fn from(handle: Handle) -> Self { handle.0.into() } } impl From for Handle { fn from(handle: control::RawResourceHandle) -> Self { Handle(handle) } } impl std::fmt::Debug for Handle { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.debug_tuple("syncobj::Handle").field(&self.0).finish() } } drm-0.11.1/src/lib.rs000064400000000000000000000267501046102023000124030ustar 00000000000000//! A safe interface to the Direct Rendering Manager subsystem found in various //! operating systems. //! //! # Summary //! //! The Direct Rendering Manager (DRM) is subsystem found in various operating //! systems that exposes graphical functionality to userspace processes. It can //! be used to send data and commands to a GPU driver that implements the //! interface. //! //! Userspace processes can access the DRM by opening a 'device node' (usually //! found in `/dev/dri/*`) and using various `ioctl` commands on the open file //! descriptor. Most processes use the libdrm library (part of the mesa project) //! to execute these commands. This crate takes a more direct approach, //! bypassing libdrm and executing the commands directly and doing minimal //! abstraction to keep the interface safe. //! //! While the DRM subsystem exposes many powerful GPU interfaces, it is not //! recommended for rendering or GPGPU operations. There are many standards made //! for these use cases, and they are far more fitting for those sort of tasks. //! //! ## Usage //! //! To begin using this crate, the [`Device`] trait must be //! implemented. See the trait's [example section](trait@Device#example) for //! details on how to implement it. //! #![warn(missing_docs)] pub(crate) mod util; pub mod buffer; pub mod control; use std::ffi::{OsStr, OsString}; use std::time::Duration; use std::{ io, os::unix::{ffi::OsStringExt, io::AsFd}, }; use rustix::io::Errno; use crate::util::*; pub use drm_ffi::{DRM_CLOEXEC as CLOEXEC, DRM_RDWR as RDWR}; /// This trait should be implemented by any object that acts as a DRM device. It /// is a prerequisite for using any DRM functionality. /// /// This crate does not provide a concrete device object due to the various ways /// it can be implemented. The user of this crate is expected to implement it /// themselves and derive this trait as necessary. The example below /// demonstrates how to do this using a small wrapper. /// /// # Example /// /// ``` /// use drm::Device; /// /// use std::fs::File; /// use std::fs::OpenOptions; /// /// use std::os::unix::io::AsFd; /// use std::os::unix::io::BorrowedFd; /// /// #[derive(Debug)] /// /// A simple wrapper for a device node. /// struct Card(File); /// /// /// Implementing [`AsFd`] is a prerequisite to implementing the traits found /// /// in this crate. Here, we are just calling [`File::as_fd()`] on the inner /// /// [`File`]. /// impl AsFd for Card { /// fn as_fd(&self) -> BorrowedFd<'_> { /// self.0.as_fd() /// } /// } /// /// /// With [`AsFd`] implemented, we can now implement [`drm::Device`]. /// impl Device for Card {} /// /// impl Card { /// /// Simple helper method for opening a [`Card`]. /// fn open() -> Self { /// let mut options = OpenOptions::new(); /// options.read(true); /// options.write(true); /// /// // The normal location of the primary device node on Linux /// Card(options.open("/dev/dri/card0").unwrap()) /// } /// } /// ``` pub trait Device: AsFd { /// Acquires the DRM Master lock for this process. /// /// # Notes /// /// Acquiring the DRM Master is done automatically when the primary device /// node is opened. If you opened the primary device node and did not /// acquire the lock, another process likely has the lock. /// /// This function is only available to processes with CAP_SYS_ADMIN /// privileges (usually as root) fn acquire_master_lock(&self) -> io::Result<()> { drm_ffi::auth::acquire_master(self.as_fd())?; Ok(()) } /// Releases the DRM Master lock for another process to use. fn release_master_lock(&self) -> io::Result<()> { drm_ffi::auth::release_master(self.as_fd())?; Ok(()) } /// Generates an [`AuthToken`] for this process. #[deprecated(note = "Consider opening a render node instead.")] fn generate_auth_token(&self) -> io::Result { let token = drm_ffi::auth::get_magic_token(self.as_fd())?; Ok(AuthToken(token.magic)) } /// Authenticates an [`AuthToken`] from another process. fn authenticate_auth_token(&self, token: AuthToken) -> io::Result<()> { drm_ffi::auth::auth_magic_token(self.as_fd(), token.0)?; Ok(()) } /// Requests the driver to expose or hide certain capabilities. See /// [`ClientCapability`] for more information. fn set_client_capability(&self, cap: ClientCapability, enable: bool) -> io::Result<()> { drm_ffi::set_capability(self.as_fd(), cap as u64, enable)?; Ok(()) } /// Gets the bus ID of this device. fn get_bus_id(&self) -> io::Result { let mut buffer = Vec::new(); let _ = drm_ffi::get_bus_id(self.as_fd(), Some(&mut buffer))?; let bus_id = OsString::from_vec(buffer); Ok(bus_id) } /// Check to see if our [`AuthToken`] has been authenticated /// by the DRM Master fn authenticated(&self) -> io::Result { let client = drm_ffi::get_client(self.as_fd(), 0)?; Ok(client.auth == 1) } /// Gets the value of a capability. fn get_driver_capability(&self, cap: DriverCapability) -> io::Result { let cap = drm_ffi::get_capability(self.as_fd(), cap as u64)?; Ok(cap.value) } /// # Possible errors: /// - `EFAULT`: Kernel could not copy fields into userspace #[allow(missing_docs)] fn get_driver(&self) -> io::Result { let mut name = Vec::new(); let mut date = Vec::new(); let mut desc = Vec::new(); let _ = drm_ffi::get_version( self.as_fd(), Some(&mut name), Some(&mut date), Some(&mut desc), )?; let name = OsString::from_vec(unsafe { transmute_vec(name) }); let date = OsString::from_vec(unsafe { transmute_vec(date) }); let desc = OsString::from_vec(unsafe { transmute_vec(desc) }); let driver = Driver { name, date, desc }; Ok(driver) } /// Waits for a vblank. fn wait_vblank( &self, target_sequence: VblankWaitTarget, flags: VblankWaitFlags, high_crtc: u32, user_data: usize, ) -> io::Result { use drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_HIGH_CRTC_MASK; use drm_ffi::_DRM_VBLANK_HIGH_CRTC_SHIFT; let high_crtc_mask = _DRM_VBLANK_HIGH_CRTC_MASK >> _DRM_VBLANK_HIGH_CRTC_SHIFT; if (high_crtc & !high_crtc_mask) != 0 { return Err(Errno::INVAL.into()); } let (sequence, wait_type) = match target_sequence { VblankWaitTarget::Absolute(n) => { (n, drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_ABSOLUTE) } VblankWaitTarget::Relative(n) => { (n, drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_RELATIVE) } }; let type_ = wait_type | (high_crtc << _DRM_VBLANK_HIGH_CRTC_SHIFT) | flags.bits(); let reply = drm_ffi::wait_vblank(self.as_fd(), type_, sequence, user_data)?; let time = match (reply.tval_sec, reply.tval_usec) { (0, 0) => None, (sec, usec) => Some(Duration::new(sec as u64, (usec * 1000) as u32)), }; Ok(VblankWaitReply { frame: reply.sequence, time, }) } } /// An authentication token, unique to the file descriptor of the device. /// /// This token can be sent to another process that owns the DRM Master lock to /// allow unprivileged use of the device, such as rendering. /// /// # Deprecation Notes /// /// This method of authentication is somewhat deprecated. Accessing unprivileged /// functionality is best done by opening a render node. However, some other /// processes may still use this method of authentication. Therefore, we still /// provide functionality for generating and authenticating these tokens. #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct AuthToken(u32); /// Driver version of a device. #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct Driver { /// Name of the driver pub name: OsString, /// Date driver was published pub date: OsString, /// Driver description pub desc: OsString, } impl Driver { /// Name of driver pub fn name(&self) -> &OsStr { self.name.as_ref() } /// Date driver was published pub fn date(&self) -> &OsStr { self.date.as_ref() } /// Driver description pub fn description(&self) -> &OsStr { self.desc.as_ref() } } /// Used to check which capabilities your graphics driver has. #[allow(clippy::upper_case_acronyms)] #[repr(u64)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum DriverCapability { /// DumbBuffer support for scanout DumbBuffer = drm_ffi::DRM_CAP_DUMB_BUFFER as u64, /// Unknown VBlankHighCRTC = drm_ffi::DRM_CAP_VBLANK_HIGH_CRTC as u64, /// Preferred depth to use for dumb buffers DumbPreferredDepth = drm_ffi::DRM_CAP_DUMB_PREFERRED_DEPTH as u64, /// Unknown DumbPreferShadow = drm_ffi::DRM_CAP_DUMB_PREFER_SHADOW as u64, /// PRIME handles are supported Prime = drm_ffi::DRM_CAP_PRIME as u64, /// Unknown MonotonicTimestamp = drm_ffi::DRM_CAP_TIMESTAMP_MONOTONIC as u64, /// Asynchronous page flipping support ASyncPageFlip = drm_ffi::DRM_CAP_ASYNC_PAGE_FLIP as u64, /// Width of cursor buffers CursorWidth = drm_ffi::DRM_CAP_CURSOR_WIDTH as u64, /// Height of cursor buffers CursorHeight = drm_ffi::DRM_CAP_CURSOR_HEIGHT as u64, /// Create framebuffers with modifiers AddFB2Modifiers = drm_ffi::DRM_CAP_ADDFB2_MODIFIERS as u64, /// Unknown PageFlipTarget = drm_ffi::DRM_CAP_PAGE_FLIP_TARGET as u64, /// Uses the CRTC's ID in vblank events CRTCInVBlankEvent = drm_ffi::DRM_CAP_CRTC_IN_VBLANK_EVENT as u64, /// SyncObj support SyncObj = drm_ffi::DRM_CAP_SYNCOBJ as u64, /// Timeline SyncObj support TimelineSyncObj = drm_ffi::DRM_CAP_SYNCOBJ_TIMELINE as u64, } /// Used to enable/disable capabilities for the process. #[repr(u64)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum ClientCapability { /// The driver provides 3D screen control Stereo3D = drm_ffi::DRM_CLIENT_CAP_STEREO_3D as u64, /// The driver provides more plane types for modesetting UniversalPlanes = drm_ffi::DRM_CLIENT_CAP_UNIVERSAL_PLANES as u64, /// The driver provides atomic modesetting Atomic = drm_ffi::DRM_CLIENT_CAP_ATOMIC as u64, } /// Used to specify a vblank sequence to wait for #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub enum VblankWaitTarget { /// Wait for a specific vblank sequence number Absolute(u32), /// Wait for a given number of vblanks Relative(u32), } bitflags::bitflags! { /// Flags to alter the behaviour when waiting for a vblank #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct VblankWaitFlags : u32 { /// Send event instead of blocking const EVENT = drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_EVENT; /// If missed, wait for next vblank const NEXT_ON_MISS = drm_ffi::drm_vblank_seq_type::_DRM_VBLANK_NEXTONMISS; } } /// Data returned from a vblank wait #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct VblankWaitReply { frame: u32, time: Option, } impl VblankWaitReply { /// Sequence of the frame pub fn frame(&self) -> u32 { self.frame } /// Time at which the vblank occurred. [`None`] if an asynchronous event was /// requested pub fn time(&self) -> Option { self.time } } drm-0.11.1/src/util.rs000064400000000000000000000010721046102023000126000ustar 00000000000000//! Utilities used internally by this crate. use crate::control::{from_u32, RawResourceHandle}; pub unsafe fn transmute_vec(from: Vec) -> Vec { let mut from = std::mem::ManuallyDrop::new(from); Vec::from_raw_parts(from.as_mut_ptr() as *mut U, from.len(), from.capacity()) } pub unsafe fn transmute_vec_from_u32>(raw: Vec) -> Vec { if cfg!(debug_assertions) { raw.into_iter() .map(|handle| from_u32(handle).unwrap()) .collect() } else { transmute_vec(raw) } }