diff --git a/Cargo.lock b/Cargo.lock index 10616ab..96bd23d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,16 +1,18 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "ab_glyph_rasterizer" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7e4e8cf778db814365e46839949ca74df4efb10e87ba4913e6ec5967ef0285" +checksum = "d9fe5e32de01730eb1f6b7f5b51c17e03e2325bf40a74f754f04f130043affff" [[package]] name = "alsa" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91888c0eee9b75c72c7dfe71247d6bb7da2755420696fc507107c358431610f8" +checksum = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934" dependencies = [ "alsa-sys", "bitflags", @@ -20,48 +22,19 @@ dependencies = [ [[package]] name = "alsa-sys" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e95eaae787b8047ceefb94fd90682b03dd3f82f2d6fcaa83908c3814a8bef36" +checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527" dependencies = [ "libc", "pkg-config", ] -[[package]] -name = "andrew" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7f09f89872c2b6b29e319377b1fbe91c6f5947df19a25596e121cf19a7b35e" -dependencies = [ - "bitflags", - "line_drawing", - "rusttype 0.7.9", - "walkdir", - "xdg", - "xml-rs", -] - -[[package]] -name = "approx" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3" -dependencies = [ - "num-traits", -] - -[[package]] -name = "arrayvec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" - [[package]] name = "autocfg" -version = "0.1.7" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" @@ -98,15 +71,15 @@ checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" [[package]] name = "byteorder" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "cc" -version = "1.0.48" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52a465a666ca3d838ebbf08b241383421412fe7ebb463527bba275526d89f76" +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48" [[package]] name = "cfg-if" @@ -115,14 +88,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "chrono" -version = "0.4.10" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ + "libc", "num-integer", "num-traits", "time", + "winapi", ] [[package]] @@ -152,24 +133,24 @@ dependencies = [ [[package]] name = "dlib" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" +checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" dependencies = [ "libloading", ] [[package]] name = "downcast-rs" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dtoa" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" +checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" [[package]] name = "fake-simd" @@ -188,9 +169,9 @@ dependencies = [ [[package]] name = "fuzzy-matcher" -version = "0.3.1" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d860c6f043a7f367ffcbdb5833c36f7dc85fa8bc6e7898e2530f643ec90f9f3" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" dependencies = [ "thread_local", ] @@ -206,9 +187,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" [[package]] name = "lazy_static" @@ -218,9 +199,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.66" +version = "0.2.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" +checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" [[package]] name = "libdbus-sys" @@ -233,19 +214,19 @@ dependencies = [ [[package]] name = "libloading" -version = "0.5.2" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" dependencies = [ - "cc", + "cfg-if 1.0.0", "winapi", ] [[package]] name = "libpulse-binding" -version = "2.22.0" +version = "2.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce89ab17bd343b08bd4321c674ef1477d34f83be18b1ab2ee47a5e5fbee64a91" +checksum = "b2405f806801527dfb3d2b6d48a282cdebe9a1b41b0652e0d7b5bad81dbc700e" dependencies = [ "bitflags", "libc", @@ -257,9 +238,9 @@ dependencies = [ [[package]] name = "libpulse-sys" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fcfb56118765adba111da47e36278b77d00aebf822e4f014a832fbfa183a13b" +checksum = "cf17e9832643c4f320c42b7d78b2c0510f45aa5e823af094413b94e45076ba82" dependencies = [ "libc", "num-derive", @@ -268,20 +249,11 @@ dependencies = [ "winapi", ] -[[package]] -name = "line_drawing" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc7ad3d82c845bdb5dde34ffdcc7a5fb4d2996e1e1ee0f19c33bc80e15196b9" -dependencies = [ - "num-traits", -] - [[package]] name = "linked-hash-map" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" [[package]] name = "maplit" @@ -301,26 +273,13 @@ dependencies = [ [[package]] name = "multimap" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97fbd5d00e0e37bfb10f433af8f5aaf631e739368dc9fc28286ca81ca4948dc" +checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333" dependencies = [ "serde", ] -[[package]] -name = "nix" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "void", -] - [[package]] name = "nix" version = "0.15.0" @@ -329,7 +288,7 @@ checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", "void", ] @@ -342,16 +301,28 @@ checksum = "dd0eaf8df8bab402257e0a5c17a254e4cc1f72a93588a1ddfb5d356c801aa7cb" dependencies = [ "bitflags", "cc", - "cfg-if", + "cfg-if 0.1.10", "libc", "void", ] [[package]] -name = "num-bigint" -version = "0.2.4" +name = "nix" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "343b3df15c945a59e72aae31e89a7cfc9e11850e96d4fde6fed5e3c7c8d9c887" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" dependencies = [ "autocfg", "num-integer", @@ -360,9 +331,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb0cf31fb3ff77e6d2a6ebd6800df7fdcd106f2ad89113c9130bcd07f93dffc" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" dependencies = [ "autocfg", "num-traits", @@ -374,16 +345,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.7", - "quote 1.0.2", + "proc-macro2", + "quote", "syn", ] [[package]] name = "num-integer" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg", "num-traits", @@ -391,9 +362,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.2" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" dependencies = [ "autocfg", "num-bigint", @@ -403,33 +374,30 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.10" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0" + [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" -[[package]] -name = "ordered-float" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" -dependencies = [ - "num-traits", -] - [[package]] name = "os_pipe" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db4d06355a7090ce852965b2d08e11426c315438462638c6d721448d0b47aa22" +checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213" dependencies = [ "libc", "winapi", @@ -446,9 +414,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4fb201c5c22a55d8b24fef95f78be52738e5e1361129be1b5e862ecdb6894a" +checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53" dependencies = [ "ucd-trie", ] @@ -465,22 +433,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.1.1" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b9fcf299b5712d06ee128a556c94709aaa04512c4dffb8ead07c5c998447fc0" +checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55" dependencies = [ "pest", "pest_meta", - "proc-macro2 1.0.7", - "quote 1.0.2", + "proc-macro2", + "quote", "syn", ] [[package]] name = "pest_meta" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df43fd99896fd72c485fe47542c7b500e4ac1e8700bf995544d1317a60ded547" +checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d" dependencies = [ "maplit", "pest", @@ -489,44 +457,26 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.17" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" [[package]] name = "proc-macro2" -version = "0.4.30" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" dependencies = [ - "unicode-xid 0.1.0", -] - -[[package]] -name = "proc-macro2" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" -dependencies = [ - "unicode-xid 0.2.0", + "unicode-xid", ] [[package]] name = "quote" -version = "0.6.13" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" dependencies = [ - "proc-macro2 0.4.30", -] - -[[package]] -name = "quote" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -dependencies = [ - "proc-macro2 1.0.7", + "proc-macro2", ] [[package]] @@ -545,12 +495,6 @@ dependencies = [ "pest_derive", ] -[[package]] -name = "redox_syscall" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" - [[package]] name = "rust-ini" version = "0.14.0" @@ -560,27 +504,6 @@ dependencies = [ "multimap", ] -[[package]] -name = "rusttype" -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310942406a39981bed7e12b09182a221a29e0990f3e7e0c971f131922ed135d5" -dependencies = [ - "rusttype 0.8.2", -] - -[[package]] -name = "rusttype" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14a911032fb5791ccbeec9f28fdcb9bf0983b81f227bafdfd227c658d0731c8a" -dependencies = [ - "approx", - "arrayvec", - "ordered-float", - "stb_truetype", -] - [[package]] name = "rusttype" version = "0.9.2" @@ -593,44 +516,35 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8506c1de11c9c4e4c38863ccbe02a305c8188e85a05a784c9e11e1c3910c8" - -[[package]] -name = "same-file" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" -dependencies = [ - "winapi-util", -] +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "serde" -version = "1.0.104" +version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" +checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.104" +version = "1.0.123" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" +checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" dependencies = [ - "proc-macro2 1.0.7", - "quote 1.0.2", + "proc-macro2", + "quote", "syn", ] [[package]] name = "serde_json" -version = "1.0.44" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c575e0cc52bdd09b47f330f646cf59afc586e9c4e3ccd6fc1f625b8ea1dad7" +checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" dependencies = [ "itoa", "ryu", @@ -639,9 +553,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.11" +version = "0.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" +checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" dependencies = [ "dtoa", "linked-hash-map", @@ -651,9 +565,9 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68" +checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ "block-buffer", "digest", @@ -668,105 +582,80 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" [[package]] -name = "smithay-client-toolkit" -version = "0.6.4" +name = "smallvec" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93960e8975909fcb14cc755de93af2149d8b8f4eb368315537d40cfd0f324054" -dependencies = [ - "andrew", - "bitflags", - "dlib", - "lazy_static", - "memmap", - "nix 0.14.1", - "wayland-client", - "wayland-protocols", -] - -[[package]] -name = "stb_truetype" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77b6b07e862c66a9f3e62a07588fee67cd90a9135a2b942409f195507b4fb51" -dependencies = [ - "byteorder", -] +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" [[package]] name = "syn" -version = "1.0.12" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc157159e2a7df58cd67b1cace10b8ed256a404fb0070593f137d8ba6bef4de" +checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" dependencies = [ - "proc-macro2 1.0.7", - "quote 1.0.2", - "unicode-xid 0.2.0", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] name = "thread_local" -version = "1.0.0" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ddf1ad580c7e3d1efff877d972bcc93f995556b9087a5a259630985c88ceab" +checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] name = "time" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" dependencies = [ "libc", - "redox_syscall", + "wasi", "winapi", ] [[package]] name = "timerfd" -version = "1.0.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c9a7822e546fa39d0b5ae14a93a33903975b62af6597288aea77f0580a6abbe" +checksum = "0bb53e6628675d73224925201a9a41f01c8d31108fdccb983975a1c1449dfc91" dependencies = [ "libc", ] [[package]] name = "ttf-parser" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52fbe7769f5af5d7d25aea74b9443b64e544a5ffb4d2b2968295ddea934f1a06" +checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" [[package]] name = "typenum" -version = "1.11.2" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "ucd-trie" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2" +checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "unicode-segmentation" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" [[package]] name = "unicode-xid" -version = "0.1.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "void" @@ -775,26 +664,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] -name = "walkdir" -version = "2.2.9" +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "wayland-client" -version = "0.23.6" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1080ebe0efabcf12aef2132152f616038f2d7dcbbccf7b2d8c5270fe14bcda" +checksum = "bdbdbe01d03b2267809f3ed99495b37395387fde789e0f2ebb78e8b43f75b6d7" dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.14.1", + "nix 0.18.0", "wayland-commons", "wayland-scanner", "wayland-sys", @@ -802,19 +686,21 @@ dependencies = [ [[package]] name = "wayland-commons" -version = "0.23.6" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb66b0d1a27c39bbce712b6372131c6e25149f03ffb0cd017cf8f7de8d66dbdb" +checksum = "480450f76717edd64ad04a4426280d737fc3d10a236b982df7b1aee19f0e2d56" dependencies = [ - "nix 0.14.1", + "nix 0.18.0", + "once_cell", + "smallvec", "wayland-sys", ] [[package]] name = "wayland-protocols" -version = "0.23.6" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc286643656742777d55dc8e70d144fa4699e426ca8e9d4ef454f4bf15ffcf9" +checksum = "319a82b4d3054dd25acc32d9aee0f84fa95b63bc983fffe4703b6b8d47e01a30" dependencies = [ "bitflags", "wayland-client", @@ -824,30 +710,29 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.23.6" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93b02247366f395b9258054f964fe293ddd019c3237afba9be2ccbe9e1651c3d" +checksum = "7010ba5767b3fcd350decc59055390b4ebe6bd1b9279a9feb1f1888987f1133d" dependencies = [ - "proc-macro2 0.4.30", - "quote 0.6.13", + "proc-macro2", + "quote", "xml-rs", ] [[package]] name = "wayland-sys" -version = "0.23.6" +version = "0.28.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94e89a86e6d6d7c7c9b19ebf48a03afaac4af6bc22ae570e9a24124b75358f4" +checksum = "6793834e0c35d11fd96a97297abe03d37be627e1847da52e17d7e0e3b51cc099" dependencies = [ - "dlib", - "lazy_static", + "pkg-config", ] [[package]] name = "winapi" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -859,15 +744,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -879,9 +755,11 @@ name = "wldash" version = "0.2.0" dependencies = [ "alsa", + "bitflags", "byteorder", "chrono", "dbus", + "dlib", "fontconfig", "fuzzy-matcher", "lazy_static", @@ -891,35 +769,28 @@ dependencies = [ "os_pipe", "rcalc_lib", "rust-ini", - "rusttype 0.9.2", + "rusttype", "serde", "serde_json", "serde_yaml", "shlex", - "smithay-client-toolkit", "timerfd", "unicode-segmentation", "wayland-client", "wayland-protocols", ] -[[package]] -name = "xdg" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" - [[package]] name = "xml-rs" -version = "0.8.0" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" +checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a" [[package]] name = "yaml-rust" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" dependencies = [ "linked-hash-map", ] diff --git a/Cargo.toml b/Cargo.toml index 787ec75..c3fc985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,15 +20,16 @@ alsa-widget = ["alsa"] pulseaudio-widget = ["libpulse-binding"] [dependencies] -smithay-client-toolkit = "0.6.4" byteorder = "1.3" +bitflags = "1.0" +dlib = "0.4" rusttype = "^0.9" chrono = "0.4" nix = "0.16" memmap = "0.7" os_pipe = "0.9" -wayland-client = { version = "0.23", features = ["dlopen"] } -wayland-protocols = { version = "0.23", features = ["client", "unstable_protocols"] } +wayland-client = { version = "0.28"} +wayland-protocols = { version = "0.28", features = ["client", "unstable_protocols"] } dbus = "0.6" fuzzy-matcher = "0.3" lazy_static = "1.4" diff --git a/src/app.rs b/src/app.rs index eb524be..f75a3c1 100644 --- a/src/app.rs +++ b/src/app.rs @@ -3,21 +3,21 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; -use chrono::Local; +use chrono::{Duration, Local, NaiveDateTime}; -use smithay_client_toolkit::keyboard::{ - keysyms, map_keyboard_auto, Event as KbEvent, KeyState, ModifiersState, +use crate::keyboard::{ + keysyms, map_keyboard, Event as KbEvent, KeyState, ModifiersState, }; -use wayland_client::protocol::{wl_compositor, wl_output, wl_pointer, wl_shm, wl_surface}; -use wayland_client::{Display, EventQueue, GlobalEvent, GlobalManager, NewProxy}; +use wayland_client::protocol::{wl_compositor, wl_output, wl_pointer, wl_shm, wl_surface, wl_seat}; +use wayland_client::{Display, EventQueue, GlobalEvent, GlobalManager, Main}; use wayland_protocols::wlr::unstable::layer_shell::v1::client::{ zwlr_layer_shell_v1, zwlr_layer_surface_v1, }; use crate::buffer::Buffer; use crate::color::Color; -use crate::widget::{DrawContext, Widget}; +use crate::widget::{WaitContext, DrawContext, Widget}; use crate::cmd::Cmd; use crate::doublemempool::DoubleMemPool; @@ -29,12 +29,12 @@ pub enum OutputMode { } struct AppInner { - compositor: Option, - surfaces: Vec, - shell_surfaces: Vec, + compositor: Option>, + surfaces: Vec>, + shell_surfaces: Vec>, configured_surfaces: Arc>, - outputs: Vec<(u32, wl_output::WlOutput)>, - shell: Option, + outputs: Vec<(u32, Main)>, + shell: Option>, draw_tx: Sender, output_mode: OutputMode, visible: bool, @@ -65,12 +65,11 @@ impl AppInner { tx: Sender, output: Option<&wl_output::WlOutput>, ) -> ( - wl_surface::WlSurface, - zwlr_layer_surface_v1::ZwlrLayerSurfaceV1, + Main, + Main, ) { let surface = compositor - .create_surface(NewProxy::implement_dummy) - .unwrap(); + .create_surface(); let this_is_stupid = AtomicBool::new(false); @@ -79,24 +78,18 @@ impl AppInner { &surface, output, zwlr_layer_shell_v1::Layer::Overlay, - "".to_string(), - move |layer| { - layer.implement_closure( - move |evt, layer| match evt { - zwlr_layer_surface_v1::Event::Configure { serial, .. } => { - if !this_is_stupid.compare_and_swap(false, true, Ordering::SeqCst) { - *(configured_surfaces.lock().unwrap()) += 1; - layer.ack_configure(serial); - tx.send(Cmd::ForceDraw).unwrap(); - } - } - _ => unreachable!(), - }, - (), - ) - }, - ) - .unwrap(); + "".to_string()); + shell_surface.quick_assign(move |layer, event, _| match event { + zwlr_layer_surface_v1::Event::Configure { serial, .. } => { + if !this_is_stupid.compare_and_swap(false, true, Ordering::SeqCst) { + *(configured_surfaces.lock().unwrap()) += 1; + layer.ack_configure(serial); + tx.send(Cmd::ForceDraw).unwrap(); + } + } + _ => unreachable!(), + }, + ); shell_surface.set_keyboard_interactivity(1); shell_surface.set_size(1, 1); @@ -169,7 +162,7 @@ impl AppInner { self.draw_tx.send(Cmd::ForceDraw).unwrap(); } - fn add_output(&mut self, id: u32, output: wl_output::WlOutput) { + fn add_output(&mut self, id: u32, output: Main) { self.outputs.push((id, output)); self.outputs_changed(); } @@ -191,15 +184,22 @@ impl AppInner { } } - fn set_compositor(&mut self, compositor: Option) { + fn set_compositor(&mut self, compositor: Option>) { self.compositor = compositor } - fn set_shell(&mut self, shell: Option) { + fn set_shell(&mut self, shell: Option>) { self.shell = shell } } +struct AppKeyboard { + current: Option, + delay: i32, + rate: i32, + next: Option, +} + pub struct App<'a> { pools: DoubleMemPool, display: Display, @@ -210,6 +210,7 @@ pub struct App<'a> { inner: Arc>, last_damage: Option>, last_dim: (u32, u32), + keyboard: Arc>, } impl<'a> App<'a> { @@ -355,6 +356,26 @@ impl<'a> App<'a> { self.redraw(true) } + pub fn set_keyboard_repeat(&mut self, ctx: &mut WaitContext) { + let kbd = self.keyboard.lock().unwrap(); + if let Some(t) = kbd.next { + ctx.set_time(t); + } + } + + pub fn key_repeat(&mut self) -> Option { + let time = Local::now().naive_local(); + let mut kbd = self.keyboard.lock().unwrap(); + if let Some(target) = kbd.next { + if time >= target { + let cmd = kbd.current.as_ref().unwrap().clone(); + kbd.next = Some(time + Duration::milliseconds(kbd.rate.into())); + return Some(cmd); + } + } + None + } + pub fn new(tx: Sender, output_mode: OutputMode, bg: Color, scale: u32) -> App<'a> { let inner = Arc::new(Mutex::new(AppInner::new(tx, output_mode, scale))); @@ -364,19 +385,16 @@ impl<'a> App<'a> { let cmd_queue = Arc::new(Mutex::new(VecDeque::new())); - let (display, mut event_queue) = Display::connect_to_env().unwrap(); - - let display_wrapper = display - .as_ref() - .make_wrapper(&event_queue.get_token()) - .unwrap(); + let display = Display::connect_to_env().unwrap(); + let mut event_queue = display.create_event_queue(); + let display_wrapper = (*display).clone().attach(event_queue.token()); // // Set up global manager // let inner_global = inner.clone(); let manager = - GlobalManager::new_with_cb(&display_wrapper, move |event, registry| match event { + GlobalManager::new_with_cb(&display_wrapper, move |event, registry, _| match event { GlobalEvent::New { id, ref interface, @@ -384,10 +402,7 @@ impl<'a> App<'a> { } => { if let "wl_output" = &interface[..] { let output = registry - .bind(version, id, move |output| { - output.implement_closure(move |_, _| {}, ()) - }) - .unwrap(); + .bind(version, id); inner_global.lock().unwrap().add_output(id, output); } } @@ -400,39 +415,30 @@ impl<'a> App<'a> { // double sync to retrieve the global list // and the globals metadata - event_queue.sync_roundtrip().unwrap(); - event_queue.sync_roundtrip().unwrap(); + event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap(); + event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap(); // wl_compositor - let compositor: wl_compositor::WlCompositor = manager - .instantiate_range(1, 4, NewProxy::implement_dummy) + let compositor = manager + .instantiate_range(1, 4) .expect("server didn't advertise `wl_compositor`"); inner.lock().unwrap().set_compositor(Some(compositor)); // wl_shm - let shm_formats = Arc::new(Mutex::new(Vec::new())); - let shm = manager - .instantiate_range(1, 1, |shm| { - shm.implement_closure( - move |evt, _| { - if let wl_shm::Event::Format { format } = evt { - shm_formats.lock().unwrap().push(format); - } - }, - (), - ) - }) + let shm: Main = manager + .instantiate_range(1, 1) .expect("server didn't advertise `wl_shm`"); - let pools = DoubleMemPool::new(&shm).expect("Failed to create a memory pool !"); + let pools = DoubleMemPool::new(shm).expect("Failed to create a memory pool !"); // // Get our seat // - let seat = manager - .instantiate_range(1, 6, NewProxy::implement_dummy) + let seat: Main = manager + .instantiate_range(1, 6) .unwrap(); + event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap(); // // Keyboard processing @@ -446,7 +452,17 @@ impl<'a> App<'a> { logo: false, num_lock: false, })); - map_keyboard_auto(&seat, move |event: KbEvent, _| match event { + + let keyboard = Arc::new(Mutex::new(AppKeyboard{ + current: None, + delay: 0, + rate: 0, + next: None, + })); + + let kb2 = keyboard.clone(); + + map_keyboard(&seat, None, move |event: KbEvent, _, _| match event { KbEvent::Key { keysym, utf8, @@ -459,101 +475,112 @@ impl<'a> App<'a> { keysyms::XKB_KEY_c if modifiers_state.lock().unwrap().ctrl => { kbd_clone.lock().unwrap().push_back(Cmd::Exit) } - v => kbd_clone.lock().unwrap().push_back(Cmd::Keyboard { - key: v, - key_state: state, - modifiers_state: *modifiers_state.lock().unwrap(), - interpreted: utf8, - }), + v => { + let ev = Cmd::Keyboard { + key: v, + key_state: state, + modifiers_state: *modifiers_state.lock().unwrap(), + interpreted: utf8, + }; + let mut kbd = kb2.lock().unwrap(); + kbd.current = Some(ev.clone()); + kbd.next = if kbd.delay > 0 { + Some(Local::now().naive_local() + Duration::milliseconds(kbd.delay.into())) + } else { + None + }; + drop(kbd); + kbd_clone.lock().unwrap().push_back(ev); + } } + } else { + kb2.lock().unwrap().next = None; } + }, + KbEvent::Leave { .. } => { + kb2.lock().unwrap().next = None; + }, + KbEvent::RepeatInfo { delay, rate } => { + let mut kbd = kb2.lock().unwrap(); + kbd.delay = delay; + kbd.rate = rate; } KbEvent::Modifiers { modifiers } => *modifiers_state.lock().unwrap() = modifiers, _ => (), }) - .expect("Failed to map keyboard"); + .expect("could not map keyboard"); // // Prepare shell so that we can create our shell surface // inner.lock().unwrap().set_shell(Some( - if let Ok(layer) = manager.instantiate_exact( - 1, - |layer: NewProxy| { - layer.implement_closure(|_, _| {}, ()) - }, - ) { + if let Ok(layer) = manager.instantiate_exact(1) { layer } else { panic!("server didn't advertise `zwlr_layer_shell_v1`"); }, )); - event_queue.sync_roundtrip().unwrap(); + event_queue.sync_roundtrip(&mut(), |_, _, _|{}).unwrap(); // // Cursor processing // let pointer_clone = cmd_queue.clone(); - seat.get_pointer(move |ptr| { - let mut pos: (u32, u32) = (0, 0); - let mut vert_scroll: f64 = 0.0; - let mut horiz_scroll: f64 = 0.0; - let mut btn: u32 = 0; - let mut btn_clicked = false; - ptr.implement_closure( - move |evt, _| match evt { - wl_pointer::Event::Enter { - surface_x, - surface_y, - .. - } => { - pos = (surface_x as u32, surface_y as u32); - } - wl_pointer::Event::Leave { .. } => { - pos = (0, 0); - } - wl_pointer::Event::Motion { - surface_x, - surface_y, - .. - } => { - pos = (surface_x as u32 * scale, surface_y as u32 * scale); - } - wl_pointer::Event::Axis { axis, value, .. } => { - if axis == wl_pointer::Axis::VerticalScroll { - vert_scroll += value; - } - } - wl_pointer::Event::Button { button, state, .. } => { - if let wl_pointer::ButtonState::Released = state { - btn = button; - btn_clicked = true; - } - } - wl_pointer::Event::Frame => { - if vert_scroll != 0.0 || horiz_scroll != 0.0 { - pointer_clone.lock().unwrap().push_back(Cmd::MouseScroll { - scroll: (horiz_scroll, vert_scroll), - pos, - }); - vert_scroll = 0.0; - horiz_scroll = 0.0; - } - if btn_clicked { - pointer_clone - .lock() - .unwrap() - .push_back(Cmd::MouseClick { btn, pos }); - btn_clicked = false; - } - } - _ => {} - }, - (), - ) - }) - .unwrap(); + let pointer = seat.get_pointer(); + let mut pos: (u32, u32) = (0, 0); + let mut vert_scroll: f64 = 0.0; + let mut horiz_scroll: f64 = 0.0; + let mut btn: u32 = 0; + let mut btn_clicked = false; + pointer.quick_assign(move |_, event, _| match event { + wl_pointer::Event::Enter { + surface_x, + surface_y, + .. + } => { + pos = (surface_x as u32, surface_y as u32); + } + wl_pointer::Event::Leave { .. } => { + pos = (0, 0); + } + wl_pointer::Event::Motion { + surface_x, + surface_y, + .. + } => { + pos = (surface_x as u32 * scale, surface_y as u32 * scale); + } + wl_pointer::Event::Axis { axis, value, .. } => { + if axis == wl_pointer::Axis::VerticalScroll { + vert_scroll += value; + } + } + wl_pointer::Event::Button { button, state, .. } => { + if let wl_pointer::ButtonState::Released = state { + btn = button; + btn_clicked = true; + } + } + wl_pointer::Event::Frame => { + if vert_scroll != 0.0 || horiz_scroll != 0.0 { + pointer_clone.lock().unwrap().push_back(Cmd::MouseScroll { + scroll: (horiz_scroll, vert_scroll), + pos, + }); + vert_scroll = 0.0; + horiz_scroll = 0.0; + } + if btn_clicked { + pointer_clone + .lock() + .unwrap() + .push_back(Cmd::MouseClick { btn, pos }); + btn_clicked = false; + } + } + _ => {} + }); display.flush().unwrap(); @@ -567,6 +594,7 @@ impl<'a> App<'a> { inner, last_damage: None, last_dim: (0, 0), + keyboard, } } } diff --git a/src/cmd.rs b/src/cmd.rs index 25c529a..c2fdc10 100644 --- a/src/cmd.rs +++ b/src/cmd.rs @@ -1,5 +1,6 @@ -use smithay_client_toolkit::keyboard::{KeyState, ModifiersState}; +use crate::keyboard::{KeyState, ModifiersState}; +#[derive(Clone)] pub enum Cmd { Exit, Draw, @@ -13,6 +14,7 @@ pub enum Cmd { scroll: (f64, f64), pos: (u32, u32), }, + KeyboardTest, Keyboard { key: u32, key_state: KeyState, diff --git a/src/doublemempool.rs b/src/doublemempool.rs index f2e9cd6..c00e372 100644 --- a/src/doublemempool.rs +++ b/src/doublemempool.rs @@ -1,5 +1,233 @@ -use smithay_client_toolkit::utils::MemPool; -use wayland_client::protocol::wl_shm; +use std::{ + cell::RefCell, + ffi::CStr, + fs::File, + io, + os::unix::io::{FromRawFd, RawFd}, + rc::Rc, + time::SystemTime, + time::UNIX_EPOCH, +}; + +#[cfg(target_os = "linux")] +use nix::sys::memfd; +use nix::{ + errno::Errno, + fcntl, + sys::{mman, stat}, + unistd, +}; + +use memmap::MmapMut; + +use wayland_client::{ + protocol::{wl_buffer, wl_shm, wl_shm_pool}, + Main, +}; + +/// A wrapper handling an SHM memory pool backed by a shared memory file +/// +/// This wrapper handles for you the creation of the shared memory file and its synchronization +/// with the protocol. +/// +/// Mempool internally tracks the release of the buffers by the compositor. As such, creating a buffer +/// that is not commited to a surface (and then never released by the server) would cause the Mempool +/// to be stuck believing it is still in use. +/// +/// Mempool will also handle the destruction of buffers and as such the `destroy()` method should not +/// be used on buffers created from Mempool. +/// +/// Overwriting the contents of the memory pool before it is completely freed may cause graphical +/// glitches due to the possible corruption of data while the compositor is reading it. +/// +/// Mempool requires a callback that will be called when the pool becomes free, this +/// happens when all the pools buffers are released by the server. +pub struct MemPool { + file: File, + len: usize, + pool: Main, + buffer_count: Rc>, + mmap: MmapMut, + callback: Rc>, +} + +impl MemPool { + /// Create a new memory pool associated with given shm + pub fn new(shm: Main, callback: F) -> io::Result + where + F: FnMut(wayland_client::DispatchData) + 'static, + { + let mem_fd = create_shm_fd()?; + let mem_file = unsafe { File::from_raw_fd(mem_fd) }; + mem_file.set_len(128)?; + + let pool = shm.create_pool(mem_fd, 128); + + let mmap = unsafe { MmapMut::map_mut(&mem_file).unwrap() }; + + Ok(MemPool { + file: mem_file, + len: 128, + pool, + buffer_count: Rc::new(RefCell::new(0)), + mmap, + callback: Rc::new(RefCell::new(callback)), + }) + } + + /// Resize the memory pool + /// + /// This affect the size as it is seen by the wayland server. Even + /// if you extend the temporary file size by writing to it, you need to + /// call this method otherwise the server won't see the new size. + /// + /// Memory pools can only be extented, as such this method will do nothing + /// if the requested new size is smaller than the current size. + /// + /// This method allows you to ensure the underlying pool is large enough to + /// hold what you want to write to it. + pub fn resize(&mut self, newsize: usize) -> io::Result<()> { + if newsize > self.len { + self.file.set_len(newsize as u64)?; + self.pool.resize(newsize as i32); + self.len = newsize; + self.mmap = unsafe { MmapMut::map_mut(&self.file).unwrap() }; + } + Ok(()) + } + + /// Create a new buffer to this pool + /// + /// The parameters are: + /// + /// - `offset`: the offset (in bytes) from the beginning of the pool at which this + /// buffer starts + /// - `width`: the width of this buffer (in pixels) + /// - `height`: the height of this buffer (in pixels) + /// - `stride`: distance (in bytes) between the beginning of a row and the next one + /// - `format`: the encoding format of the pixels. Using a format that was not + /// advertised to the `wl_shm` global by the server is a protocol error and will + /// terminate your connection + pub fn buffer( + &self, + offset: i32, + width: i32, + height: i32, + stride: i32, + format: wl_shm::Format, + ) -> wl_buffer::WlBuffer { + *self.buffer_count.borrow_mut() += 1; + let my_buffer_count = self.buffer_count.clone(); + let my_callback = self.callback.clone(); + let buffer = self + .pool + .create_buffer(offset, width, height, stride, format); + buffer.quick_assign(move |buffer, event, dispatch_data| match event { + wl_buffer::Event::Release => { + buffer.destroy(); + let new_count = { + // borrow the buffer_count for as short as possible, in case + // the user wants to create a new buffer from the callback + let mut my_buffer_count = my_buffer_count.borrow_mut(); + *my_buffer_count -= 1; + *my_buffer_count + }; + if new_count == 0 { + (&mut *my_callback.borrow_mut())(dispatch_data); + } + } + _ => unreachable!(), + }); + (*buffer).clone().detach() + } + + /// Uses the memmap crate to map the underlying shared memory file + pub fn mmap(&mut self) -> &mut MmapMut { + &mut self.mmap + } + + /// Returns true if the pool contains buffers that are currently in use by the server + pub fn is_used(&self) -> bool { + *self.buffer_count.borrow() != 0 + } +} + +impl Drop for MemPool { + fn drop(&mut self) { + self.pool.destroy(); + } +} + +impl io::Write for MemPool { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut self.file, buf) + } + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut self.file) + } +} + +impl io::Seek for MemPool { + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + io::Seek::seek(&mut self.file, pos) + } +} + +fn create_shm_fd() -> io::Result { + // Only try memfd on linux + #[cfg(target_os = "linux")] + loop { + match memfd::memfd_create( + CStr::from_bytes_with_nul(b"smithay-client-toolkit\0").unwrap(), + memfd::MemFdCreateFlag::MFD_CLOEXEC, + ) { + Ok(fd) => return Ok(fd), + Err(nix::Error::Sys(Errno::EINTR)) => continue, + Err(nix::Error::Sys(Errno::ENOSYS)) => break, + Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)), + Err(err) => unreachable!(err), + } + } + + // Fallback to using shm_open + let sys_time = SystemTime::now(); + let mut mem_file_handle = format!( + "/smithay-client-toolkit-{}", + sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos() + ); + loop { + match mman::shm_open( + mem_file_handle.as_str(), + fcntl::OFlag::O_CREAT + | fcntl::OFlag::O_EXCL + | fcntl::OFlag::O_RDWR + | fcntl::OFlag::O_CLOEXEC, + stat::Mode::S_IRUSR | stat::Mode::S_IWUSR, + ) { + Ok(fd) => match mman::shm_unlink(mem_file_handle.as_str()) { + Ok(_) => return Ok(fd), + Err(nix::Error::Sys(errno)) => match unistd::close(fd) { + Ok(_) => return Err(io::Error::from(errno)), + Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)), + Err(err) => panic!(err), + }, + Err(err) => panic!(err), + }, + Err(nix::Error::Sys(Errno::EEXIST)) => { + // If a file with that handle exists then change the handle + mem_file_handle = format!( + "/smithay-client-toolkit-{}", + sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos() + ); + continue; + } + Err(nix::Error::Sys(Errno::EINTR)) => continue, + Err(nix::Error::Sys(errno)) => return Err(io::Error::from(errno)), + Err(err) => unreachable!(err), + } + } +} + pub struct DoubleMemPool { pool1: MemPool, @@ -8,10 +236,10 @@ pub struct DoubleMemPool { } impl DoubleMemPool { - pub fn new(shm: &wl_shm::WlShm) -> ::std::io::Result { + pub fn new(shm: Main) -> ::std::io::Result { Ok(DoubleMemPool { - pool1: MemPool::new(shm, move || {})?, - pool2: MemPool::new(shm, move || {})?, + pool1: MemPool::new(shm.clone(), move |_| {})?, + pool2: MemPool::new(shm.clone(), move |_| {})?, switch: false, }) } diff --git a/src/keyboard/ffi.rs b/src/keyboard/ffi.rs new file mode 100644 index 0000000..4b4bcd2 --- /dev/null +++ b/src/keyboard/ffi.rs @@ -0,0 +1,267 @@ +#![allow(dead_code, non_camel_case_types, clippy::identity_op)] +#![cfg_attr(rustfmt, rustfmt_skip)] + +use std::os::raw::{c_char, c_int, c_void, c_uint}; + +pub const XKB_MOD_NAME_SHIFT : &[u8] = b"Shift\0"; +pub const XKB_MOD_NAME_CAPS : &[u8] = b"Lock\0"; +pub const XKB_MOD_NAME_CTRL : &[u8] = b"Control\0"; +pub const XKB_MOD_NAME_ALT : &[u8] = b"Mod1\0"; +pub const XKB_MOD_NAME_NUM : &[u8] = b"Mod2\0"; +pub const XKB_MOD_NAME_LOGO : &[u8] = b"Mod4\0"; + +pub const XKB_LED_NAME_CAPS : &[u8] = b"Caps Lock\0"; +pub const XKB_LED_NAME_NUM : &[u8] = b"Num Lock\0"; +pub const XKB_LED_NAME_SCROLL : &[u8] = b"Scroll Lock\0"; + +pub struct xkb_context; +pub struct xkb_keymap; +pub struct xkb_state; +pub struct xkb_compose_table; +pub struct xkb_compose_state; + +pub type xkb_keycode_t = u32; +pub type xkb_keysym_t = u32; +pub type xkb_layout_index_t = u32; +pub type xkb_layout_mask_t = u32; +pub type xkb_level_index_t = u32; +pub type xkb_mod_index_t = u32; +pub type xkb_mod_mask_t = u32; +pub type xkb_led_index_t = u32; +pub type xkb_led_mask_t = u32; + +pub const XKB_KEYCODE_INVALID :u32 = 0xffff_ffff; +pub const XKB_LAYOUT_INVALID :u32 = 0xffff_ffff; +pub const XKB_LEVEL_INVALID :u32 = 0xffff_ffff; +pub const XKB_MOD_INVALID :u32 = 0xffff_ffff; +pub const XKB_LED_INVALID :u32 = 0xffff_ffff; +pub const XKB_KEYCODE_MAX :u32 = 0xffff_fffe; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct xkb_rule_names { + pub rules: *const c_char, + pub model: *const c_char , + pub layout: *const c_char, + pub variant: *const c_char, + pub options: *const c_char, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_keysym_flags { + /** Do not apply any flags. */ + XKB_KEYSYM_NO_FLAGS = 0, + /** Find keysym by case-insensitive search. */ + XKB_KEYSYM_CASE_INSENSITIVE = (1 << 0) +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_context_flags { + /** Do not apply any context flags. */ + XKB_CONTEXT_NO_FLAGS = 0, + /** Create this context with an empty include path. */ + XKB_CONTEXT_NO_DEFAULT_INCLUDES = (1 << 0), + /** + * Don't take RMLVO names from the environment. + * @since 0.3.0 + */ + XKB_CONTEXT_NO_ENVIRONMENT_NAMES = (1 << 1) +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_log_level { + /** Log critical internal errors only. */ + XKB_LOG_LEVEL_CRITICAL = 10, + /** Log all errors. */ + XKB_LOG_LEVEL_ERROR = 20, + /** Log warnings and errors. */ + XKB_LOG_LEVEL_WARNING = 30, + /** Log information, warnings, and errors. */ + XKB_LOG_LEVEL_INFO = 40, + /** Log everything. */ + XKB_LOG_LEVEL_DEBUG = 50 +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_keymap_compile_flags { + /** Do not apply any flags. */ + XKB_KEYMAP_COMPILE_NO_FLAGS = 0, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_keymap_format { + /** Cannot be used for creation */ + XKB_KEYMAP_USE_ORIGINAL_FORMAT = 0, + /** The current/classic XKB text format, as generated by xkbcomp -xkb. */ + XKB_KEYMAP_FORMAT_TEXT_V1 = 1, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_key_direction { + /** The key was released. */ + XKB_KEY_UP, + /** The key was pressed. */ + XKB_KEY_DOWN +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_compose_compile_flags { + XKB_COMPOSE_COMPILE_NO_FLAGS = 0 +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_compose_format { + XKB_COMPOSE_FORMAT_TEXT_V1 = 1 +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_compose_state_flags { + XKB_COMPOSE_STATE_NO_FLAGS = 0 +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_compose_status { + XKB_COMPOSE_NOTHING, + XKB_COMPOSE_COMPOSING, + XKB_COMPOSE_COMPOSED, + XKB_COMPOSE_CANCELLED +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum xkb_compose_feed_result { + XKB_COMPOSE_FEED_IGNORED, + XKB_COMPOSE_FEED_ACCEPTED +} + +bitflags::bitflags!( + pub struct xkb_state_component: u32 { + /** Depressed modifiers, i.e. a key is physically holding them. */ + const XKB_STATE_MODS_DEPRESSED = (1 << 0); + /** Latched modifiers, i.e. will be unset after the next non-modifier + * key press. */ + const XKB_STATE_MODS_LATCHED = (1 << 1); + /** Locked modifiers, i.e. will be unset after the key provoking the + * lock has been pressed again. */ + const XKB_STATE_MODS_LOCKED = (1 << 2); + /** Effective modifiers, i.e. currently active and affect key + * processing (derived from the other state components). + * Use this unless you explictly care how the state came about. */ + const XKB_STATE_MODS_EFFECTIVE = (1 << 3); + /** Depressed layout, i.e. a key is physically holding it. */ + const XKB_STATE_LAYOUT_DEPRESSED = (1 << 4); + /** Latched layout, i.e. will be unset after the next non-modifier + * key press. */ + const XKB_STATE_LAYOUT_LATCHED = (1 << 5); + /** Locked layout, i.e. will be unset after the key provoking the lock + * has been pressed again. */ + const XKB_STATE_LAYOUT_LOCKED = (1 << 6); + /** Effective layout, i.e. currently active and affects key processing + * (derived from the other state components). + * Use this unless you explictly care how the state came about. */ + const XKB_STATE_LAYOUT_EFFECTIVE = (1 << 7); + /** LEDs (derived from the other state components). */ + const XKB_STATE_LEDS = (1 << 8); + } +); + +dlopen_external_library!(XkbCommon, +functions: + fn xkb_keysym_get_name(xkb_keysym_t, *mut c_char, usize) -> c_int, + fn xkb_keysym_from_name(*const c_char, xkb_keysym_flags) -> xkb_keysym_t, + fn xkb_keysym_to_utf8(xkb_keysym_t, *mut c_char, usize) -> c_int, + fn xkb_keysym_to_utf32(xkb_keysym_t) -> u32, + fn xkb_context_new(xkb_context_flags) -> *mut xkb_context, + fn xkb_context_ref(*mut xkb_context) -> *mut xkb_context, + fn xkb_context_unref(*mut xkb_context) -> (), + fn xkb_context_set_user_data(*mut xkb_context, *mut c_void) -> (), + fn xkb_context_get_user_data(*mut xkb_context) -> *mut c_void, + fn xkb_context_include_path_append(*mut xkb_context, *const c_char) -> c_int, + fn xkb_context_include_path_append_default(*mut xkb_context) -> c_int, + fn xkb_context_include_path_reset_defaults(*mut xkb_context) -> c_int, + fn xkb_context_include_path_clear(*mut xkb_context) -> (), + fn xkb_context_num_include_paths(*mut xkb_context) -> c_uint, + fn xkb_context_include_path_get(*mut xkb_context, c_uint) -> *const c_char, + fn xkb_context_set_log_level(*mut xkb_context, xkb_log_level) -> (), + fn xkb_context_get_log_level(*mut xkb_context) -> xkb_log_level, + fn xkb_context_set_log_verbosity(*mut xkb_context, c_int) -> (), + fn xkb_context_get_log_verbosity(*mut xkb_context) -> c_int, + fn xkb_keymap_new_from_names(*mut xkb_context, + *const xkb_rule_names, + xkb_keymap_compile_flags + ) -> *mut xkb_keymap, + fn xkb_keymap_new_from_string(*mut xkb_context, + *const c_char, + xkb_keymap_format, + xkb_keymap_compile_flags + ) -> *mut xkb_keymap, + fn xkb_keymap_new_from_buffer(*mut xkb_context, + *const c_char, + usize, + xkb_keymap_format, + xkb_keymap_compile_flags + ) -> *mut xkb_keymap, + fn xkb_keymap_ref(*mut xkb_keymap) -> *mut xkb_keymap, + fn xkb_keymap_unref(*mut xkb_keymap) -> (), + fn xkb_keymap_get_as_string(*mut xkb_keymap, xkb_keymap_format) -> *const c_char, + fn xkb_keymap_key_repeats(*mut xkb_keymap, xkb_keycode_t) -> c_int, + + fn xkb_state_new(*mut xkb_keymap) -> *mut xkb_state, + fn xkb_state_ref(*mut xkb_state) -> *mut xkb_state, + fn xkb_state_unref(*mut xkb_state) -> (), + fn xkb_state_update_mask(*mut xkb_state, + xkb_mod_mask_t, + xkb_mod_mask_t, + xkb_mod_mask_t, + xkb_layout_index_t, + xkb_layout_index_t, + xkb_layout_index_t + ) -> xkb_state_component, + fn xkb_state_update_key(*mut xkb_state, + xkb_keycode_t, + xkb_key_direction + ) -> xkb_state_component, + fn xkb_state_key_get_syms(*mut xkb_state, + xkb_keycode_t, + *const *mut xkb_keysym_t + ) -> c_int, + fn xkb_state_key_get_utf8(*mut xkb_state, + xkb_keycode_t, + *mut c_char, + usize + ) -> c_int, + fn xkb_state_key_get_utf32(*mut xkb_state, xkb_keycode_t) -> u32, + fn xkb_state_key_get_one_sym(*mut xkb_state, xkb_keycode_t) -> xkb_keysym_t, + fn xkb_state_mod_name_is_active(*mut xkb_state, *const c_char, xkb_state_component) -> c_int, + fn xkb_compose_table_new_from_locale(*mut xkb_context, *const c_char, xkb_compose_compile_flags) -> *mut xkb_compose_table, + fn xkb_compose_table_unref(*mut xkb_compose_table) -> (), + fn xkb_compose_state_new(*mut xkb_compose_table, xkb_compose_state_flags) -> *mut xkb_compose_state, + fn xkb_compose_state_unref(*mut xkb_compose_state) -> (), + fn xkb_compose_state_feed(*mut xkb_compose_state, xkb_keysym_t) -> xkb_compose_feed_result, + fn xkb_compose_state_reset(*mut xkb_compose_state) -> (), + fn xkb_compose_state_get_status(*mut xkb_compose_state) -> xkb_compose_status, + fn xkb_compose_state_get_utf8(*mut xkb_compose_state, *mut c_char, usize) -> c_int, + fn xkb_compose_state_get_one_sym(*mut xkb_compose_state) -> xkb_keysym_t, +); + +lazy_static::lazy_static!( + pub static ref XKBCOMMON_OPTION: Option = { + XkbCommon::open("libxkbcommon.so.0") + .or_else(|_| XkbCommon::open("libxkbcommon.so")) + .ok() + }; + pub static ref XKBCOMMON_HANDLE: &'static XkbCommon = { + XKBCOMMON_OPTION.as_ref().expect("Library libxkbcommon.so could not be loaded.") + }; +); + diff --git a/src/keyboard/keysyms.rs b/src/keyboard/keysyms.rs new file mode 100644 index 0000000..b769680 --- /dev/null +++ b/src/keyboard/keysyms.rs @@ -0,0 +1,17 @@ +#![allow(missing_docs, non_upper_case_globals, unused_parens, clippy::all)] + +pub const XKB_KEY_BackSpace :u32 = 0xff08; /* Back space, back char */ +pub const XKB_KEY_Tab :u32 = 0xff09; +pub const XKB_KEY_Return :u32 = 0xff0d; /* Return, enter */ +pub const XKB_KEY_Escape :u32 = 0xff1b; +pub const XKB_KEY_Delete :u32 = 0xffff; /* Delete, rubout */ +pub const XKB_KEY_Home :u32 = 0xff50; +pub const XKB_KEY_Left :u32 = 0xff51; /* Move left, left arrow */ +pub const XKB_KEY_Right :u32 = 0xff53; /* Move right, right arrow */ +pub const XKB_KEY_End :u32 = 0xff57; /* EOL */ + +pub const XKB_KEY_ISO_Left_Tab :u32 = 0xfe20; +pub const XKB_KEY_e :u32 = 0x0065; /* U+0065 LATIN SMALL LETTER E */ +pub const XKB_KEY_a :u32 = 0x0061; /* U+0061 LATIN SMALL LETTER A */ +pub const XKB_KEY_c :u32 = 0x0063; /* U+0063 LATIN SMALL LETTER C */ +pub const XKB_KEY_u :u32 = 0x0075; /* U+0075 LATIN SMALL LETTER U */ diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs new file mode 100644 index 0000000..16add51 --- /dev/null +++ b/src/keyboard/mod.rs @@ -0,0 +1,338 @@ +//! Utilities for keymap interpretation of keyboard input +//! +//! This module provides an implementation for `wl_keyboard` +//! objects using `libxkbcommon` to interpret the keyboard input +//! given the user keymap. +//! +//! The entry point of this module is the [`map_keyboard`](fn.map_keyboard.html) +//! function which, given a `wl_seat` and a callback, setup keymap interpretation +//! and key repetition for the `wl_keyboard` of this seat. +//! +//! Key repetition relies on an event source, that needs to be inserted in your +//! calloop event loop. Not doing so will prevent key repetition to work +//! (but the rest of the functionnality will not be affected). + +use std::{cell::RefCell, os::unix::io::RawFd, rc::Rc}; + +use byteorder::{ByteOrder, NativeEndian}; + +pub use wayland_client::protocol::wl_keyboard::KeyState; +use wayland_client::{ + protocol::{wl_keyboard, wl_seat, wl_surface}, + Attached, +}; + +mod ffi; +mod state; +pub mod keysyms; + +use self::state::KbState; +pub use self::state::{ModifiersState, RMLVO}; + +#[derive(Debug)] +/// An error that occurred while trying to initialize a mapped keyboard +pub enum Error { + /// libxkbcommon is not available + XKBNotFound, + /// Provided RMLVO specified a keymap that would not be loaded + BadNames, +} + +/// Events received from a mapped keyboard +pub enum Event<'a> { + /// The keyboard focus has entered a surface + Enter { + /// serial number of the event + serial: u32, + /// surface that was entered + surface: wl_surface::WlSurface, + /// raw values of the currently pressed keys + rawkeys: &'a [u32], + /// interpreted symbols of the currently pressed keys + keysyms: &'a [u32], + }, + /// The keyboard focus has left a surface + Leave { + /// serial number of the event + serial: u32, + /// surface that was left + surface: wl_surface::WlSurface, + }, + /// The key modifiers have changed state + Modifiers { + /// current state of the modifiers + modifiers: ModifiersState, + }, + /// A key event occurred + Key { + /// serial number of the event + serial: u32, + /// time at which the keypress occurred + time: u32, + /// raw value of the key + rawkey: u32, + /// interpreted symbol of the key + keysym: u32, + /// new state of the key + state: KeyState, + /// utf8 interpretation of the entered text + /// + /// will always be `None` on key release events + utf8: Option, + repeats: bool, + }, + RepeatInfo { + rate: i32, + delay: i32, + }, +} + +/// Implement a keyboard for keymap translation with key repetition +/// +/// This requires you to provide a callback to receive the events after they +/// have been interpreted with the keymap. +/// +/// The keymap will be loaded from the provided RMLVO rules, or from the compositor +/// provided keymap if `None`. +/// +/// Returns an error if xkbcommon could not be initialized, the RMLVO specification +/// contained invalid values, or if the provided seat does not have keyboard capability. +/// +/// **Note:** This adapter does not handle key repetition. See `map_keyboard_repeat` for that. +pub fn map_keyboard( + seat: &Attached, + rmlvo: Option, + callback: F, +) -> Result +where + F: FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>) + 'static, +{ + let keyboard = seat.get_keyboard(); + + let state = Rc::new(RefCell::new( + rmlvo + .map(KbState::from_rmlvo) + .unwrap_or_else(KbState::new)?, + )); + + let callback = Rc::new(RefCell::new(callback)); + + // prepare the handler + let mut kbd_handler = KbdHandler { + callback, + state, + }; + + keyboard.quick_assign(move |keyboard, event, data| { + kbd_handler.event(keyboard.detach(), event, data) + }); + + Ok(keyboard.detach()) +} + +/* + * Classic handling + */ + +type KbdCallback = dyn FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>); + +struct KbdHandler { + state: Rc>, + callback: Rc>, +} + +impl KbdHandler { + fn event( + &mut self, + kbd: wl_keyboard::WlKeyboard, + event: wl_keyboard::Event, + dispatch_data: wayland_client::DispatchData, + ) { + use wl_keyboard::Event; + + match event { + Event::Keymap { format, fd, size } => self.keymap(kbd, format, fd, size), + Event::Enter { + serial, + surface, + keys, + } => self.enter(kbd, serial, surface, keys, dispatch_data), + Event::Leave { serial, surface } => self.leave(kbd, serial, surface, dispatch_data), + Event::Key { + serial, + time, + key, + state, + } => self.key(kbd, serial, time, key, state, dispatch_data), + Event::Modifiers { + mods_depressed, + mods_latched, + mods_locked, + group, + .. + } => self.modifiers( + kbd, + mods_depressed, + mods_latched, + mods_locked, + group, + dispatch_data, + ), + Event::RepeatInfo { rate, delay } => self.repeat_info(kbd, rate, delay, dispatch_data), + _ => {} + } + } + + fn keymap( + &mut self, + _: wl_keyboard::WlKeyboard, + format: wl_keyboard::KeymapFormat, + fd: RawFd, + size: u32, + ) { + let mut state = self.state.borrow_mut(); + if state.locked() { + // state is locked, ignore keymap updates + return; + } + if state.ready() { + // new keymap, we first deinit to free resources + unsafe { + state.de_init(); + } + } + match format { + wl_keyboard::KeymapFormat::XkbV1 => unsafe { + state.init_with_fd(fd, size as usize); + }, + wl_keyboard::KeymapFormat::NoKeymap => { + // TODO: how to handle this (hopefully never occuring) case? + } + _ => unreachable!(), + } + } + + fn enter( + &mut self, + object: wl_keyboard::WlKeyboard, + serial: u32, + surface: wl_surface::WlSurface, + keys: Vec, + dispatch_data: wayland_client::DispatchData, + ) { + let mut state = self.state.borrow_mut(); + let rawkeys = keys + .chunks_exact(4) + .map(NativeEndian::read_u32) + .collect::>(); + let keys: Vec = rawkeys.iter().map(|k| state.get_one_sym_raw(*k)).collect(); + (&mut *self.callback.borrow_mut())( + Event::Enter { + serial, + surface, + rawkeys: &rawkeys, + keysyms: &keys, + }, + object, + dispatch_data, + ); + } + + fn leave( + &mut self, + object: wl_keyboard::WlKeyboard, + serial: u32, + surface: wl_surface::WlSurface, + dispatch_data: wayland_client::DispatchData, + ) { + (&mut *self.callback.borrow_mut())(Event::Leave { serial, surface }, object, dispatch_data); + } + + fn key( + &mut self, + object: wl_keyboard::WlKeyboard, + serial: u32, + time: u32, + key: u32, + key_state: wl_keyboard::KeyState, + dispatch_data: wayland_client::DispatchData, + ) { + let (sym, utf8, repeats) = { + let mut state = self.state.borrow_mut(); + // Get the values to generate a key event + let sym = state.get_one_sym_raw(key); + let utf8 = if key_state == wl_keyboard::KeyState::Pressed { + match state.compose_feed(sym) { + Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => { + if let Some(status) = state.compose_status() { + match status { + ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => { + state.compose_get_utf8() + } + ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => { + state.get_utf8_raw(key) + } + _ => None, + } + } else { + state.get_utf8_raw(key) + } + } + Some(_) => { + // XKB_COMPOSE_FEED_IGNORED + None + } + None => { + // XKB COMPOSE is not initialized + state.get_utf8_raw(key) + } + } + } else { + None + }; + let repeats = unsafe { state.key_repeats(key + 8) }; + (sym, utf8, repeats) + }; + + (&mut *self.callback.borrow_mut())( + Event::Key { + serial, + time, + rawkey: key, + keysym: sym, + state: key_state, + utf8, + repeats, + }, + object, + dispatch_data, + ); + } + + fn modifiers( + &mut self, + object: wl_keyboard::WlKeyboard, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + group: u32, + dispatch_data: wayland_client::DispatchData, + ) { + { + let mut state = self.state.borrow_mut(); + state.update_modifiers(mods_depressed, mods_latched, mods_locked, group); + (&mut *self.callback.borrow_mut())( + Event::Modifiers { + modifiers: state.mods_state(), + }, + object, + dispatch_data, + ); + } + } + + fn repeat_info(&mut self, object: wl_keyboard::WlKeyboard, rate: i32, delay: i32, dispatch_data: wayland_client::DispatchData) { + (&mut *self.callback.borrow_mut())(Event::RepeatInfo { rate, delay }, object, dispatch_data); + } +} + diff --git a/src/keyboard/state.rs b/src/keyboard/state.rs new file mode 100644 index 0000000..e687db6 --- /dev/null +++ b/src/keyboard/state.rs @@ -0,0 +1,401 @@ +use std::{ + env, + ffi::CString, + fs::File, + os::raw::c_char, + os::unix::{ + ffi::OsStringExt, + io::{FromRawFd, RawFd}, + }, + ptr, +}; + +use memmap::MmapOptions; + +use super::ffi::{self, xkb_state_component, XKBCOMMON_HANDLE as XKBH}; +use super::Error; + +pub struct KbState { + xkb_context: *mut ffi::xkb_context, + xkb_keymap: *mut ffi::xkb_keymap, + xkb_state: *mut ffi::xkb_state, + xkb_compose_table: *mut ffi::xkb_compose_table, + xkb_compose_state: *mut ffi::xkb_compose_state, + mods_state: ModifiersState, + locked: bool, +} + +/// The RMLVO description of a keymap +/// +/// All fields are optional, and the system default +/// will be used if set to `None`. +pub struct RMLVO { + /// The rules file to use + pub rules: Option, + /// The keyboard model by which to interpret keycodes and LEDs + pub model: Option, + /// A comma separated list of layouts (languages) to include in the keymap + pub layout: Option, + /// A comma separated list of variants, one per layout, which may modify or + /// augment the respective layout in various ways + pub variant: Option, + /// A comma separated list of options, through which the user specifies + /// non-layout related preferences, like which key combinations are + /// used for switching layouts, or which key is the Compose key. + pub options: Option, +} + +/// Represents the current state of the keyboard modifiers +/// +/// Each field of this struct represents a modifier and is `true` if this modifier is active. +/// +/// For some modifiers, this means that the key is currently pressed, others are toggled +/// (like caps lock). +#[derive(Copy, Clone, Debug, Default)] +pub struct ModifiersState { + /// The "control" key + pub ctrl: bool, + /// The "alt" key + pub alt: bool, + /// The "shift" key + pub shift: bool, + /// The "Caps lock" key + pub caps_lock: bool, + /// The "logo" key + /// + /// Also known as the "windows" key on most keyboards + pub logo: bool, + /// The "Num lock" key + pub num_lock: bool, +} + +impl ModifiersState { + fn new() -> ModifiersState { + ModifiersState::default() + } + + fn update_with(&mut self, state: *mut ffi::xkb_state) { + self.ctrl = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.alt = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.shift = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.caps_lock = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_CAPS.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.logo = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + self.num_lock = unsafe { + (XKBH.xkb_state_mod_name_is_active)( + state, + ffi::XKB_MOD_NAME_NUM.as_ptr() as *const c_char, + xkb_state_component::XKB_STATE_MODS_EFFECTIVE, + ) > 0 + }; + } +} + +impl KbState { + pub(crate) fn update_modifiers( + &mut self, + mods_depressed: u32, + mods_latched: u32, + mods_locked: u32, + group: u32, + ) { + if !self.ready() { + return; + } + let mask = unsafe { + (XKBH.xkb_state_update_mask)( + self.xkb_state, + mods_depressed, + mods_latched, + mods_locked, + 0, + 0, + group, + ) + }; + if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) { + // effective value of mods have changed, we need to update our state + self.mods_state.update_with(self.xkb_state); + } + } + + pub(crate) fn get_one_sym_raw(&mut self, keycode: u32) -> u32 { + if !self.ready() { + return 0; + } + unsafe { (XKBH.xkb_state_key_get_one_sym)(self.xkb_state, keycode + 8) } + } + + pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option { + if !self.ready() { + return None; + } + let size = unsafe { + (XKBH.xkb_state_key_get_utf8)(self.xkb_state, keycode + 8, ptr::null_mut(), 0) + } + 1; + if size <= 1 { + return None; + }; + let mut buffer = Vec::with_capacity(size as usize); + unsafe { + buffer.set_len(size as usize); + (XKBH.xkb_state_key_get_utf8)( + self.xkb_state, + keycode + 8, + buffer.as_mut_ptr() as *mut _, + size as usize, + ); + }; + // remove the final `\0` + buffer.pop(); + // libxkbcommon will always provide valid UTF8 + Some(unsafe { String::from_utf8_unchecked(buffer) }) + } + + pub(crate) fn compose_feed(&mut self, keysym: u32) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { + return None; + } + Some(unsafe { (XKBH.xkb_compose_state_feed)(self.xkb_compose_state, keysym) }) + } + + pub(crate) fn compose_status(&mut self) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { + return None; + } + Some(unsafe { (XKBH.xkb_compose_state_get_status)(self.xkb_compose_state) }) + } + + pub(crate) fn compose_get_utf8(&mut self) -> Option { + if !self.ready() || self.xkb_compose_state.is_null() { + return None; + } + let size = unsafe { + (XKBH.xkb_compose_state_get_utf8)(self.xkb_compose_state, ptr::null_mut(), 0) + } + 1; + if size <= 1 { + return None; + }; + let mut buffer = Vec::with_capacity(size as usize); + unsafe { + buffer.set_len(size as usize); + (XKBH.xkb_compose_state_get_utf8)( + self.xkb_compose_state, + buffer.as_mut_ptr() as *mut _, + size as usize, + ); + }; + // remove the final `\0` + buffer.pop(); + // libxkbcommon will always provide valid UTF8 + Some(unsafe { String::from_utf8_unchecked(buffer) }) + } + + pub(crate) fn new() -> Result { + let xkbh = match ffi::XKBCOMMON_OPTION.as_ref() { + Some(h) => h, + None => return Err(Error::XKBNotFound), + }; + let xkb_context = + unsafe { (xkbh.xkb_context_new)(ffi::xkb_context_flags::XKB_CONTEXT_NO_FLAGS) }; + if xkb_context.is_null() { + return Err(Error::XKBNotFound); + } + + let mut me = KbState { + xkb_context, + xkb_keymap: ptr::null_mut(), + xkb_state: ptr::null_mut(), + xkb_compose_table: ptr::null_mut(), + xkb_compose_state: ptr::null_mut(), + mods_state: ModifiersState::new(), + locked: false, + }; + + unsafe { + me.init_compose(); + } + + Ok(me) + } + + pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result { + fn to_cstring(s: Option) -> Result, Error> { + s.map_or(Ok(None), |s| CString::new(s).map(Option::Some)) + .map_err(|_| Error::BadNames) + } + + let mut state = KbState::new()?; + + let rules = to_cstring(rmlvo.rules)?; + let model = to_cstring(rmlvo.model)?; + let layout = to_cstring(rmlvo.layout)?; + let variant = to_cstring(rmlvo.variant)?; + let options = to_cstring(rmlvo.options)?; + + let xkb_names = ffi::xkb_rule_names { + rules: rules.map_or(ptr::null(), |s| s.as_ptr()), + model: model.map_or(ptr::null(), |s| s.as_ptr()), + layout: layout.map_or(ptr::null(), |s| s.as_ptr()), + variant: variant.map_or(ptr::null(), |s| s.as_ptr()), + options: options.map_or(ptr::null(), |s| s.as_ptr()), + }; + + unsafe { + state.init_with_rmlvo(xkb_names)?; + } + + state.locked = true; + Ok(state) + } + + pub(crate) unsafe fn init_compose(&mut self) { + let locale = env::var_os("LC_ALL") + .and_then(|v| if v.is_empty() { None } else { Some(v) }) + .or_else(|| env::var_os("LC_CTYPE")) + .and_then(|v| if v.is_empty() { None } else { Some(v) }) + .or_else(|| env::var_os("LANG")) + .and_then(|v| if v.is_empty() { None } else { Some(v) }) + .unwrap_or_else(|| "C".into()); + let locale = CString::new(locale.into_vec()).unwrap(); + + let compose_table = (XKBH.xkb_compose_table_new_from_locale)( + self.xkb_context, + locale.as_ptr(), + ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS, + ); + + if compose_table.is_null() { + // init of compose table failed, continue without compose + return; + } + + let compose_state = (XKBH.xkb_compose_state_new)( + compose_table, + ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS, + ); + + if compose_state.is_null() { + // init of compose state failed, continue without compose + (XKBH.xkb_compose_table_unref)(compose_table); + return; + } + + self.xkb_compose_table = compose_table; + self.xkb_compose_state = compose_state; + } + + pub(crate) unsafe fn post_init(&mut self, xkb_keymap: *mut ffi::xkb_keymap) { + let xkb_state = (XKBH.xkb_state_new)(xkb_keymap); + self.xkb_keymap = xkb_keymap; + self.xkb_state = xkb_state; + self.mods_state.update_with(xkb_state); + } + + pub(crate) unsafe fn de_init(&mut self) { + (XKBH.xkb_state_unref)(self.xkb_state); + self.xkb_state = ptr::null_mut(); + (XKBH.xkb_keymap_unref)(self.xkb_keymap); + self.xkb_keymap = ptr::null_mut(); + } + + pub(crate) unsafe fn init_with_fd(&mut self, fd: RawFd, size: usize) { + let map = MmapOptions::new() + .len(size) + .map(&File::from_raw_fd(fd)) + .unwrap(); + + let xkb_keymap = (XKBH.xkb_keymap_new_from_string)( + self.xkb_context, + map.as_ptr() as *const _, + ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1, + ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ); + + if xkb_keymap.is_null() { + panic!("Received invalid keymap from compositor."); + } + + self.post_init(xkb_keymap); + } + + pub(crate) unsafe fn init_with_rmlvo( + &mut self, + names: ffi::xkb_rule_names, + ) -> Result<(), Error> { + let xkb_keymap = (XKBH.xkb_keymap_new_from_names)( + self.xkb_context, + &names, + ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS, + ); + + if xkb_keymap.is_null() { + return Err(Error::BadNames); + } + + self.post_init(xkb_keymap); + + Ok(()) + } + + pub(crate) unsafe fn key_repeats(&mut self, xkb_keycode_t: ffi::xkb_keycode_t) -> bool { + (XKBH.xkb_keymap_key_repeats)(self.xkb_keymap, xkb_keycode_t) == 1 + } + + #[inline] + pub(crate) fn ready(&self) -> bool { + !self.xkb_state.is_null() + } + + #[inline] + pub(crate) fn locked(&self) -> bool { + self.locked + } + + #[inline] + pub(crate) fn mods_state(&self) -> ModifiersState { + self.mods_state + } +} + +impl Drop for KbState { + fn drop(&mut self) { + unsafe { + (XKBH.xkb_compose_state_unref)(self.xkb_compose_state); + (XKBH.xkb_compose_table_unref)(self.xkb_compose_table); + (XKBH.xkb_state_unref)(self.xkb_state); + (XKBH.xkb_keymap_unref)(self.xkb_keymap); + (XKBH.xkb_context_unref)(self.xkb_context); + } + } +} + diff --git a/src/main.rs b/src/main.rs index 9d246d2..20fa172 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,7 @@ use timerfd::{SetTimeFlags, TimerFd, TimerState}; mod app; mod buffer; +mod keyboard; mod cmd; mod color; mod config; @@ -23,6 +24,9 @@ mod fonts; mod widget; mod widgets; +#[macro_use] +extern crate dlib; + use app::{App, OutputMode}; use cmd::Cmd; use config::Config; @@ -202,7 +206,7 @@ fn main() { }); let mut timer = TimerFd::new().unwrap(); - let ev_fd = PollFd::new(app.event_queue().get_connection_fd(), PollFlags::POLLIN); + let ev_fd = PollFd::new(app.event_queue().display().get_connection_fd(), PollFlags::POLLIN); let rx_fd = PollFd::new(rx_pipe.as_raw_fd(), PollFlags::POLLIN); let tm_fd = PollFd::new(timer.as_raw_fd(), PollFlags::POLLIN); let ipc_fd = PollFd::new(listener.as_raw_fd(), PollFlags::POLLIN); @@ -224,6 +228,11 @@ fn main() { app.redraw(false).expect("Failed to draw"); app.flush_display(); } + Cmd::KeyboardTest => { + if let Some(cmd) = app.key_repeat() { + q.lock().unwrap().push_back(cmd); + } + } Cmd::ForceDraw => { app.redraw(true).expect("Failed to draw"); app.flush_display(); @@ -279,6 +288,7 @@ fn main() { wait_ctx.target_time = None; app.get_widget().wait(&mut wait_ctx); + app.set_keyboard_repeat(&mut wait_ctx); if let Some(target_time) = wait_ctx.target_time { let n = Local::now().naive_local(); @@ -313,7 +323,7 @@ fn main() { } app.event_queue() - .dispatch_pending() + .dispatch_pending(&mut (), |_, _, _|{}) .expect("Failed to dispatch all messages."); } @@ -365,7 +375,9 @@ fn main() { .contains(PollFlags::POLLIN) { timer.read(); - q.lock().unwrap().push_back(Cmd::Draw); + let mut qq = q.lock().unwrap(); + qq.push_back(Cmd::KeyboardTest); + qq.push_back(Cmd::Draw); } } } diff --git a/src/widget.rs b/src/widget.rs index 6f16938..740cc71 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -2,7 +2,7 @@ use crate::buffer::Buffer; use crate::color::Color; use chrono::NaiveDateTime; use nix::poll::PollFd; -pub use smithay_client_toolkit::keyboard::{KeyState, ModifiersState}; +pub use crate::keyboard::{KeyState, ModifiersState}; pub struct DrawContext<'a> { pub buf: &'a mut Buffer<'a>, diff --git a/src/widgets/launcher.rs b/src/widgets/launcher.rs index d0217ab..9c23158 100644 --- a/src/widgets/launcher.rs +++ b/src/widgets/launcher.rs @@ -15,7 +15,7 @@ use std::process::Command; use std::sync::mpsc::Sender; use fuzzy_matcher::skim::{fuzzy_indices, fuzzy_match}; -use smithay_client_toolkit::keyboard::keysyms; +use crate::keyboard::keysyms; use unicode_segmentation::UnicodeSegmentation; pub struct Launcher<'a> {