summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaxVerevkin <maxxverrr@gmail.com>2023-01-02 20:23:35 +0200
committerMax Verevkin <34583604+MaxVerevkin@users.noreply.github.com>2023-01-02 20:44:14 +0200
commiteebeed1f2774146c3d7df90c7a982f58428d4cff (patch)
treec038e06100fbd8b4debf29c5b077d51a56a5f98b
parent2570f39b5daf6e2a13b3973d47aa7c67fbb457fb (diff)
downloadi3bar-mango-eebeed1f2774146c3d7df90c7a982f58428d4cff.tar.gz
rewrite using wayrs
-rw-r--r--Cargo.lock365
-rw-r--r--Cargo.toml14
-rw-r--r--README.md1
-rw-r--r--protocols/wlr-layer-shell-unstable-v1.xml391
-rw-r--r--protocols/xdg-shell.xml1352
-rw-r--r--src/bar.rs160
-rw-r--r--src/config.rs2
-rw-r--r--src/cursor.rs109
-rw-r--r--src/main.rs43
-rw-r--r--src/protocol.rs8
-rw-r--r--src/river_protocols.rs4
-rw-r--r--src/river_protocols/control.rs112
-rw-r--r--src/river_protocols/status.rs144
-rw-r--r--src/shared_state.rs14
-rw-r--r--src/state.rs713
-rw-r--r--src/status_cmd.rs86
16 files changed, 2503 insertions, 1015 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 9f74f8a..039669f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -13,9 +13,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.66"
+version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
+checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61"
[[package]]
name = "atty"
@@ -41,10 +41,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
+name = "bytes"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
+
+[[package]]
name = "cairo-rs"
-version = "0.16.3"
+version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "247e1183fa769ac22121f92276dae52f89acaf297f24b1320019f439b6e3b46f"
+checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d"
dependencies = [
"bitflags",
"cairo-sys-rs",
@@ -66,12 +72,6 @@ dependencies = [
]
[[package]]
-name = "cc"
-version = "1.0.77"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
-
-[[package]]
name = "cfg-expr"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -87,6 +87,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
+name = "convert_case"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
+dependencies = [
+ "unicode-segmentation",
+]
+
+[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -108,21 +117,6 @@ dependencies = [
]
[[package]]
-name = "dlib"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
-dependencies = [
- "libloading",
-]
-
-[[package]]
-name = "downcast-rs"
-version = "1.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
-
-[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -211,9 +205,9 @@ dependencies = [
[[package]]
name = "gio"
-version = "0.16.3"
+version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d4a17d999e6e4e05d87c2bb05b7140d47769bc53211711a33e2f91536458714"
+checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092"
dependencies = [
"bitflags",
"futures-channel",
@@ -244,9 +238,9 @@ dependencies = [
[[package]]
name = "glib"
-version = "0.16.4"
+version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5204a4217749b385cefbfb7bf3a2fcde83e4ce6d0945f64440a1f5bd4010305"
+checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f"
dependencies = [
"bitflags",
"futures-channel",
@@ -333,51 +327,26 @@ dependencies = [
"pangocairo",
"serde",
"serde_json",
- "smithay-client-toolkit",
+ "thiserror",
"tokio",
"toml",
- "wayland-backend",
- "wayland-client",
- "wayland-scanner",
-]
-
-[[package]]
-name = "io-lifetimes"
-version = "1.0.0-rc1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f65dae1d3aa98e6877917ab4e6fdbfdfb00e95885ea7c4f4f29e3a5dfc08fdf"
-dependencies = [
- "libc",
- "windows-sys 0.36.1",
+ "wayrs-client",
+ "wayrs-scanner",
+ "wayrs-shm-alloc",
+ "xcursor",
]
[[package]]
name = "itoa"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
-
-[[package]]
-name = "lazy_static"
-version = "1.4.0"
+version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "libc"
-version = "0.2.137"
+version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
-
-[[package]]
-name = "libloading"
-version = "0.7.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
-dependencies = [
- "cfg-if",
- "winapi",
-]
+checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "log"
@@ -405,9 +374,9 @@ dependencies = [
[[package]]
name = "memoffset"
-version = "0.6.5"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4"
dependencies = [
"autocfg",
]
@@ -427,20 +396,20 @@ dependencies = [
"libc",
"log",
"wasi",
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
name = "nix"
-version = "0.25.0"
+version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
+checksum = "46a58d1d356c6597d08cde02c2f09d785b09e28711837b1ed667dc652c08a694"
dependencies = [
- "autocfg",
"bitflags",
"cfg-if",
"libc",
"memoffset",
+ "static_assertions",
]
[[package]]
@@ -461,9 +430,9 @@ checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
[[package]]
name = "pango"
-version = "0.16.3"
+version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f6a83cd4015382dbb0f4fcf3ab7b277d4885711a62b2f2c1e6582a120094edad"
+checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94"
dependencies = [
"bitflags",
"gio",
@@ -567,27 +536,27 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.47"
+version = "1.0.49"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quick-xml"
-version = "0.23.1"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11bafc859c6815fbaffbbbf4229ecb767ac913fecb27f9ad4343662e9ef099ea"
+checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
-version = "1.0.21"
+version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
@@ -631,30 +600,24 @@ checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848"
[[package]]
name = "ryu"
-version = "1.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
-
-[[package]]
-name = "scoped-tls"
-version = "1.0.1"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "serde"
-version = "1.0.148"
+version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e53f64bb4ba0191d6d0676e1b141ca55047d83b74f5607e6d8eb88126c52c2dc"
+checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.148"
+version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a55492425aa53521babf6137309e7d34c20bbfbbfcfe2c7f3a047fd1f6b92c0c"
+checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
@@ -663,9 +626,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.89"
+version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
+checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883"
dependencies = [
"itoa",
"ryu",
@@ -673,6 +636,24 @@ dependencies = [
]
[[package]]
+name = "shmemfdrs"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a07caaa5573b8fa1142d19783d30ba2999c6a221169784f3bb7e2b9b308bcbdb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "slab"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -688,27 +669,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
-name = "smithay-client-toolkit"
-version = "0.16.0"
-source = "git+https://github.com/Smithay/client-toolkit#638ca60e3ae8277d645991cc986be3df5981fbb6"
-dependencies = [
- "bitflags",
- "dlib",
- "lazy_static",
- "log",
- "memmap2",
- "nix",
- "pkg-config",
- "thiserror",
- "wayland-backend",
- "wayland-client",
- "wayland-cursor",
- "wayland-protocols",
- "wayland-protocols-wlr",
- "wayland-scanner",
-]
-
-[[package]]
name = "socket2"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -719,10 +679,16 @@ dependencies = [
]
[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
name = "syn"
-version = "1.0.104"
+version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4ae548ec36cf198c0ef7710d3c230987c2d6d7bd98ad6edc0274462724c585ce"
+checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
@@ -753,18 +719,18 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10deb33631e3c9018b9baf9dcbbc4f737320d2b576bac10f6aefa048fa407e3e"
+checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.37"
+version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "982d17546b47146b28f7c22e3d08465f6b8903d0ea13c1660d9d84a6e7adcdbb"
+checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
dependencies = [
"proc-macro2",
"quote",
@@ -773,24 +739,27 @@ dependencies = [
[[package]]
name = "tokio"
-version = "1.22.0"
+version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d76ce4a75fb488c605c54bf610f221cea8b0dafb53333c1a67e8ee199dcd2ae3"
+checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
dependencies = [
"autocfg",
+ "bytes",
"libc",
+ "memchr",
"mio",
"pin-project-lite",
+ "signal-hook-registry",
"socket2",
"tokio-macros",
- "winapi",
+ "windows-sys",
]
[[package]]
name = "tokio-macros"
-version = "1.8.0"
+version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484"
+checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8"
dependencies = [
"proc-macro2",
"quote",
@@ -799,18 +768,24 @@ dependencies = [
[[package]]
name = "toml"
-version = "0.5.9"
+version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
-version = "1.0.5"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
+checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
[[package]]
name = "version-compare"
@@ -831,77 +806,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
-name = "wayland-backend"
-version = "0.1.0-beta.13"
+name = "wayrs-client"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3f96c52ca34b33e6bb55497327428be54dad648973aa5d3d1e02982d2fcc298"
+checksum = "36735e4fa01ade0a22aa5366c0245e49e955b7088379127d86b8bb14f2427eac"
dependencies = [
- "cc",
- "downcast-rs",
- "io-lifetimes",
- "nix",
- "scoped-tls",
- "smallvec",
- "wayland-sys",
-]
-
-[[package]]
-name = "wayland-client"
-version = "0.30.0-beta.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f6ab5911cc8a28e9d781933c1afe0f97ffa6885ad8941294784d025f06e5ad9"
-dependencies = [
- "bitflags",
- "futures-channel",
- "futures-core",
"nix",
"thiserror",
- "wayland-backend",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-cursor"
-version = "0.30.0-beta.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa5ce1bbc13a3d806648974cba1d53481fea2ac29483eb147e4cce1250fc5263"
-dependencies = [
- "nix",
- "wayland-client",
- "xcursor",
-]
-
-[[package]]
-name = "wayland-protocols"
-version = "0.30.0-beta.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e32104ba51988754f4429dbb59c7b7fd2ccf1feaa0009bb59e49a5e2f91e985d"
-dependencies = [
- "bitflags",
- "wayland-backend",
- "wayland-client",
- "wayland-scanner",
-]
-
-[[package]]
-name = "wayland-protocols-wlr"
-version = "0.1.0-beta.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "130613a48629204e4ab50ea4b5de2efa2e17c58f71a5af15b39535f99421f8c3"
-dependencies = [
- "bitflags",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-scanner",
+ "tokio",
+ "wayrs-scanner",
]
[[package]]
-name = "wayland-scanner"
-version = "0.30.0-beta.13"
+name = "wayrs-scanner"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fb55ca28710dcc155078db4e2bd29aacf24bd311261c48750f38c6c5430bf1f"
+checksum = "3957115661161e3f6efcadc6f09c894e3ed8536374baeffedc5e6c80a680b517"
dependencies = [
+ "convert_case",
"proc-macro2",
"quick-xml",
"quote",
@@ -909,14 +831,14 @@ dependencies = [
]
[[package]]
-name = "wayland-sys"
-version = "0.30.0-beta.13"
+name = "wayrs-shm-alloc"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "882bda56e8397384ad66ec0ebe763e0b3b53153a6327344f7d74b85981a271e1"
+checksum = "7e3811c51fb48e8bd025ea8005f9a724e97ef955912eef050eedd257507c2c78"
dependencies = [
- "dlib",
- "log",
- "pkg-config",
+ "memmap2",
+ "shmemfdrs",
+ "wayrs-client",
]
[[package]]
@@ -952,30 +874,17 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
-dependencies = [
- "windows_aarch64_msvc 0.36.1",
- "windows_i686_gnu 0.36.1",
- "windows_i686_msvc 0.36.1",
- "windows_x86_64_gnu 0.36.1",
- "windows_x86_64_msvc 0.36.1",
-]
-
-[[package]]
-name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
- "windows_aarch64_msvc 0.42.0",
- "windows_i686_gnu 0.42.0",
- "windows_i686_msvc 0.42.0",
- "windows_x86_64_gnu 0.42.0",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
"windows_x86_64_gnullvm",
- "windows_x86_64_msvc 0.42.0",
+ "windows_x86_64_msvc",
]
[[package]]
@@ -986,48 +895,24 @@ checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
-
-[[package]]
-name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
-
-[[package]]
-name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
-
-[[package]]
-name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
-
-[[package]]
-name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
@@ -1040,12 +925,6 @@ checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.36.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
-
-[[package]]
-name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
diff --git a/Cargo.toml b/Cargo.toml
index 40a46bc..6c6aac9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,17 +13,17 @@ pangocairo = "0.16"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
memchr = "2"
-wayland-client = "0.30.0-beta.13"
-wayland-scanner = "0.30.0-beta.13"
-wayland-backend = "0.1.0-beta.13"
+thiserror = "1.0.38"
-[dependencies.smithay-client-toolkit]
-git = "https://github.com/Smithay/client-toolkit"
-default-features = false
+wayrs-client = { version = "0.2.1", features = ["tokio"] }
+wayrs-scanner = "0.2"
+wayrs-shm-alloc = "0.2"
+
+xcursor = "0.3.4"
[dependencies.tokio]
version = "1"
-features = ["macros", "net", "rt", "sync"]
+features = ["macros", "net", "rt", "sync", "io-util", "process"]
[profile.release]
lto = "fat"
diff --git a/README.md b/README.md
index b5fb93f..c26747a 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@ tags_r = 6
blocks_r = 6
blocks_overlap = 0
command = "i3status-rs"
+show_layout_name = false
```
## How progressive short mode and rounded corners work
diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 0000000..b701bd3
--- /dev/null
+++ b/protocols/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,391 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_layer_shell_unstable_v1">
+ <copyright>
+ Copyright © 2017 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <interface name="zwlr_layer_shell_v1" version="4">
+ <description summary="create surfaces that are layers of the desktop">
+ Clients can use this interface to assign the surface_layer role to
+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+ rendered with a defined z-depth respective to each other. They may also be
+ anchored to the edges and corners of a screen and specify input handling
+ semantics. This interface should be suitable for the implementation of
+ many desktop shell components, and a broad number of other applications
+ that interact with the desktop.
+ </description>
+
+ <request name="get_layer_surface">
+ <description summary="create a layer_surface from a surface">
+ Create a layer surface for an existing surface. This assigns the role of
+ layer_surface, or raises a protocol error if another role is already
+ assigned.
+
+ Creating a layer surface from a wl_surface which has a buffer attached
+ or committed is a client error, and any attempts by a client to attach
+ or manipulate a buffer prior to the first layer_surface.configure call
+ must also be treated as errors.
+
+ After creating a layer_surface object and setting it up, the client
+ must perform an initial commit without any buffer attached.
+ The compositor will reply with a layer_surface.configure event.
+ The client must acknowledge it and is then allowed to attach a buffer
+ to map the surface.
+
+ You may pass NULL for output to allow the compositor to decide which
+ output to use. Generally this will be the one that the user most
+ recently interacted with.
+
+ Clients can specify a namespace that defines the purpose of the layer
+ surface.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
+ <arg name="namespace" type="string" summary="namespace for the layer surface"/>
+ </request>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="wl_surface has another role"/>
+ <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
+ <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
+ </enum>
+
+ <enum name="layer">
+ <description summary="available layers for surfaces">
+ These values indicate which layers a surface can be rendered in. They
+ are ordered by z depth, bottom-most first. Traditional shell surfaces
+ will typically be rendered between the bottom and top layers.
+ Fullscreen shell surfaces are typically rendered at the top layer.
+ Multiple surfaces can share a single layer, and ordering within a
+ single layer is undefined.
+ </description>
+
+ <entry name="background" value="0"/>
+ <entry name="bottom" value="1"/>
+ <entry name="top" value="2"/>
+ <entry name="overlay" value="3"/>
+ </enum>
+
+ <!-- Version 3 additions -->
+
+ <request name="destroy" type="destructor" since="3">
+ <description summary="destroy the layer_shell object">
+ This request indicates that the client will not use the layer_shell
+ object any more. Objects that have been created through this instance
+ are not affected.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwlr_layer_surface_v1" version="4">
+ <description summary="layer metadata interface">
+ An interface that may be implemented by a wl_surface, for surfaces that
+ are designed to be rendered as a layer of a stacked desktop-like
+ environment.
+
+ Layer surface state (layer, size, anchor, exclusive zone,
+ margin, interactivity) is double-buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ Attaching a null buffer to a layer surface unmaps it.
+
+ Unmapping a layer_surface means that the surface cannot be shown by the
+ compositor until it is explicitly mapped again. The layer_surface
+ returns to the state it had right after layer_shell.get_layer_surface.
+ The client can re-map the surface by performing a commit without any
+ buffer attached, waiting for a configure event and handling it as usual.
+ </description>
+
+ <request name="set_size">
+ <description summary="sets the size of the surface">
+ Sets the size of the surface in surface-local coordinates. The
+ compositor will display the surface centered with respect to its
+ anchors.
+
+ If you pass 0 for either value, the compositor will assign it and
+ inform you of the assignment in the configure event. You must set your
+ anchor to opposite edges in the dimensions you omit; not doing so is a
+ protocol error. Both values are 0 by default.
+
+ Size is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </request>
+
+ <request name="set_anchor">
+ <description summary="configures the anchor point of the surface">
+ Requests that the compositor anchor the surface to the specified edges
+ and corners. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left corner of the output); otherwise the anchor point
+ will be centered on that edge, or in the center if none is specified.
+
+ Anchor is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"/>
+ </request>
+
+ <request name="set_exclusive_zone">
+ <description summary="configures the exclusive geometry of this surface">
+ Requests that the compositor avoids occluding an area with other
+ surfaces. The compositor's use of this information is
+ implementation-dependent - do not assume that this region will not
+ actually be occluded.
+
+ A positive value is only meaningful if the surface is anchored to one
+ edge or an edge and both perpendicular edges. If the surface is not
+ anchored, anchored to only two perpendicular edges (a corner), anchored
+ to only two parallel edges or anchored to all edges, a positive value
+ will be treated the same as zero.
+
+ A positive zone is the distance from the edge in surface-local
+ coordinates to consider exclusive.
+
+ Surfaces that do not wish to have an exclusive zone may instead specify
+ how they should interact with surfaces that do. If set to zero, the
+ surface indicates that it would like to be moved to avoid occluding
+ surfaces with a positive exclusive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accommodate for other
+ surfaces, and the compositor should extend it all the way to the edges
+ it is anchored to.
+
+ For example, a panel might set its exclusive zone to 10, so that
+ maximized shell surfaces are not shown on top of it. A notification
+ might set its exclusive zone to 0, so that it is moved to avoid
+ occluding the panel, but shell surfaces are shown underneath it. A
+ wallpaper or lock screen might set their exclusive zone to -1, so that
+ they stretch below or over the panel.
+
+ The default value is 0.
+
+ Exclusive zone is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="zone" type="int"/>
+ </request>
+
+ <request name="set_margin">
+ <description summary="sets a margin from the anchor point">
+ Requests that the surface be placed some distance away from the anchor
+ point on the output, in surface-local coordinates. Setting this value
+ for edges you are not anchored to has no effect.
+
+ The exclusive zone includes the margin.
+
+ Margin is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="top" type="int"/>
+ <arg name="right" type="int"/>
+ <arg name="bottom" type="int"/>
+ <arg name="left" type="int"/>
+ </request>
+
+ <enum name="keyboard_interactivity">
+ <description summary="types of keyboard interaction possible for a layer shell surface">
+ Types of keyboard interaction possible for layer shell surfaces. The
+ rationale for this is twofold: (1) some applications are not interested
+ in keyboard events and not allowing them to be focused can improve the
+ desktop experience; (2) some applications will want to take exclusive
+ keyboard focus.
+ </description>
+
+ <entry name="none" value="0">
+ <description summary="no keyboard focus is possible">
+ This value indicates that this surface is not interested in keyboard
+ events and the compositor should never assign it the keyboard focus.
+
+ This is the default value, set for newly created layer shell surfaces.
+
+ This is useful for e.g. desktop widgets that display information or
+ only have interaction with non-keyboard input devices.
+ </description>
+ </entry>
+ <entry name="exclusive" value="1">
+ <description summary="request exclusive keyboard focus">
+ Request exclusive keyboard focus if this surface is above the shell surface layer.
+
+ For the top and overlay layers, the seat will always give
+ exclusive keyboard focus to the top-most layer which has keyboard
+ interactivity set to exclusive. If this layer contains multiple
+ surfaces with keyboard interactivity set to exclusive, the compositor
+ determines the one receiving keyboard events in an implementation-
+ defined manner. In this case, no guarantee is made when this surface
+ will receive keyboard focus (if ever).
+
+ For the bottom and background layers, the compositor is allowed to use
+ normal focus semantics.
+
+ This setting is mainly intended for applications that need to ensure
+ they receive all keyboard events, such as a lock screen or a password
+ prompt.
+ </description>
+ </entry>
+ <entry name="on_demand" value="2" since="4">
+ <description summary="request regular keyboard focus semantics">
+ This requests the compositor to allow this surface to be focused and
+ unfocused by the user in an implementation-defined manner. The user
+ should be able to unfocus this surface even regardless of the layer
+ it is on.
+
+ Typically, the compositor will want to use its normal mechanism to
+ manage keyboard focus between layer shell surfaces with this setting
+ and regular toplevels on the desktop layer (e.g. click to focus).
+ Nevertheless, it is possible for a compositor to require a special
+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
+ a click even if focus follows the mouse normally, or providing a
+ keybinding to switch focus between layers).
+
+ This setting is mainly intended for desktop shell components (e.g.
+ panels) that allow keyboard interaction. Using this option can allow
+ implementing a desktop shell that can be fully usable without the
+ mouse.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_keyboard_interactivity">
+ <description summary="requests keyboard events">
+ Set how keyboard events are delivered to this surface. By default,
+ layer shell surfaces do not receive keyboard events; this request can
+ be used to change this.
+
+ This setting is inherited by child surfaces set by the get_popup
+ request.
+
+ Layer surfaces receive pointer, touch, and tablet events normally. If
+ you do not want to receive them, set the input region on your surface
+ to an empty region.
+
+ Keyboard interactivity is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign this layer_surface as an xdg_popup parent">
+ This assigns an xdg_popup's parent to this layer_surface. This popup
+ should have been created via xdg_surface::get_popup with the parent set
+ to NULL, and this request must be invoked before committing the popup's
+ initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="popup" type="object" interface="xdg_popup"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the layer_surface">
+ This request destroys the layer surface.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event asks the client to resize its surface.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ The client is free to dismiss all but the last configure event it
+ received.
+
+ The width and height arguments specify the size of the window in
+ surface-local coordinates.
+
+ The size is a hint, in the sense that the client is free to ignore it if
+ it doesn't resize, pick a smaller size (to satisfy aspect ratio or
+ resize in steps of NxM pixels). If the client picks a smaller size and
+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+ surface will be centered on this axis.
+
+ If the width or height arguments are zero, it means the client should
+ decide its own window dimension.
+ </description>
+ <arg name="serial" type="uint"/>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </event>
+
+ <event name="closed">
+ <description summary="surface should be closed">
+ The closed event is sent by the compositor when the surface will no
+ longer be shown. The output may have been destroyed or the user may
+ have asked for it to be removed. Further changes to the surface will be
+ ignored. The client should destroy the resource after receiving this
+ event, and create a new surface if they so choose.
+ </description>
+ </event>
+
+ <enum name="error">
+ <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
+ <entry name="invalid_size" value="1" summary="size is invalid"/>
+ <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
+ <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
+ </enum>
+
+ <enum name="anchor" bitfield="true">
+ <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
+ <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
+ <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
+ <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
+ </enum>
+
+ <!-- Version 2 additions -->
+
+ <request name="set_layer" since="2">
+ <description summary="change the layer of the surface">
+ Change the layer that the surface is rendered on.
+
+ Layer is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
+ </request>
+ </interface>
+</protocol>
+
diff --git a/protocols/xdg-shell.xml b/protocols/xdg-shell.xml
new file mode 100644
index 0000000..cc2b439
--- /dev/null
+++ b/protocols/xdg-shell.xml
@@ -0,0 +1,1352 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="xdg_shell">
+
+ <copyright>
+ Copyright © 2008-2013 Kristian Høgsberg
+ Copyright © 2013 Rafael Antognolli
+ Copyright © 2013 Jasper St. Pierre
+ Copyright © 2010-2013 Intel Corporation
+ Copyright © 2015-2017 Samsung Electronics Co., Ltd
+ Copyright © 2015-2017 Red Hat Inc.
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) 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.
+ </copyright>
+
+ <interface name="xdg_wm_base" version="5">
+ <description summary="create desktop-style surfaces">
+ The xdg_wm_base interface is exposed as a global object enabling clients
+ to turn their wl_surfaces into windows in a desktop environment. It
+ defines the basic functionality needed for clients and the compositor to
+ create windows that can be dragged, resized, maximized, etc, as well as
+ creating transient windows such as popup menus.
+ </description>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="given wl_surface has another role"/>
+ <entry name="defunct_surfaces" value="1"
+ summary="xdg_wm_base was destroyed before children"/>
+ <entry name="not_the_topmost_popup" value="2"
+ summary="the client tried to map or destroy a non-topmost popup"/>
+ <entry name="invalid_popup_parent" value="3"
+ summary="the client specified an invalid popup parent surface"/>
+ <entry name="invalid_surface_state" value="4"
+ summary="the client provided an invalid surface state"/>
+ <entry name="invalid_positioner" value="5"
+ summary="the client provided an invalid positioner"/>
+ <entry name="unresponsive" value="6"
+ summary="the client didn’t respond to a ping event in time"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy xdg_wm_base">
+ Destroy this xdg_wm_base object.
+
+ Destroying a bound xdg_wm_base object while there are surfaces
+ still alive created by this xdg_wm_base object instance is illegal
+ and will result in a defunct_surfaces error.
+ </description>
+ </request>
+
+ <request name="create_positioner">
+ <description summary="create a positioner object">
+ Create a positioner object. A positioner object is used to position
+ surfaces relative to some parent surface. See the interface description
+ and xdg_surface.get_popup for details.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_positioner"/>
+ </request>
+
+ <request name="get_xdg_surface">
+ <description summary="create a shell surface from a surface">
+ This creates an xdg_surface for the given surface. While xdg_surface
+ itself is not a role, the corresponding surface may only be assigned
+ a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is
+ illegal to create an xdg_surface for a wl_surface which already has an
+ assigned role and this will result in a role error.
+
+ This creates an xdg_surface for the given surface. An xdg_surface is
+ used as basis to define a role to a given surface, such as xdg_toplevel
+ or xdg_popup. It also manages functionality shared between xdg_surface
+ based surface roles.
+
+ See the documentation of xdg_surface for more details about what an
+ xdg_surface is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_surface"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ </request>
+
+ <request name="pong">
+ <description summary="respond to a ping event">
+ A client must respond to a ping event with a pong request or
+ the client may be deemed unresponsive. See xdg_wm_base.ping
+ and xdg_wm_base.error.unresponsive.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the ping event"/>
+ </request>
+
+ <event name="ping">
+ <description summary="check if the client is alive">
+ The ping event asks the client if it's still alive. Pass the
+ serial specified in the event back to the compositor by sending
+ a "pong" request back with the specified serial. See xdg_wm_base.pong.
+
+ Compositors can use this to determine if the client is still
+ alive. It's unspecified what will happen if the client doesn't
+ respond to the ping request, or in what timeframe. Clients should
+ try to respond in a reasonable amount of time. The “unresponsive”
+ error is provided for compositors that wish to disconnect unresponsive
+ clients.
+
+ A compositor is free to ping in any way it wants, but a client must
+ always respond to any xdg_wm_base object it created.
+ </description>
+ <arg name="serial" type="uint" summary="pass this to the pong request"/>
+ </event>
+ </interface>
+
+ <interface name="xdg_positioner" version="5">
+ <description summary="child surface positioner">
+ The xdg_positioner provides a collection of rules for the placement of a
+ child surface relative to a parent surface. Rules can be defined to ensure
+ the child surface remains within the visible area's borders, and to
+ specify how the child surface changes its position, such as sliding along
+ an axis, or flipping around a rectangle. These positioner-created rules are
+ constrained by the requirement that a child surface must intersect with or
+ be at least partially adjacent to its parent surface.
+
+ See the various requests for details about possible rules.
+
+ At the time of the request, the compositor makes a copy of the rules
+ specified by the xdg_positioner. Thus, after the request is complete the
+ xdg_positioner object can be destroyed or reused; further changes to the
+ object will have no effect on previous usages.
+
+ For an xdg_positioner object to be considered complete, it must have a
+ non-zero size set by set_size, and a non-zero anchor rectangle set by
+ set_anchor_rect. Passing an incomplete xdg_positioner object when
+ positioning a surface raises an invalid_positioner error.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_input" value="0" summary="invalid input provided"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_positioner object">
+ Notify the compositor that the xdg_positioner will no longer be used.
+ </description>
+ </request>
+
+ <request name="set_size">
+ <description summary="set the size of the to-be positioned rectangle">
+ Set the size of the surface that is to be positioned with the positioner
+ object. The size is in surface-local coordinates and corresponds to the
+ window geometry. See xdg_surface.set_window_geometry.
+
+ If a zero or negative size is set the invalid_input error is raised.
+ </description>
+ <arg name="width" type="int" summary="width of positioned rectangle"/>
+ <arg name="height" type="int" summary="height of positioned rectangle"/>
+ </request>
+
+ <request name="set_anchor_rect">
+ <description summary="set the anchor rectangle within the parent surface">
+ Specify the anchor rectangle within the parent surface that the child
+ surface will be placed relative to. The rectangle is relative to the
+ window geometry as defined by xdg_surface.set_window_geometry of the
+ parent surface.
+
+ When the xdg_positioner object is used to position a child surface, the
+ anchor rectangle may not extend outside the window geometry of the
+ positioned child's parent surface.
+
+ If a negative size is set the invalid_input error is raised.
+ </description>
+ <arg name="x" type="int" summary="x position of anchor rectangle"/>
+ <arg name="y" type="int" summary="y position of anchor rectangle"/>
+ <arg name="width" type="int" summary="width of anchor rectangle"/>
+ <arg name="height" type="int" summary="height of anchor rectangle"/>
+ </request>
+
+ <enum name="anchor">
+ <entry name="none" value="0"/>
+ <entry name="top" value="1"/>
+ <entry name="bottom" value="2"/>
+ <entry name="left" value="3"/>
+ <entry name="right" value="4"/>
+ <entry name="top_left" value="5"/>
+ <entry name="bottom_left" value="6"/>
+ <entry name="top_right" value="7"/>
+ <entry name="bottom_right" value="8"/>
+ </enum>
+
+ <request name="set_anchor">
+ <description summary="set anchor rectangle anchor">
+ Defines the anchor point for the anchor rectangle. The specified anchor
+ is used derive an anchor point that the child surface will be
+ positioned relative to. If a corner anchor is set (e.g. 'top_left' or
+ 'bottom_right'), the anchor point will be at the specified corner;
+ otherwise, the derived anchor point will be centered on the specified
+ edge, or in the center of the anchor rectangle if no edge is specified.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"
+ summary="anchor"/>
+ </request>
+
+ <enum name="gravity">
+ <entry name="none" value="0"/>
+ <entry name="top" value="1"/>
+ <entry name="bottom" value="2"/>
+ <entry name="left" value="3"/>
+ <entry name="right" value="4"/>
+ <entry name="top_left" value="5"/>
+ <entry name="bottom_left" value="6"/>
+ <entry name="top_right" value="7"/>
+ <entry name="bottom_right" value="8"/>
+ </enum>
+
+ <request name="set_gravity">
+ <description summary="set child surface gravity">
+ Defines in what direction a surface should be positioned, relative to
+ the anchor point of the parent surface. If a corner gravity is
+ specified (e.g. 'bottom_right' or 'top_left'), then the child surface
+ will be placed towards the specified gravity; otherwise, the child
+ surface will be centered over the anchor point on any axis that had no
+ gravity specified. If the gravity is not in the ‘gravity’ enum, an
+ invalid_input error is raised.
+ </description>
+ <arg name="gravity" type="uint" enum="gravity"
+ summary="gravity direction"/>
+ </request>
+
+ <enum name="constraint_adjustment" bitfield="true">
+ <description summary="constraint adjustments">
+ The constraint adjustment value define ways the compositor will adjust
+ the position of the surface, if the unadjusted position would result
+ in the surface being partly constrained.
+
+ Whether a surface is considered 'constrained' is left to the compositor
+ to determine. For example, the surface may be partly outside the
+ compositor's defined 'work area', thus necessitating the child surface's
+ position be adjusted until it is entirely inside the work area.
+
+ The adjustments can be combined, according to a defined precedence: 1)
+ Flip, 2) Slide, 3) Resize.
+ </description>
+ <entry name="none" value="0">
+ <description summary="don't move the child surface when constrained">
+ Don't alter the surface position even if it is constrained on some
+ axis, for example partially outside the edge of an output.
+ </description>
+ </entry>
+ <entry name="slide_x" value="1">
+ <description summary="move along the x axis until unconstrained">
+ Slide the surface along the x axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the x axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ x axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+ </description>
+ </entry>
+ <entry name="slide_y" value="2">
+ <description summary="move along the y axis until unconstrained">
+ Slide the surface along the y axis until it is no longer constrained.
+
+ First try to slide towards the direction of the gravity on the y axis
+ until either the edge in the opposite direction of the gravity is
+ unconstrained or the edge in the direction of the gravity is
+ constrained.
+
+ Then try to slide towards the opposite direction of the gravity on the
+ y axis until either the edge in the direction of the gravity is
+ unconstrained or the edge in the opposite direction of the gravity is
+ constrained.
+ </description>
+ </entry>
+ <entry name="flip_x" value="4">
+ <description summary="invert the anchor and gravity on the x axis">
+ Invert the anchor and gravity on the x axis if the surface is
+ constrained on the x axis. For example, if the left edge of the
+ surface is constrained, the gravity is 'left' and the anchor is
+ 'left', change the gravity to 'right' and the anchor to 'right'.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_x adjustment will be the one before the
+ adjustment.
+ </description>
+ </entry>
+ <entry name="flip_y" value="8">
+ <description summary="invert the anchor and gravity on the y axis">
+ Invert the anchor and gravity on the y axis if the surface is
+ constrained on the y axis. For example, if the bottom edge of the
+ surface is constrained, the gravity is 'bottom' and the anchor is
+ 'bottom', change the gravity to 'top' and the anchor to 'top'.
+
+ The adjusted position is calculated given the original anchor
+ rectangle and offset, but with the new flipped anchor and gravity
+ values.
+
+ If the adjusted position also ends up being constrained, the resulting
+ position of the flip_y adjustment will be the one before the
+ adjustment.
+ </description>
+ </entry>
+ <entry name="resize_x" value="16">
+ <description summary="horizontally resize the surface">
+ Resize the surface horizontally so that it is completely
+ unconstrained.
+ </description>
+ </entry>
+ <entry name="resize_y" value="32">
+ <description summary="vertically resize the surface">
+ Resize the surface vertically so that it is completely unconstrained.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_constraint_adjustment">
+ <description summary="set the adjustment to be done when constrained">
+ Specify how the window should be positioned if the originally intended
+ position caused the surface to be constrained, meaning at least
+ partially outside positioning boundaries set by the compositor. The
+ adjustment is set by constructing a bitmask describing the adjustment to
+ be made when the surface is constrained on that axis.
+
+ If no bit for one axis is set, the compositor will assume that the child
+ surface should not change its position on that axis when constrained.
+
+ If more than one bit for one axis is set, the order of how adjustments
+ are applied is specified in the corresponding adjustment descriptions.
+
+ The default adjustment is none.
+ </description>
+ <arg name="constraint_adjustment" type="uint"
+ summary="bit mask of constraint adjustments"/>
+ </request>
+
+ <request name="set_offset">
+ <description summary="set surface position offset">
+ Specify the surface position offset relative to the position of the
+ anchor on the anchor rectangle and the anchor on the surface. For
+ example if the anchor of the anchor rectangle is at (x, y), the surface
+ has the gravity bottom|right, and the offset is (ox, oy), the calculated
+ surface position will be (x + ox, y + oy). The offset position of the
+ surface is the one used for constraint testing. See
+ set_constraint_adjustment.
+
+ An example use case is placing a popup menu on top of a user interface
+ element, while aligning the user interface element of the parent surface
+ with some user interface element placed somewhere in the popup surface.
+ </description>
+ <arg name="x" type="int" summary="surface position x offset"/>
+ <arg name="y" type="int" summary="surface position y offset"/>
+ </request>
+
+ <!-- Version 3 additions -->
+
+ <request name="set_reactive" since="3">
+ <description summary="continuously reconstrain the surface">
+ When set reactive, the surface is reconstrained if the conditions used
+ for constraining changed, e.g. the parent window moved.
+
+ If the conditions changed and the popup was reconstrained, an
+ xdg_popup.configure event is sent with updated geometry, followed by an
+ xdg_surface.configure event.
+ </description>
+ </request>
+
+ <request name="set_parent_size" since="3">
+ <description summary="">
+ Set the parent window geometry the compositor should use when
+ positioning the popup. The compositor may use this information to
+ determine the future state the popup should be constrained using. If
+ this doesn't match the dimension of the parent the popup is eventually
+ positioned against, the behavior is undefined.
+
+ The arguments are given in the surface-local coordinate space.
+ </description>
+ <arg name="parent_width" type="int"
+ summary="future window geometry width of parent"/>
+ <arg name="parent_height" type="int"
+ summary="future window geometry height of parent"/>
+ </request>
+
+ <request name="set_parent_configure" since="3">
+ <description summary="set parent configure this is a response to">
+ Set the serial of an xdg_surface.configure event this positioner will be
+ used in response to. The compositor may use this information together
+ with set_parent_size to determine what future state the popup should be
+ constrained using.
+ </description>
+ <arg name="serial" type="uint"
+ summary="serial of parent configure event"/>
+ </request>
+ </interface>
+
+ <interface name="xdg_surface" version="5">
+ <description summary="desktop user interface surface base interface">
+ An interface that may be implemented by a wl_surface, for
+ implementations that provide a desktop-style user interface.
+
+ It provides a base set of functionality required to construct user
+ interface elements requiring management by the compositor, such as
+ toplevel windows, menus, etc. The types of functionality are split into
+ xdg_surface roles.
+
+ Creating an xdg_surface does not set the role for a wl_surface. In order
+ to map an xdg_surface, the client must create a role-specific object
+ using, e.g., get_toplevel, get_popup. The wl_surface for any given
+ xdg_surface can have at most one role, and may not be assigned any role
+ not based on xdg_surface.
+
+ A role must be assigned before any other requests are made to the
+ xdg_surface object.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_surface state to take effect.
+
+ Creating an xdg_surface from a wl_surface which has a buffer attached or
+ committed is a client error, and any attempts by a client to attach or
+ manipulate a buffer prior to the first xdg_surface.configure call must
+ also be treated as errors.
+
+ After creating a role-specific object and setting it up, the client must
+ perform an initial commit without any buffer attached. The compositor
+ will reply with an xdg_surface.configure event. The client must
+ acknowledge it and is then allowed to attach a buffer to map the surface.
+
+ Mapping an xdg_surface-based role surface is defined as making it
+ possible for the surface to be shown by the compositor. Note that
+ a mapped surface is not guaranteed to be visible once it is mapped.
+
+ For an xdg_surface to be mapped by the compositor, the following
+ conditions must be met:
+ (1) the client has assigned an xdg_surface-based role to the surface
+ (2) the client has set and committed the xdg_surface state and the
+ role-dependent state to the surface
+ (3) the client has committed a buffer to the surface
+
+ A newly-unmapped surface is considered to have met condition (1) out
+ of the 3 required conditions for mapping a surface if its role surface
+ has not been destroyed, i.e. the client must perform the initial commit
+ again before attaching a buffer.
+ </description>
+
+ <enum name="error">
+ <entry name="not_constructed" value="1"
+ summary="Surface was not fully constructed"/>
+ <entry name="already_constructed" value="2"
+ summary="Surface was already constructed"/>
+ <entry name="unconfigured_buffer" value="3"
+ summary="Attaching a buffer to an unconfigured surface"/>
+ <entry name="invalid_serial" value="4"
+ summary="Invalid serial number when acking a configure event"/>
+ <entry name="invalid_size" value="5"
+ summary="Width or height was zero or negative"/>
+ <entry name="defunct_role_object" value="6"
+ summary="Surface was destroyed before its role object"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_surface">
+ Destroy the xdg_surface object. An xdg_surface must only be destroyed
+ after its role object has been destroyed, otherwise
+ a defunct_role_object error is raised.
+ </description>
+ </request>
+
+ <request name="get_toplevel">
+ <description summary="assign the xdg_toplevel surface role">
+ This creates an xdg_toplevel object for the given xdg_surface and gives
+ the associated wl_surface the xdg_toplevel role.
+
+ See the documentation of xdg_toplevel for more details about what an
+ xdg_toplevel is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_toplevel"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign the xdg_popup surface role">
+ This creates an xdg_popup object for the given xdg_surface and gives
+ the associated wl_surface the xdg_popup role.
+
+ If null is passed as a parent, a parent surface must be specified using
+ some other protocol, before committing the initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="id" type="new_id" interface="xdg_popup"/>
+ <arg name="parent" type="object" interface="xdg_surface" allow-null="true"/>
+ <arg name="positioner" type="object" interface="xdg_positioner"/>
+ </request>
+
+ <request name="set_window_geometry">
+ <description summary="set the new window geometry">
+ The window geometry of a surface is its "visible bounds" from the
+ user's perspective. Client-side decorations often have invisible
+ portions like drop-shadows which should be ignored for the
+ purposes of aligning, placing and constraining windows.
+
+ The window geometry is double buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ When maintaining a position, the compositor should treat the (x, y)
+ coordinate of the window geometry as the top left corner of the window.
+ A client changing the (x, y) window geometry coordinate should in
+ general not alter the position of the window.
+
+ Once the window geometry of the surface is set, it is not possible to
+ unset it, and it will remain the same until set_window_geometry is
+ called again, even if a new subsurface or buffer is attached.
+
+ If never set, the value is the full bounds of the surface,
+ including any subsurfaces. This updates dynamically on every
+ commit. This unset is meant for extremely simple clients.
+
+ The arguments are given in the surface-local coordinate space of
+ the wl_surface associated with this xdg_surface.
+
+ The width and height must be greater than zero. Setting an invalid size
+ will raise an invalid_size error. When applied, the effective window
+ geometry will be the set window geometry clamped to the bounding
+ rectangle of the combined geometry of the surface of the xdg_surface and
+ the associated subsurfaces.
+ </description>
+ <arg name="x" type="int"/>
+ <arg name="y" type="int"/>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ For instance, for toplevel surfaces the compositor might use this
+ information to move a surface to the top left only when the client has
+ drawn itself for the maximized or fullscreen state.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+ Acking a configure event that was never sent raises an invalid_serial
+ error.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+
+ Sending an ack_configure request consumes the serial number sent with
+ the request, as well as serial numbers sent by all configure events
+ sent on this xdg_surface prior to the configure event referenced by
+ the committed serial.
+
+ It is an error to issue multiple ack_configure requests referencing a
+ serial from the same configure event, or to issue an ack_configure
+ request referencing a serial from a configure event issued before the
+ event identified by the last ack_configure request for the same
+ xdg_surface. Doing so will raise an invalid_serial error.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event marks the end of a configure sequence. A configure
+ sequence is a set of one or more events configuring the state of the
+ xdg_surface, including the final xdg_surface.configure event.
+
+ Where applicable, xdg_surface surface roles will during a configure
+ sequence extend this event as a latched state sent as events before the
+ xdg_surface.configure event. Such events should be considered to make up
+ a set of atomically applied configuration states, where the
+ xdg_surface.configure commits the accumulated state.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ If the client receives multiple configure events before it can respond
+ to one, it is free to discard all but the last event it received.
+ </description>
+ <arg name="serial" type="uint" summary="serial of the configure event"/>
+ </event>
+
+ </interface>
+
+ <interface name="xdg_toplevel" version="5">
+ <description summary="toplevel surface">
+ This interface defines an xdg_surface role which allows a surface to,
+ among other things, set window-like properties such as maximize,
+ fullscreen, and minimize, set application-specific metadata like title and
+ id, and well as trigger user interactive operations such as interactive
+ resize and move.
+
+ Unmapping an xdg_toplevel means that the surface cannot be shown
+ by the compositor until it is explicitly mapped again.
+ All active operations (e.g., move, resize) are canceled and all
+ attributes (e.g. title, state, stacking, ...) are discarded for
+ an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to
+ the state it had right after xdg_surface.get_toplevel. The client
+ can re-map the toplevel by perfoming a commit without any buffer
+ attached, waiting for a configure event and handling it as usual (see
+ xdg_surface description).
+
+ Attaching a null buffer to a toplevel unmaps the surface.
+ </description>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the xdg_toplevel">
+ This request destroys the role surface and unmaps the surface;
+ see "Unmapping" behavior in interface section for details.
+ </description>
+ </request>
+
+ <enum name="error">
+ <entry name="invalid_resize_edge" value="0" summary="provided value is
+ not a valid variant of the resize_edge enum"/>
+ <entry name="invalid_parent" value="1"
+ summary="invalid parent toplevel"/>
+ <entry name="invalid_size" value="2"
+ summary="client provided an invalid min or max size"/>
+ </enum>
+
+ <request name="set_parent">
+ <description summary="set the parent of this surface">
+ Set the "parent" of this surface. This surface should be stacked
+ above the parent surface and all other ancestor surfaces.
+
+ Parent surfaces should be set on dialogs, toolboxes, or other
+ "auxiliary" surfaces, so that the parent is raised when the dialog
+ is raised.
+
+ Setting a null parent for a child surface unsets its parent. Setting
+ a null parent for a surface which currently has no parent is a no-op.
+
+ Only mapped surfaces can have child surfaces. Setting a parent which
+ is not mapped is equivalent to setting a null parent. If a surface
+ becomes unmapped, its children's parent is set to the parent of
+ the now-unmapped surface. If the now-unmapped surface has no parent,
+ its children's parent is unset. If the now-unmapped surface becomes
+ mapped again, its parent-child relationship is not restored.
+
+ The parent toplevel must not be one of the child toplevel's
+ descendants, and the parent must be different from the child toplevel,
+ otherwise the invalid_parent protocol error is raised.
+ </description>
+ <arg name="parent" type="object" interface="xdg_toplevel" allow-null="true"/>
+ </request>
+
+ <request name="set_title">
+ <description summary="set surface title">
+ Set a short title for the surface.
+
+ This string may be used to identify the surface in a task bar,
+ window list, or other user interface elements provided by the
+ compositor.
+
+ The string must be encoded in UTF-8.
+ </description>
+ <arg name="title" type="string"/>
+ </request>
+
+ <request name="set_app_id">
+ <description summary="set application ID">
+ Set an application identifier for the surface.
+
+ The app ID identifies the general class of applications to which
+ the surface belongs. The compositor can use this to group multiple
+ surfaces together, or to determine how to launch a new application.
+
+ For D-Bus activatable applications, the app ID is used as the D-Bus
+ service name.
+
+ The compositor shell will try to group application surfaces together
+ by their app ID. As a best practice, it is suggested to select app
+ ID's that match the basename of the application's .desktop file.
+ For example, "org.freedesktop.FooViewer" where the .desktop file is
+ "org.freedesktop.FooViewer.desktop".
+
+ Like other properties, a set_app_id request can be sent after the
+ xdg_toplevel has been mapped to update the property.
+
+ See the desktop-entry specification [0] for more details on
+ application identifiers and how they relate to well-known D-Bus
+ names and .desktop files.
+
+ [0] https://standards.freedesktop.org/desktop-entry-spec/
+ </description>
+ <arg name="app_id" type="string"/>
+ </request>
+
+ <request name="show_window_menu">
+ <description summary="show the window menu">
+ Clients implementing client-side decorations might want to show
+ a context menu when right-clicking on the decorations, giving the
+ user a menu that they can use to maximize or minimize the window.
+
+ This request asks the compositor to pop up such a window menu at
+ the given position, relative to the local surface coordinates of
+ the parent surface. There are no guarantees as to what menu items
+ the window menu contains, or even if a window menu will be drawn
+ at all.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ <arg name="x" type="int" summary="the x position to pop up the window menu at"/>
+ <arg name="y" type="int" summary="the y position to pop up the window menu at"/>
+ </request>
+
+ <request name="move">
+ <description summary="start an interactive move">
+ Start an interactive, user-driven move of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive move (touch,
+ pointer, etc).
+
+ The server may ignore move requests depending on the state of
+ the surface (e.g. fullscreen or maximized), or if the passed serial
+ is no longer valid.
+
+ If triggered, the surface will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the move. It is up to the
+ compositor to visually indicate that the move is taking place, such as
+ updating a pointer cursor, during the move. There is no guarantee
+ that the device focus will return when the move is completed.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ </request>
+
+ <enum name="resize_edge">
+ <description summary="edge values for resizing">
+ These values are used to indicate which edge of a surface
+ is being dragged in a resize operation.
+ </description>
+ <entry name="none" value="0"/>
+ <entry name="top" value="1"/>
+ <entry name="bottom" value="2"/>
+ <entry name="left" value="4"/>
+ <entry name="top_left" value="5"/>
+ <entry name="bottom_left" value="6"/>
+ <entry name="right" value="8"/>
+ <entry name="top_right" value="9"/>
+ <entry name="bottom_right" value="10"/>
+ </enum>
+
+ <request name="resize">
+ <description summary="start an interactive resize">
+ Start a user-driven, interactive resize of the surface.
+
+ This request must be used in response to some sort of user action
+ like a button press, key press, or touch down event. The passed
+ serial is used to determine the type of interactive resize (touch,
+ pointer, etc).
+
+ The server may ignore resize requests depending on the state of
+ the surface (e.g. fullscreen or maximized).
+
+ If triggered, the client will receive configure events with the
+ "resize" state enum value and the expected sizes. See the "resize"
+ enum value for more details about what is required. The client
+ must also acknowledge configure events using "ack_configure". After
+ the resize is completed, the client will receive another "configure"
+ event without the resize state.
+
+ If triggered, the surface also will lose the focus of the device
+ (wl_pointer, wl_touch, etc) used for the resize. It is up to the
+ compositor to visually indicate that the resize is taking place,
+ such as updating a pointer cursor, during the resize. There is no
+ guarantee that the device focus will return when the resize is
+ completed.
+
+ The edges parameter specifies how the surface should be resized, and
+ is one of the values of the resize_edge enum. Values not matching
+ a variant of the enum will cause a protocol error. The compositor
+ may use this information to update the surface position for example
+ when dragging the top left corner. The compositor may also use
+ this information to adapt its behavior, e.g. choose an appropriate
+ cursor image.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat" summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ <arg name="edges" type="uint" enum="resize_edge" summary="which edge or corner is being dragged"/>
+ </request>
+
+ <enum name="state">
+ <description summary="types of state on the surface">
+ The different state values used on the surface. This is designed for
+ state values like maximized, fullscreen. It is paired with the
+ configure event to ensure that both the client and the compositor
+ setting the state can be synchronized.
+
+ States set in this way are double-buffered. They will get applied on
+ the next commit.
+ </description>
+ <entry name="maximized" value="1" summary="the surface is maximized">
+ <description summary="the surface is maximized">
+ The surface is maximized. The window geometry specified in the configure
+ event must be obeyed by the client.
+
+ The client should draw without shadow or other
+ decoration outside of the window geometry.
+ </description>
+ </entry>
+ <entry name="fullscreen" value="2" summary="the surface is fullscreen">
+ <description summary="the surface is fullscreen">
+ The surface is fullscreen. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it. For
+ a surface to cover the whole fullscreened area, the geometry
+ dimensions must be obeyed by the client. For more details, see
+ xdg_toplevel.set_fullscreen.
+ </description>
+ </entry>
+ <entry name="resizing" value="3" summary="the surface is being resized">
+ <description summary="the surface is being resized">
+ The surface is being resized. The window geometry specified in the
+ configure event is a maximum; the client cannot resize beyond it.
+ Clients that have aspect ratio or cell sizing configuration can use
+ a smaller size, however.
+ </description>
+ </entry>
+ <entry name="activated" value="4" summary="the surface is now activated">
+ <description summary="the surface is now activated">
+ Client window decorations should be painted as if the window is
+ active. Do not assume this means that the window actually has
+ keyboard or pointer focus.
+ </description>
+ </entry>
+ <entry name="tiled_left" value="5" since="2">
+ <description summary="the surface’s left edge is tiled">
+ The window is currently in a tiled layout and the left edge is
+ considered to be adjacent to another part of the tiling grid.
+ </description>
+ </entry>
+ <entry name="tiled_right" value="6" since="2">
+ <description summary="the surface’s right edge is tiled">
+ The window is currently in a tiled layout and the right edge is
+ considered to be adjacent to another part of the tiling grid.
+ </description>
+ </entry>
+ <entry name="tiled_top" value="7" since="2">
+ <description summary="the surface’s top edge is tiled">
+ The window is currently in a tiled layout and the top edge is
+ considered to be adjacent to another part of the tiling grid.
+ </description>
+ </entry>
+ <entry name="tiled_bottom" value="8" since="2">
+ <description summary="the surface’s bottom edge is tiled">
+ The window is currently in a tiled layout and the bottom edge is
+ considered to be adjacent to another part of the tiling grid.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_max_size">
+ <description summary="set the maximum size">
+ Set a maximum size for the window.
+
+ The client can specify a maximum size so that the compositor does
+ not try to configure the window beyond this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered. They will get applied
+ on the next commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the maximum
+ size. The compositor may decide to ignore the values set by the
+ client and request a larger size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected maximum size in the given dimension.
+ As a result, a client wishing to reset the maximum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a maximum size to be smaller than the minimum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width or height will result in a
+ invalid_size error.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="set_min_size">
+ <description summary="set the minimum size">
+ Set a minimum size for the window.
+
+ The client can specify a minimum size so that the compositor does
+ not try to configure the window below this size.
+
+ The width and height arguments are in window geometry coordinates.
+ See xdg_surface.set_window_geometry.
+
+ Values set in this way are double-buffered. They will get applied
+ on the next commit.
+
+ The compositor can use this information to allow or disallow
+ different states like maximize or fullscreen and draw accurate
+ animations.
+
+ Similarly, a tiling window manager may use this information to
+ place and resize client windows in a more effective way.
+
+ The client should not rely on the compositor to obey the minimum
+ size. The compositor may decide to ignore the values set by the
+ client and request a smaller size.
+
+ If never set, or a value of zero in the request, means that the
+ client has no expected minimum size in the given dimension.
+ As a result, a client wishing to reset the minimum size
+ to an unspecified state can use zero for width and height in the
+ request.
+
+ Requesting a minimum size to be larger than the maximum size of
+ a surface is illegal and will result in an invalid_size error.
+
+ The width and height must be greater than or equal to zero. Using
+ strictly negative values for width and height will result in a
+ invalid_size error.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </request>
+
+ <request name="set_maximized">
+ <description summary="maximize the window">
+ Maximize the surface.
+
+ After requesting that the surface should be maximized, the compositor
+ will respond by emitting a configure event. Whether this configure
+ actually sets the window maximized is subject to compositor policies.
+ The client must then update its content, drawing in the configured
+ state. The client must also acknowledge the configure when committing
+ the new content (see ack_configure).
+
+ It is up to the compositor to decide how and where to maximize the
+ surface, for example which output and what region of the screen should
+ be used.
+
+ If the surface was already maximized, the compositor will still emit
+ a configure event with the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+ </description>
+ </request>
+
+ <request name="unset_maximized">
+ <description summary="unmaximize the window">
+ Unmaximize the surface.
+
+ After requesting that the surface should be unmaximized, the compositor
+ will respond by emitting a configure event. Whether this actually
+ un-maximizes the window is subject to compositor policies.
+ If available and applicable, the compositor will include the window
+ geometry dimensions the window had prior to being maximized in the
+ configure event. The client must then update its content, drawing it in
+ the configured state. The client must also acknowledge the configure
+ when committing the new content (see ack_configure).
+
+ It is up to the compositor to position the surface after it was
+ unmaximized; usually the position the surface had before maximizing, if
+ applicable.
+
+ If the surface was already not maximized, the compositor will still
+ emit a configure event without the "maximized" state.
+
+ If the surface is in a fullscreen state, this request has no direct
+ effect. It may alter the state the surface is returned to when
+ unmaximized unless overridden by the compositor.
+ </description>
+ </request>
+
+ <request name="set_fullscreen">
+ <description summary="set the window as fullscreen on an output">
+ Make the surface fullscreen.
+
+ After requesting that the surface should be fullscreened, the
+ compositor will respond by emitting a configure event. Whether the
+ client is actually put into a fullscreen state is subject to compositor
+ policies. The client must also acknowledge the configure when
+ committing the new content (see ack_configure).
+
+ The output passed by the request indicates the client's preference as
+ to which display it should be set fullscreen on. If this value is NULL,
+ it's up to the compositor to choose which display will be used to map
+ this surface.
+
+ If the surface doesn't cover the whole output, the compositor will
+ position the surface in the center of the output and compensate with
+ with border fill covering the rest of the output. The content of the
+ border fill is undefined, but should be assumed to be in some way that
+ attempts to blend into the surrounding area (e.g. solid black).
+
+ If the fullscreened surface is not opaque, the compositor must make
+ sure that other screen content not part of the same surface tree (made
+ up of subsurfaces, popups or similarly coupled surfaces) are not
+ visible below the fullscreened surface.
+ </description>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ </request>
+
+ <request name="unset_fullscreen">
+ <description summary="unset the window as fullscreen">
+ Make the surface no longer fullscreen.
+
+ After requesting that the surface should be unfullscreened, the
+ compositor will respond by emitting a configure event.
+ Whether this actually removes the fullscreen state of the client is
+ subject to compositor policies.
+
+ Making a surface unfullscreen sets states for the surface based on the following:
+ * the state(s) it may have had before becoming fullscreen
+ * any state(s) decided by the compositor
+ * any state(s) requested by the client while the surface was fullscreen
+
+ The compositor may include the previous window geometry dimensions in
+ the configure event, if applicable.
+
+ The client must also acknowledge the configure when committing the new
+ content (see ack_configure).
+ </description>
+ </request>
+
+ <request name="set_minimized">
+ <description summary="set the window as minimized">
+ Request that the compositor minimize your surface. There is no
+ way to know if the surface is currently minimized, nor is there
+ any way to unset minimization on this surface.
+
+ If you are looking to throttle redrawing when minimized, please
+ instead use the wl_surface.frame event for this, as this will
+ also work with live previews on windows in Alt-Tab, Expose or
+ similar compositor features.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ This configure event asks the client to resize its toplevel surface or
+ to change its state. The configured state should not be applied
+ immediately. See xdg_surface.configure for details.
+
+ The width and height arguments specify a hint to the window
+ about how its surface should be resized in window geometry
+ coordinates. See set_window_geometry.
+
+ If the width or height arguments are zero, it means the client
+ should decide its own window dimension. This may happen when the
+ compositor needs to configure the state of the surface but doesn't
+ have any information about any previous or expected dimension.
+
+ The states listed in the event specify how the width/height
+ arguments should be interpreted, and possibly how it should be
+ drawn.
+
+ Clients must send an ack_configure in response to this event. See
+ xdg_surface.configure and xdg_surface.ack_configure for details.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ <arg name="states" type="array"/>
+ </event>
+
+ <event name="close">
+ <description summary="surface wants to be closed">
+ The close event is sent by the compositor when the user
+ wants the surface to be closed. This should be equivalent to
+ the user clicking the close button in client-side decorations,
+ if your application has any.
+
+ This is only a request that the user intends to close the
+ window. The client may choose to ignore this request, or show
+ a dialog to ask the user to save their data, etc.
+ </description>
+ </event>
+
+ <!-- Version 4 additions -->
+
+ <event name="configure_bounds" since="4">
+ <description summary="recommended window geometry bounds">
+ The configure_bounds event may be sent prior to a xdg_toplevel.configure
+ event to communicate the bounds a window geometry size is recommended
+ to constrain to.
+
+ The passed width and height are in surface coordinate space. If width
+ and height are 0, it means bounds is unknown and equivalent to as if no
+ configure_bounds event was ever sent for this surface.
+
+ The bounds can for example correspond to the size of a monitor excluding
+ any panels or other shell components, so that a surface isn't created in
+ a way that it cannot fit.
+
+ The bounds may change at any point, and in such a case, a new
+ xdg_toplevel.configure_bounds will be sent, followed by
+ xdg_toplevel.configure and xdg_surface.configure.
+ </description>
+ <arg name="width" type="int"/>
+ <arg name="height" type="int"/>
+ </event>
+
+ <!-- Version 5 additions -->
+
+ <enum name="wm_capabilities" since="5">
+ <entry name="window_menu" value="1" summary="show_window_menu is available"/>
+ <entry name="maximize" value="2" summary="set_maximized and unset_maximized are available"/>
+ <entry name="fullscreen" value="3" summary="set_fullscreen and unset_fullscreen are available"/>
+ <entry name="minimize" value="4" summary="set_minimized is available"/>
+ </enum>
+
+ <event name="wm_capabilities" since="5">
+ <description summary="compositor capabilities">
+ This event advertises the capabilities supported by the compositor. If
+ a capability isn't supported, clients should hide or disable the UI
+ elements that expose this functionality. For instance, if the
+ compositor doesn't advertise support for minimized toplevels, a button
+ triggering the set_minimized request should not be displayed.
+
+ The compositor will ignore requests it doesn't support. For instance,
+ a compositor which doesn't advertise support for minimized will ignore
+ set_minimized requests.
+
+ Compositors must send this event once before the first
+ xdg_surface.configure event. When the capabilities change, compositors
+ must send this event again and then send an xdg_surface.configure
+ event.
+
+ The configured state should not be applied immediately. See
+ xdg_surface.configure for details.
+
+ The capabilities are sent as an array of 32-bit unsigned integers in
+ native endianness.
+ </description>
+ <arg name="capabilities" type="array" summary="array of 32-bit capabilities"/>
+ </event>
+ </interface>
+
+ <interface name="xdg_popup" version="5">
+ <description summary="short-lived, popup surfaces for menus">
+ A popup surface is a short-lived, temporary surface. It can be used to
+ implement for example menus, popovers, tooltips and other similar user
+ interface concepts.
+
+ A popup can be made to take an explicit grab. See xdg_popup.grab for
+ details.
+
+ When the popup is dismissed, a popup_done event will be sent out, and at
+ the same time the surface will be unmapped. See the xdg_popup.popup_done
+ event for details.
+
+ Explicitly destroying the xdg_popup object will also dismiss the popup and
+ unmap the surface. Clients that want to dismiss the popup when another
+ surface of their own is clicked should dismiss the popup using the destroy
+ request.
+
+ A newly created xdg_popup will be stacked on top of all previously created
+ xdg_popup surfaces associated with the same xdg_toplevel.
+
+ The parent of an xdg_popup must be mapped (see the xdg_surface
+ description) before the xdg_popup itself.
+
+ The client must call wl_surface.commit on the corresponding wl_surface
+ for the xdg_popup state to take effect.
+ </description>
+
+ <enum name="error">
+ <entry name="invalid_grab" value="0"
+ summary="tried to grab after being mapped"/>
+ </enum>
+
+ <request name="destroy" type="destructor">
+ <description summary="remove xdg_popup interface">
+ This destroys the popup. Explicitly destroying the xdg_popup
+ object will also dismiss the popup, and unmap the surface.
+
+ If this xdg_popup is not the "topmost" popup, a protocol error
+ will be sent.
+ </description>
+ </request>
+
+ <request name="grab">
+ <description summary="make the popup take an explicit grab">
+ This request makes the created popup take an explicit grab. An explicit
+ grab will be dismissed when the user dismisses the popup, or when the
+ client destroys the xdg_popup. This can be done by the user clicking
+ outside the surface, using the keyboard, or even locking the screen
+ through closing the lid or a timeout.
+
+ If the compositor denies the grab, the popup will be immediately
+ dismissed.
+
+ This request must be used in response to some sort of user action like a
+ button press, key press, or touch down event. The serial number of the
+ event should be passed as 'serial'.
+
+ The parent of a grabbing popup must either be an xdg_toplevel surface or
+ another xdg_popup with an explicit grab. If the parent is another
+ xdg_popup it means that the popups are nested, with this popup now being
+ the topmost popup.
+
+ Nested popups must be destroyed in the reverse order they were created
+ in, e.g. the only popup you are allowed to destroy at all times is the
+ topmost one.
+
+ When compositors choose to dismiss a popup, they may dismiss every
+ nested grabbing popup as well. When a compositor dismisses popups, it
+ will follow the same dismissing order as required from the client.
+
+ If the topmost grabbing popup is destroyed, the grab will be returned to
+ the parent of the popup, if that parent previously had an explicit grab.
+
+ If the parent is a grabbing popup which has already been dismissed, this
+ popup will be immediately dismissed. If the parent is a popup that did
+ not take an explicit grab, an error will be raised.
+
+ During a popup grab, the client owning the grab will receive pointer
+ and touch events for all their surfaces as normal (similar to an
+ "owner-events" grab in X11 parlance), while the top most grabbing popup
+ will always have keyboard focus.
+ </description>
+ <arg name="seat" type="object" interface="wl_seat"
+ summary="the wl_seat of the user event"/>
+ <arg name="serial" type="uint" summary="the serial of the user event"/>
+ </request>
+
+ <event name="configure">
+ <description summary="configure the popup surface">
+ This event asks the popup surface to configure itself given the
+ configuration. The configured state should not be applied immediately.
+ See xdg_surface.configure for details.
+
+ The x and y arguments represent the position the popup was placed at
+ given the xdg_positioner rule, relative to the upper left corner of the
+ window geometry of the parent surface.
+
+ For version 2 or older, the configure event for an xdg_popup is only
+ ever sent once for the initial configuration. Starting with version 3,
+ it may be sent again if the popup is setup with an xdg_positioner with
+ set_reactive requested, or in response to xdg_popup.reposition requests.
+ </description>
+ <arg name="x" type="int"
+ summary="x position relative to parent surface window geometry"/>
+ <arg name="y" type="int"
+ summary="y position relative to parent surface window geometry"/>
+ <arg name="width" type="int" summary="window geometry width"/>
+ <arg name="height" type="int" summary="window geometry height"/>
+ </event>
+
+ <event name="popup_done">
+ <description summary="popup interaction is done">
+ The popup_done event is sent out when a popup is dismissed by the
+ compositor. The client should destroy the xdg_popup object at this
+ point.
+ </description>
+ </event>
+
+ <!-- Version 3 additions -->
+
+ <request name="reposition" since="3">
+ <description summary="recalculate the popup's location">
+ Reposition an already-mapped popup. The popup will be placed given the
+ details in the passed xdg_positioner object, and a
+ xdg_popup.repositioned followed by xdg_popup.configure and
+ xdg_surface.configure will be emitted in response. Any parameters set
+ by the previous positioner will be discarded.
+
+ The passed token will be sent in the corresponding
+ xdg_popup.repositioned event. The new popup position will not take
+ effect until the corresponding configure event is acknowledged by the
+ client. See xdg_popup.repositioned for details. The token itself is
+ opaque, and has no other special meaning.
+
+ If multiple reposition requests are sent, the compositor may skip all
+ but the last one.
+
+ If the popup is repositioned in response to a configure event for its
+ parent, the client should send an xdg_positioner.set_parent_configure
+ and possibly an xdg_positioner.set_parent_size request to allow the
+ compositor to properly constrain the popup.
+
+ If the popup is repositioned together with a parent that is being
+ resized, but not in response to a configure event, the client should
+ send an xdg_positioner.set_parent_size request.
+ </description>
+ <arg name="positioner" type="object" interface="xdg_positioner"/>
+ <arg name="token" type="uint" summary="reposition request token"/>
+ </request>
+
+ <event name="repositioned" since="3">
+ <description summary="signal the completion of a repositioned request">
+ The repositioned event is sent as part of a popup configuration
+ sequence, together with xdg_popup.configure and lastly
+ xdg_surface.configure to notify the completion of a reposition request.
+
+ The repositioned event is to notify about the completion of a
+ xdg_popup.reposition request. The token argument is the token passed
+ in the xdg_popup.reposition request.
+
+ Immediately after this event is emitted, xdg_popup.configure and
+ xdg_surface.configure will be sent with the updated size and position,
+ as well as a new configure serial.
+
+ The client should optionally update the content of the popup, but must
+ acknowledge the new popup configuration for the new position to take
+ effect. See xdg_surface.ack_configure for details.
+ </description>
+ <arg name="token" type="uint" summary="reposition request token"/>
+ </event>
+
+ </interface>
+</protocol>
+
diff --git a/src/bar.rs b/src/bar.rs
index 90215a2..9f55710 100644
--- a/src/bar.rs
+++ b/src/bar.rs
@@ -1,34 +1,34 @@
use std::collections::BinaryHeap;
+use std::ffi::CString;
use pangocairo::cairo;
-use smithay_client_toolkit::{
- reexports::client::protocol::{wl_output, wl_seat, wl_shm},
- shell::layer::LayerSurface,
-};
-use crate::{
- button_manager::ButtonManager,
- config::Config,
- i3bar_protocol::{self, Block, MinWidth},
- ord_adaptor::DefaultLess,
- pointer_btn::PointerBtn,
- river_protocols::{control::RiverControlState, status::RiverOutputStatus},
- shared_state::SharedState,
- state::ComputedBlock,
- tags::{compute_tag_label, TagState, TagsInfo},
- text::{self, ComputedText, RenderOptions},
-};
+use wayrs_client::connection::Connection;
+
+use crate::button_manager::ButtonManager;
+use crate::config::Config;
+use crate::i3bar_protocol::{self, Block, MinWidth};
+use crate::ord_adaptor::DefaultLess;
+use crate::pointer_btn::PointerBtn;
+use crate::protocol::*;
+use crate::shared_state::SharedState;
+use crate::state::{ComputedBlock, State};
+use crate::tags::{compute_tag_label, TagState, TagsInfo};
+use crate::text::{self, ComputedText, RenderOptions};
pub struct Bar {
- pub output: wl_output::WlOutput,
+ pub output: WlOutput,
+ pub output_reg_name: u32,
pub configured: bool,
+ pub frame_cb: Option<WlCallback>,
pub width: u32,
pub height: u32,
pub scale: i32,
- pub layer: LayerSurface,
+ pub surface: WlSurface,
+ pub layer_surface: ZwlrLayerSurfaceV1,
pub blocks_btns: ButtonManager<(Option<String>, Option<String>)>,
- pub river_output_status: Option<RiverOutputStatus>,
- pub river_control: Option<RiverControlState>,
+ pub river_output_status: Option<ZriverOutputStatusV1>,
+ pub river_control: Option<ZriverControlV1>,
pub layout_name: Option<String>,
pub layout_name_computed: Option<ComputedText>,
pub tags_btns: ButtonManager,
@@ -37,16 +37,6 @@ pub struct Bar {
}
impl Bar {
- pub fn configure(&mut self, ss: &mut SharedState, width: u32) {
- if self.width != width && width != 0 {
- self.width = width;
- self.layer.set_exclusive_zone(self.height as i32);
- }
-
- self.configured = true;
- self.draw(ss);
- }
-
pub fn has_tags_provider(&self) -> bool {
// TODO: add more tags providers
self.river_output_status.is_some()
@@ -59,20 +49,23 @@ impl Bar {
pub fn click(
&mut self,
+ conn: &mut Connection<State>,
ss: &mut SharedState,
button: PointerBtn,
- seat: &wl_seat::WlSeat,
+ seat: WlSeat,
x: f64,
_y: f64,
) -> anyhow::Result<()> {
if let Some(tag) = self.tags_btns.click(x) {
if let Some(river_control) = &self.river_control {
let cmd = match button {
- PointerBtn::Left => "set-focused-tags",
- PointerBtn::Right => "toggle-focused-tags",
+ PointerBtn::Left => wayrs_client::cstr!("set-focused-tags"),
+ PointerBtn::Right => wayrs_client::cstr!("toggle-focused-tags"),
_ => return Ok(()),
};
- river_control.run_command(&ss.qh, seat, [cmd.into(), (1u32 << tag).to_string()]);
+ river_control.add_argument(conn, cmd.into());
+ river_control.add_argument(conn, CString::new((1u32 << tag).to_string()).unwrap());
+ river_control.run_command(conn, seat);
}
} else if let Some((name, instance)) = self.blocks_btns.click(x) {
if let Some(cmd) = &mut ss.status_cmd {
@@ -87,10 +80,8 @@ impl Bar {
Ok(())
}
- pub fn draw(&mut self, ss: &mut SharedState) {
- if !self.configured {
- return;
- }
+ pub fn frame(&mut self, conn: &mut Connection<State>, ss: &mut SharedState) {
+ assert!(self.configured);
let stride = 4 * self.width as i32;
let width = self.width as i32;
@@ -98,15 +89,13 @@ impl Bar {
let width_f = width as f64;
let height_f = height as f64;
- let (buffer, canvas) = ss
- .pool
- .create_buffer(
- width * self.scale,
- height * self.scale,
- stride * self.scale,
- wl_shm::Format::Argb8888,
- )
- .expect("create buffer");
+ let (buffer, canvas) = ss.shm.alloc_buffer(
+ conn,
+ width * self.scale,
+ height * self.scale,
+ stride * self.scale,
+ wl_shm::Format::Argb8888,
+ );
let cairo_surf = unsafe {
cairo::ImageSurface::create_for_data_unsafe(
@@ -121,7 +110,7 @@ impl Bar {
let cairo_ctx = cairo::Context::new(&cairo_surf).expect("cairo context");
cairo_ctx.scale(self.scale as f64, self.scale as f64);
- self.layer.wl_surface().set_buffer_scale(self.scale);
+ self.surface.set_buffer_scale(conn, self.scale);
// Background
cairo_ctx.save().unwrap();
@@ -181,34 +170,36 @@ impl Bar {
}
// Display layout name
- if let Some(layout_name) = &self.layout_name {
- let text = self.layout_name_computed.get_or_insert_with(|| {
- ComputedText::new(
- layout_name,
- text::Attributes {
- font: &ss.config.font,
- padding_left: 25.0,
- padding_right: 25.0,
- min_width: None,
- align: Default::default(),
- markup: false,
- },
+ if ss.config.show_layout_name {
+ if let Some(layout_name) = &self.layout_name {
+ let text = self.layout_name_computed.get_or_insert_with(|| {
+ ComputedText::new(
+ layout_name,
+ text::Attributes {
+ font: &ss.config.font,
+ padding_left: 25.0,
+ padding_right: 25.0,
+ min_width: None,
+ align: Default::default(),
+ markup: false,
+ },
+ &cairo_ctx,
+ )
+ });
+ text.render(
&cairo_ctx,
- )
- });
- text.render(
- &cairo_ctx,
- RenderOptions {
- x_offset: offset_left,
- bar_height: height_f,
- fg_color: ss.config.tag_inactive_fg,
- bg_color: None,
- r_left: 0.0,
- r_right: 0.0,
- overlap: 0.0,
- },
- );
- offset_left += text.width;
+ RenderOptions {
+ x_offset: offset_left,
+ bar_height: height_f,
+ fg_color: ss.config.tag_inactive_fg,
+ bg_color: None,
+ r_left: 0.0,
+ r_right: 0.0,
+ overlap: 0.0,
+ },
+ );
+ offset_left += text.width;
+ }
}
// Display the blocks
@@ -223,16 +214,17 @@ impl Bar {
height_f,
);
- // Attach the buffer to the surface and mark the entire surface as damaged
- buffer
- .attach_to(self.layer.wl_surface())
- .expect("attaching buffer");
- self.layer
- .wl_surface()
- .damage_buffer(0, 0, width * self.scale, height * self.scale);
+ self.surface.attach(conn, buffer, 0, 0);
+ self.surface
+ .damage_buffer(conn, 0, 0, width * self.scale, height * self.scale);
+ self.surface.commit(conn);
+ }
- // Finally, commit the surface
- self.layer.wl_surface().commit();
+ pub fn request_frame(&mut self, conn: &mut Connection<State>) {
+ if self.configured && self.frame_cb.is_none() {
+ self.frame_cb = Some(self.surface.frame(conn));
+ self.surface.commit(conn);
+ }
}
}
diff --git a/src/config.rs b/src/config.rs
index 2af9af0..a5b05d0 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -34,6 +34,7 @@ pub struct Config {
// misc
pub hide_inactive_tags: bool,
pub invert_touchpad_scrolling: bool,
+ pub show_layout_name: bool,
}
impl Default for Config {
@@ -60,6 +61,7 @@ impl Default for Config {
command: None,
hide_inactive_tags: true,
invert_touchpad_scrolling: true,
+ show_layout_name: true,
}
}
}
diff --git a/src/cursor.rs b/src/cursor.rs
new file mode 100644
index 0000000..57db8bb
--- /dev/null
+++ b/src/cursor.rs
@@ -0,0 +1,109 @@
+use std::io;
+
+use wayrs_client::connection::Connection;
+use wayrs_client::protocol::wl_compositor::WlCompositor;
+use wayrs_client::protocol::wl_pointer::WlPointer;
+use wayrs_client::protocol::wl_shm::Format;
+use wayrs_client::protocol::wl_surface::WlSurface;
+
+use wayrs_shm_alloc::ShmAlloc;
+
+use xcursor::parser::Image;
+
+use crate::state::State;
+
+#[derive(Debug, thiserror::Error)]
+pub enum CursorError {
+ #[error("theme not found")]
+ ThemeNotFound,
+ #[error("theme could not be parsed")]
+ ThemeParseError,
+ #[error(transparent)]
+ ReadError(#[from] io::Error),
+}
+
+pub struct Cursor {
+ pub surface: WlSurface,
+ pub images: Vec<Image>,
+}
+
+impl Cursor {
+ pub fn new(
+ conn: &mut Connection<State>,
+ compositor: WlCompositor,
+ ) -> Result<Self, CursorError> {
+ let theme_name = std::env::var("XCURSOR_THEME").ok();
+ let theme_name = theme_name.as_deref().unwrap_or("default");
+
+ let theme_path = xcursor::CursorTheme::load(theme_name)
+ .load_icon("default")
+ .ok_or(CursorError::ThemeNotFound)?;
+ let raw_theme = std::fs::read(theme_path)?;
+
+ let mut images =
+ xcursor::parser::parse_xcursor(&raw_theme).ok_or(CursorError::ThemeParseError)?;
+ images.sort_unstable_by_key(|img| img.size);
+
+ let surface = compositor.create_surface(conn);
+
+ Ok(Cursor { surface, images })
+ }
+
+ pub fn set(
+ &self,
+ conn: &mut Connection<State>,
+ serial: u32,
+ pointer: WlPointer,
+ scale: u32,
+ shm: &mut ShmAlloc,
+ ) {
+ let target_size = std::env::var("XCURSOR_SIZE")
+ .ok()
+ .and_then(|x| x.parse().ok())
+ .unwrap_or(24)
+ * scale;
+
+ let image = match self
+ .images
+ .binary_search_by_key(&target_size, |img| img.size)
+ {
+ Ok(indx) => &self.images[indx],
+ Err(indx) if indx == 0 => self.images.first().unwrap(),
+ Err(indx) if indx >= self.images.len() => self.images.last().unwrap(),
+ Err(indx) => {
+ let a = &self.images[indx - 1];
+ let b = &self.images[indx];
+ if target_size - a.size < b.size - target_size {
+ a
+ } else {
+ b
+ }
+ }
+ };
+
+ let (buffer, canvas) = shm.alloc_buffer(
+ conn,
+ image.width as i32,
+ image.height as i32,
+ (image.width * 4) as i32,
+ Format::Argb8888,
+ );
+
+ assert_eq!(image.pixels_argb.len(), canvas.len(),);
+ canvas.copy_from_slice(&image.pixels_rgba);
+
+ self.surface.attach(conn, buffer, 0, 0);
+ self.surface
+ .damage_buffer(conn, 0, 0, image.width as i32, image.height as i32);
+ self.surface.set_buffer_scale(conn, scale as i32);
+ self.surface.commit(conn);
+
+ pointer.set_cursor(
+ conn,
+ serial,
+ self.surface,
+ (image.xhot / scale) as i32,
+ (image.yhot / scale) as i32,
+ );
+ }
+}
diff --git a/src/main.rs b/src/main.rs
index 3dad693..73c2d6c 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::single_component_path_imports)]
+
#[macro_use]
extern crate anyhow;
#[macro_use]
@@ -7,10 +9,11 @@ mod bar;
mod button_manager;
mod color;
mod config;
+mod cursor;
mod i3bar_protocol;
mod ord_adaptor;
mod pointer_btn;
-mod river_protocols;
+mod protocol;
mod shared_state;
mod state;
mod status_cmd;
@@ -18,45 +21,35 @@ mod tags;
mod text;
mod utils;
-use std::os::unix::io::AsRawFd;
-
-use smithay_client_toolkit::reexports::client::Connection;
-use tokio::io::{unix::AsyncFd, Interest};
+use wayrs_client::connection::Connection;
use state::State;
-use wayland_client::globals::registry_queue_init;
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
env_logger::init();
- let conn = Connection::connect_to_env()?;
- let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
- let mut state = State::new(&mut event_queue, &globals);
-
- let async_fd = AsyncFd::with_interest(
- event_queue.prepare_read()?.connection_fd().as_raw_fd(),
- Interest::READABLE,
- )?;
+ let mut conn = Connection::connect()?;
+ let globals = conn.async_collect_initial_globals().await?;
+ let mut state = State::new(&mut conn, &globals);
+ conn.async_flush().await?;
loop {
tokio::select! {
- readable = async_fd.readable() => {
- readable?.clear_ready();
- event_queue.prepare_read()?.read()?;
- event_queue.dispatch_pending(&mut state)?;
- event_queue.flush()?;
+ recv_events = conn.async_recv_events() => {
+ recv_events?;
+ conn.dispatch_events(&mut state)?;
+ conn.async_flush().await?;
}
- readable = state.wait_for_status_cmd() => {
- readable?.clear_ready();
- if let Err(e) = state.notify_available() {
+ reat_res = state.status_cmd_read() => {
+ reat_res?;
+ if let Err(e) = state.status_cmd_notify_available(&mut conn) {
if let Some(mut status_cmd) = state.shared_state.status_cmd.take() {
let _ = status_cmd.child.kill();
}
- state.set_error(e.to_string());
- state.draw_all();
+ state.set_error(&mut conn, e.to_string());
}
- event_queue.flush()?;
+ conn.async_flush().await?;
}
}
}
diff --git a/src/protocol.rs b/src/protocol.rs
new file mode 100644
index 0000000..ac8b2c1
--- /dev/null
+++ b/src/protocol.rs
@@ -0,0 +1,8 @@
+#![allow(clippy::enum_variant_names)]
+
+use wayrs_client;
+pub use wayrs_client::protocol::*;
+wayrs_scanner::generate!("protocols/xdg-shell.xml");
+wayrs_scanner::generate!("protocols/wlr-layer-shell-unstable-v1.xml");
+wayrs_scanner::generate!("protocols/river-status-unstable-v1.xml");
+wayrs_scanner::generate!("protocols/river-control-unstable-v1.xml");
diff --git a/src/river_protocols.rs b/src/river_protocols.rs
deleted file mode 100644
index 0a2cf91..0000000
--- a/src/river_protocols.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![allow(clippy::enum_variant_names)]
-
-pub mod control;
-pub mod status;
diff --git a/src/river_protocols/control.rs b/src/river_protocols/control.rs
deleted file mode 100644
index 7cce23d..0000000
--- a/src/river_protocols/control.rs
+++ /dev/null
@@ -1,112 +0,0 @@
-use wayland_client::globals::{BindError, GlobalList};
-use wayland_client::{protocol::wl_seat, Connection, Dispatch, QueueHandle};
-
-use protocol::{zriver_command_callback_v1, zriver_control_v1};
-
-#[derive(Debug, Clone)]
-pub struct RiverControlState {
- control: zriver_control_v1::ZriverControlV1,
-}
-
-impl RiverControlState {
- pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Result<Self, BindError>
- where
- D: Dispatch<zriver_control_v1::ZriverControlV1, (), D> + 'static,
- {
- let control = globals.bind(qh, 1..=1, ())?;
- Ok(Self { control })
- }
-
- pub fn run_command<D, Args, Arg>(&self, qh: &QueueHandle<D>, seat: &wl_seat::WlSeat, args: Args)
- where
- D: Dispatch<zriver_command_callback_v1::ZriverCommandCallbackV1, RiverCommandCallbackData>
- + 'static,
- Args: IntoIterator<Item = Arg>,
- Arg: Into<String>,
- {
- for arg in args {
- self.control.add_argument(arg.into());
- }
- self.control
- .run_command(seat, qh, RiverCommandCallbackData {});
- }
-}
-
-pub trait RiverControlHandler: Sized {
- fn river_control_state(&mut self) -> &mut RiverControlState;
-
- fn command_failure(&mut self, conn: &Connection, qh: &QueueHandle<Self>, message: String);
-
- fn command_success(&mut self, conn: &Connection, qh: &QueueHandle<Self>, message: String);
-}
-
-#[derive(Debug)]
-pub struct RiverCommandCallbackData {
- // This is empty right now, but may be populated in the future.
-}
-
-#[macro_export]
-macro_rules! delegate_river_control {
- ($ty: ty) => {
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::control::protocol::zriver_control_v1::ZriverControlV1: ()
- ] => $crate::river_protocols::control::RiverControlState);
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::control::protocol::zriver_command_callback_v1::ZriverCommandCallbackV1: $crate::river_protocols::control::RiverCommandCallbackData
- ] => $crate::river_protocols::control::RiverControlState);
- };
-}
-
-impl<D> Dispatch<zriver_control_v1::ZriverControlV1, (), D> for RiverControlState
-where
- D: Dispatch<zriver_control_v1::ZriverControlV1, ()> + RiverControlHandler + 'static,
-{
- fn event(
- _: &mut D,
- _: &zriver_control_v1::ZriverControlV1,
- _: zriver_control_v1::Event,
- _: &(),
- _: &Connection,
- _: &QueueHandle<D>,
- ) {
- unreachable!("zriver_control_v1 has no events")
- }
-}
-
-impl<D> Dispatch<zriver_command_callback_v1::ZriverCommandCallbackV1, RiverCommandCallbackData, D>
- for RiverControlState
-where
- D: Dispatch<zriver_command_callback_v1::ZriverCommandCallbackV1, RiverCommandCallbackData>
- + RiverControlHandler
- + 'static,
-{
- fn event(
- data: &mut D,
- _callback: &zriver_command_callback_v1::ZriverCommandCallbackV1,
- event: zriver_command_callback_v1::Event,
- _udata: &RiverCommandCallbackData,
- conn: &Connection,
- qh: &QueueHandle<D>,
- ) {
- use zriver_command_callback_v1::Event;
- match event {
- Event::Success { output } => data.command_success(conn, qh, output),
- Event::Failure { failure_message } => data.command_success(conn, qh, failure_message),
- }
- }
-}
-
-pub mod protocol {
- #![allow(non_upper_case_globals)]
-
- use smithay_client_toolkit::reexports::client as wayland_client;
- use smithay_client_toolkit::reexports::client::protocol::*;
-
- pub mod __interfaces {
- use smithay_client_toolkit::reexports::client::protocol::__interfaces::*;
- wayland_scanner::generate_interfaces!("protocols/river-control-unstable-v1.xml");
- }
- use self::__interfaces::*;
-
- wayland_scanner::generate_client_code!("protocols/river-control-unstable-v1.xml");
-}
diff --git a/src/river_protocols/status.rs b/src/river_protocols/status.rs
deleted file mode 100644
index ad93279..0000000
--- a/src/river_protocols/status.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use wayland_client::{
- globals::{BindError, GlobalList},
- protocol::wl_output,
- Connection, Dispatch, QueueHandle,
-};
-
-use protocol::{zriver_output_status_v1, zriver_status_manager_v1};
-
-#[derive(Debug, Clone)]
-pub struct RiverStatusState {
- status_manager: zriver_status_manager_v1::ZriverStatusManagerV1,
-}
-
-impl RiverStatusState {
- pub fn new<D>(globals: &GlobalList, qh: &QueueHandle<D>) -> Result<Self, BindError>
- where
- D: Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, (), D> + 'static,
- {
- let status_manager = globals.bind(qh, 1..=4, ())?;
- Ok(Self { status_manager })
- }
-
- #[must_use = "The output status is destroyed if dropped"]
- pub fn new_output_status<D>(
- &mut self,
- qh: &QueueHandle<D>,
- output: &wl_output::WlOutput,
- ) -> RiverOutputStatus
- where
- D: Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, ()>
- + Dispatch<zriver_output_status_v1::ZriverOutputStatusV1, RiverStatusData>
- + 'static,
- {
- RiverOutputStatus(self.status_manager.get_river_output_status(
- output,
- qh,
- RiverStatusData {
- output: output.clone(),
- },
- ))
- }
-}
-
-pub trait RiverStatusHandler: Sized {
- fn river_status_state(&mut self) -> &mut RiverStatusState;
-
- fn focused_tags_updated(&mut self, output: &wl_output::WlOutput, focused: u32);
-
- fn urgent_tags_updated(&mut self, output: &wl_output::WlOutput, urgent: u32);
-
- fn views_tags_updated(&mut self, output: &wl_output::WlOutput, tags: Vec<u32>);
-
- fn layout_name_updated(&mut self, output: &wl_output::WlOutput, layout_name: Option<String>);
-}
-
-#[derive(Debug)]
-pub struct RiverOutputStatus(zriver_output_status_v1::ZriverOutputStatusV1);
-
-impl Drop for RiverOutputStatus {
- fn drop(&mut self) {
- self.0.destroy();
- }
-}
-
-#[macro_export]
-macro_rules! delegate_river_status {
- ($ty: ty) => {
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::status::protocol::zriver_status_manager_v1::ZriverStatusManagerV1: ()
- ] => $crate::river_protocols::status::RiverStatusState);
- ::smithay_client_toolkit::reexports::client::delegate_dispatch!($ty: [
- $crate::river_protocols::status::protocol::zriver_output_status_v1::ZriverOutputStatusV1: $crate::river_protocols::status::RiverStatusData
- ] => $crate::river_protocols::status::RiverStatusState);
- };
-}
-
-#[derive(Debug)]
-pub struct RiverStatusData {
- output: wl_output::WlOutput,
-}
-
-impl<D> Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, (), D> for RiverStatusState
-where
- D: Dispatch<zriver_status_manager_v1::ZriverStatusManagerV1, ()> + RiverStatusHandler + 'static,
-{
- fn event(
- _: &mut D,
- _: &zriver_status_manager_v1::ZriverStatusManagerV1,
- _: zriver_status_manager_v1::Event,
- _: &(),
- _: &Connection,
- _: &QueueHandle<D>,
- ) {
- unreachable!("zriver_status_manager_v1 has no events")
- }
-}
-
-impl<D> Dispatch<zriver_output_status_v1::ZriverOutputStatusV1, RiverStatusData, D>
- for RiverStatusState
-where
- D: Dispatch<zriver_output_status_v1::ZriverOutputStatusV1, RiverStatusData>
- + RiverStatusHandler
- + 'static,
-{
- fn event(
- data: &mut D,
- _: &zriver_output_status_v1::ZriverOutputStatusV1,
- event: zriver_output_status_v1::Event,
- udata: &RiverStatusData,
- _: &Connection,
- _: &QueueHandle<D>,
- ) {
- use zriver_output_status_v1::Event;
-
- match event {
- Event::FocusedTags { tags } => data.focused_tags_updated(&udata.output, tags),
- Event::UrgentTags { tags } => data.urgent_tags_updated(&udata.output, tags),
- Event::ViewTags { tags } => {
- let tags = tags
- .chunks_exact(4)
- .map(|bytes| u32::from_ne_bytes(bytes.try_into().unwrap()))
- .collect();
- data.views_tags_updated(&udata.output, tags);
- }
- Event::LayoutName { name } => data.layout_name_updated(&udata.output, Some(name)),
- Event::LayoutNameClear => data.layout_name_updated(&udata.output, None),
- }
- }
-}
-
-pub mod protocol {
- #![allow(non_upper_case_globals)]
-
- use smithay_client_toolkit::reexports::client as wayland_client;
- use smithay_client_toolkit::reexports::client::protocol::*;
-
- pub mod __interfaces {
- use smithay_client_toolkit::reexports::client::protocol::__interfaces::*;
- wayland_scanner::generate_interfaces!("protocols/river-status-unstable-v1.xml");
- }
- use self::__interfaces::*;
-
- wayland_scanner::generate_client_code!("protocols/river-status-unstable-v1.xml");
-}
diff --git a/src/shared_state.rs b/src/shared_state.rs
index c4ec9d6..dd18606 100644
--- a/src/shared_state.rs
+++ b/src/shared_state.rs
@@ -1,16 +1,8 @@
-use smithay_client_toolkit::{reexports::client::QueueHandle, shm::slot::SlotPool};
+use crate::{config::Config, i3bar_protocol::Block, state::ComputedBlock, status_cmd::StatusCmd};
+use wayrs_shm_alloc::ShmAlloc;
-use crate::{
- config::Config,
- i3bar_protocol::Block,
- state::{ComputedBlock, State},
- status_cmd::StatusCmd,
-};
-
-#[derive(Debug)]
pub struct SharedState {
- pub qh: QueueHandle<State>,
- pub pool: SlotPool,
+ pub shm: ShmAlloc,
pub config: Config,
pub status_cmd: Option<StatusCmd>,
pub blocks: Vec<Block>,
diff --git a/src/state.rs b/src/state.rs
index e2e57ee..dbe73fd 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,103 +1,76 @@
-use std::{future::pending, io};
+use crate::cursor::Cursor;
+use crate::protocol::*;
-use smithay_client_toolkit::{
- compositor::{CompositorHandler, CompositorState},
- delegate_compositor, delegate_layer, delegate_output, delegate_pointer, delegate_registry,
- delegate_seat, delegate_shm,
- output::{OutputHandler, OutputState},
- reexports::client::{
- protocol::{wl_output, wl_pointer, wl_seat, wl_surface},
- Connection, EventQueue, QueueHandle,
- },
- registry::{ProvidesRegistryState, RegistryState},
- registry_handlers,
- seat::{
- pointer::{PointerEvent, PointerEventKind, PointerHandler, ThemeSpec, ThemedPointer},
- Capability, SeatHandler, SeatState,
- },
- shell::layer::{
- Anchor, Layer, LayerShell, LayerShellHandler, LayerSurface, LayerSurfaceConfigure,
- },
- shm::{slot::SlotPool, ShmHandler, ShmState},
-};
-use tokio::io::unix::AsyncFdReadyGuard;
-use wayland_client::globals::GlobalList;
+use std::convert::Infallible;
+use std::future::pending;
+
+use wayrs_client::connection::Connection;
+use wayrs_client::global::{Global, GlobalExt, Globals, GlobalsExt};
+use wayrs_client::proxy::{Dispatch, Dispatcher, Proxy};
+use wayrs_shm_alloc::ShmAlloc;
use crate::{
- bar::Bar,
- config::Config,
- delegate_river_control, delegate_river_status,
- i3bar_protocol::Block,
- pointer_btn::PointerBtn,
- river_protocols::{
- control::{RiverControlHandler, RiverControlState},
- status::{RiverStatusHandler, RiverStatusState},
- },
- shared_state::SharedState,
- status_cmd::StatusCmd,
- text::ComputedText,
+ bar::Bar, config::Config, i3bar_protocol::Block, pointer_btn::PointerBtn,
+ shared_state::SharedState, status_cmd::StatusCmd, text::ComputedText,
};
pub struct State {
- registry_state: RegistryState,
- shm_state: ShmState,
- seat_state: SeatState,
- output_state: OutputState,
- compositor_state: CompositorState,
- layer_state: LayerShell,
- river_status_state: Option<RiverStatusState>,
- river_control_state: Option<RiverControlState>,
+ wl_compositor: WlCompositor,
+ layer_shell: ZwlrLayerShellV1,
+
+ river_status_manager: Option<ZriverStatusManagerV1>,
+ river_control: Option<ZriverControlV1>,
seats: Vec<Seat>,
bars: Vec<Bar>,
+
pub shared_state: SharedState,
}
struct Seat {
- seat: wl_seat::WlSeat,
- pointer: wl_pointer::WlPointer,
- themed_pointer: ThemedPointer,
- pointer_surface: wl_surface::WlSurface,
- finger_scroll: f64,
+ seat: WlSeat,
+ reg_name: u32,
+ pointer: Option<WlPointer>,
+ cursor: Cursor,
+ cur_surface: Option<WlSurface>,
+ x: f64,
+ y: f64,
+ pending_button: Option<PointerBtn>,
+ pending_scroll: f64,
+ scroll_frame: ScrollFrame,
}
impl State {
- pub fn new(event_queue: &mut EventQueue<Self>, globals: &GlobalList) -> Self {
+ pub fn new(conn: &mut Connection<Self>, globals: &Globals) -> Self {
let mut error = Ok(());
- let qh = event_queue.handle();
-
let config = Config::new()
.map_err(|e| error = Err(e))
.unwrap_or_default();
- let status_cmd = match &error {
- Err(_) => None,
- Ok(()) => config.command.as_ref().and_then(|cmd| {
- StatusCmd::new(cmd)
- .map_err(|e| error = Err(anyhow!(e)))
- .ok()
- }),
- };
+ let status_cmd = config
+ .command
+ .as_ref()
+ .and_then(|cmd| StatusCmd::new(cmd).map_err(|e| error = Err(e)).ok());
- let shm_state = ShmState::bind(globals, &qh).unwrap();
- let pool = SlotPool::new(1024, &shm_state).expect("Failed to create pool");
+ let shm = globals.bind(conn, 1..=1).expect("could not bind wl_shm");
let mut this = Self {
- registry_state: RegistryState::new(globals),
- shm_state,
- seat_state: SeatState::new(globals, &qh),
- output_state: OutputState::new(globals, &qh),
- compositor_state: CompositorState::bind(globals, &qh).unwrap(),
- layer_state: LayerShell::bind(globals, &qh).unwrap(),
- river_status_state: RiverStatusState::new(globals, &qh).ok(),
- river_control_state: RiverControlState::new(globals, &qh).ok(),
+ wl_compositor: globals
+ .bind(conn, 4..=5)
+ .expect("could not bind wl_compositor"),
+ layer_shell: globals
+ .bind(conn, 1..=4)
+ .expect("could not bind layer_shell"),
+
+ river_status_manager: globals.bind(conn, 1..=4).ok(),
+ river_control: globals.bind(conn, 1..=1).ok(),
seats: Vec::new(),
bars: Vec::new(),
+
shared_state: SharedState {
- qh: event_queue.handle(),
- pool,
+ shm: ShmAlloc::new(conn, shm, 1024),
config,
status_cmd,
blocks: Vec::new(),
@@ -105,113 +78,102 @@ impl State {
},
};
+ globals
+ .iter()
+ .filter(|g| g.is::<WlSeat>())
+ .for_each(|g| this.bind_seat(conn, g));
+ globals
+ .iter()
+ .filter(|g| g.is::<WlOutput>())
+ .for_each(|g| this.bind_output(conn, g));
+
if let Err(e) = error {
- this.set_error(e.to_string());
+ this.set_error(conn, e.to_string());
}
this
}
- pub fn set_blocks(&mut self, blocks: Vec<Block>) {
+ pub fn set_blocks(&mut self, conn: &mut Connection<Self>, blocks: Vec<Block>) {
self.shared_state.blocks = blocks;
- self.draw_all();
+ self.draw_all(conn);
}
- pub fn set_error(&mut self, error: impl Into<String>) {
- self.set_blocks(vec![Block {
- full_text: error.into(),
- ..Default::default()
- }]);
+ pub fn set_error(&mut self, conn: &mut Connection<Self>, error: impl Into<String>) {
+ self.set_blocks(
+ conn,
+ vec![Block {
+ full_text: error.into(),
+ ..Default::default()
+ }],
+ );
}
- pub fn notify_available(&mut self) -> anyhow::Result<()> {
- if let Some(cmd) = &mut self.shared_state.status_cmd {
- if let Some(blocks) = cmd.notify_available()? {
- self.set_blocks(blocks);
- }
- }
- Ok(())
- }
-
- pub fn draw_all(&mut self) {
+ pub fn draw_all(&mut self, conn: &mut Connection<Self>) {
for bar in &mut self.bars {
- bar.draw(&mut self.shared_state);
+ bar.request_frame(conn);
}
}
- pub async fn wait_for_status_cmd(&self) -> io::Result<AsyncFdReadyGuard<i32>> {
- match &self.shared_state.status_cmd {
- Some(cmd) => cmd.async_fd.readable().await,
+ pub async fn status_cmd_read(&mut self) -> anyhow::Result<()> {
+ match &mut self.shared_state.status_cmd {
+ Some(cmd) => cmd.read().await,
None => {
pending::<()>().await;
unreachable!()
}
}
}
-}
-impl CompositorHandler for State {
- fn scale_factor_changed(
+ pub fn status_cmd_notify_available(
&mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- surface: &wl_surface::WlSurface,
- scale: i32,
- ) {
- if let Some(bar) = self
- .bars
- .iter_mut()
- .find(|bar| bar.layer.wl_surface() == surface)
- {
- bar.scale = scale;
- bar.draw(&mut self.shared_state);
+ conn: &mut Connection<Self>,
+ ) -> anyhow::Result<()> {
+ if let Some(cmd) = &mut self.shared_state.status_cmd {
+ if let Some(blocks) = cmd.notify_available()? {
+ self.set_blocks(conn, blocks);
+ }
}
+ Ok(())
}
- fn frame(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- _surface: &wl_surface::WlSurface,
- _time: u32,
- ) {
- }
-}
+ fn bind_output(&mut self, conn: &mut Connection<Self>, global: &Global) {
+ let output = global.bind(conn, 2..=4).expect("could not bind wl_output");
+ let surface = self.wl_compositor.create_surface(conn);
-impl OutputHandler for State {
- fn output_state(&mut self) -> &mut OutputState {
- &mut self.output_state
- }
+ use zwlr_layer_shell_v1::Layer;
+ use zwlr_layer_surface_v1::Anchor;
+ let layer_surface = self.layer_shell.get_layer_surface(
+ conn,
+ surface,
+ output,
+ Layer::Top,
+ wayrs_client::cstr!("i3bar-river").into(),
+ );
+ layer_surface.set_size(conn, 0, self.shared_state.config.height);
+ layer_surface.set_anchor(conn, Anchor::Top | Anchor::Left | Anchor::Right); // Top + Left + Right
+ layer_surface.set_exclusive_zone(conn, self.shared_state.config.height as i32 + 5); // TODO: make the margin configurable
+
+ // Note: layer_surface is commited when we receive the scale factor of this output
- fn new_output(
- &mut self,
- _conn: &Connection,
- qh: &QueueHandle<Self>,
- output: wl_output::WlOutput,
- ) {
- let height = self.shared_state.config.height;
- let surface = self.compositor_state.create_surface(qh);
- let layer = LayerSurface::builder()
- .output(&output)
- .size((0, height))
- .anchor(Anchor::TOP | Anchor::LEFT | Anchor::RIGHT)
- .namespace("i3bar-river")
- .map(qh, &self.layer_state, surface, Layer::Top)
- .expect("layer surface creation");
let river_output_status = self
- .river_status_state
- .as_mut()
- .map(|s| s.new_output_status(qh, &output));
+ .river_status_manager
+ .as_ref()
+ .map(|s| s.get_river_output_status(conn, output));
+
self.bars.push(Bar {
output,
+ output_reg_name: global.name,
configured: false,
+ frame_cb: None,
width: 0,
- height,
+ height: self.shared_state.config.height,
scale: 1,
- layer,
+ surface,
+ layer_surface,
blocks_btns: Default::default(),
river_output_status,
- river_control: self.river_control_state.clone(),
+ river_control: self.river_control,
layout_name: None,
layout_name_computed: None,
tags_btns: Default::default(),
@@ -220,238 +182,318 @@ impl OutputHandler for State {
});
}
- fn update_output(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- _output: wl_output::WlOutput,
- ) {
+ fn bind_seat(&mut self, conn: &mut Connection<Self>, global: &Global) {
+ let seat: WlSeat = global.bind(conn, 5..=8).unwrap();
+ let cursor = Cursor::new(conn, self.wl_compositor).unwrap();
+ self.seats.push(Seat {
+ seat,
+ reg_name: global.name,
+ pointer: None,
+ cursor,
+ cur_surface: None,
+ x: 0.0,
+ y: 0.0,
+ pending_button: None,
+ pending_scroll: 0.0,
+ scroll_frame: ScrollFrame::default(),
+ });
}
- fn output_destroyed(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- _output: wl_output::WlOutput,
- ) {
+ fn drop_bar(&mut self, conn: &mut Connection<Self>, bar_index: usize) {
+ let bar = self.bars.swap_remove(bar_index);
+ bar.surface.destroy(conn);
+ bar.layer_surface.destroy(conn);
+ if let Some(output_status) = bar.river_output_status {
+ output_status.destroy(conn);
+ }
+ if bar.output.version() >= 3 {
+ bar.output.release(conn);
+ }
}
-}
-impl LayerShellHandler for State {
- fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, layer: &LayerSurface) {
- self.bars.retain(|b| &b.layer != layer)
+ fn drop_seat(&mut self, conn: &mut Connection<Self>, seat_index: usize) {
+ let seat = self.seats.swap_remove(seat_index);
+ if let Some(pointer) = seat.pointer {
+ if pointer.version() >= 3 {
+ pointer.release(conn);
+ }
+ }
+ if seat.seat.version() >= 5 {
+ seat.seat.release(conn);
+ }
}
+}
- fn configure(
- &mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- layer: &LayerSurface,
- configure: LayerSurfaceConfigure,
- _serial: u32,
- ) {
- self.bars
- .iter_mut()
- .find(|b| &b.layer == layer)
- .unwrap()
- .configure(&mut self.shared_state, configure.new_size.0);
- }
+impl Dispatcher for State {
+ type Error = Infallible;
}
-impl SeatHandler for State {
- fn seat_state(&mut self) -> &mut SeatState {
- &mut self.seat_state
+impl Dispatch<WlRegistry> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, _: WlRegistry, event: wl_registry::Event) {
+ match event {
+ wl_registry::Event::Global(global) if global.is::<WlOutput>() => {
+ self.bind_output(conn, &global);
+ }
+ wl_registry::Event::Global(global) if global.is::<WlSeat>() => {
+ self.bind_seat(conn, &global);
+ }
+ wl_registry::Event::GlobalRemove(name) => {
+ if let Some(bar_index) =
+ self.bars.iter().position(|bar| bar.output_reg_name == name)
+ {
+ self.drop_bar(conn, bar_index);
+ } else if let Some(seat_index) =
+ self.seats.iter().position(|seat| seat.reg_name == name)
+ {
+ self.drop_seat(conn, seat_index);
+ }
+ }
+ _ => (),
+ }
}
+}
- fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
+impl Dispatch<WlCallback> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, cb: WlCallback, event: wl_callback::Event) {
+ let wl_callback::Event::Done(_) = event;
+ if let Some(bar) = self.bars.iter_mut().find(|bar| bar.frame_cb == Some(cb)) {
+ bar.frame_cb = None;
+ bar.frame(conn, &mut self.shared_state);
+ }
+ }
+}
- fn new_capability(
- &mut self,
- _conn: &Connection,
- qh: &QueueHandle<Self>,
- seat: wl_seat::WlSeat,
- capability: Capability,
- ) {
- if capability == Capability::Pointer {
- let (pointer, themed_pointer) = self
- .seat_state
- .get_pointer_with_theme(qh, &seat, ThemeSpec::System, 1)
- .expect("Failed to create pointer");
- self.seats.push(Seat {
- seat,
- pointer,
- themed_pointer,
- pointer_surface: self.compositor_state.create_surface(qh),
- finger_scroll: 0.0,
- });
+impl Dispatch<WlOutput> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, output: WlOutput, event: wl_output::Event) {
+ if let wl_output::Event::Scale(scale) = event {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|bar| bar.output == output)
+ .unwrap();
+ bar.scale = scale;
+ // If bar is not configured yet, it is because we were waiting for the "scale" event
+ // before commiting the surface. Otherwise, there is no need to do any redrawing (we'll
+ // do that after "configure" event).
+ if !bar.configured {
+ bar.surface.commit(conn);
+ }
}
}
+}
- fn remove_capability(
+impl Dispatch<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1> for State {
+ fn event(
&mut self,
- _conn: &Connection,
- _qh: &QueueHandle<Self>,
- seat: wl_seat::WlSeat,
- capability: Capability,
+ conn: &mut Connection<Self>,
+ layer_surface: zwlr_layer_surface_v1::ZwlrLayerSurfaceV1,
+ event: zwlr_layer_surface_v1::Event,
) {
- if capability == Capability::Pointer {
- self.seats.retain(|p| p.seat == seat);
+ match event {
+ zwlr_layer_surface_v1::Event::Configure(args) => {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|bar| bar.layer_surface == layer_surface)
+ .unwrap();
+ assert_ne!(args.width, 0);
+ bar.width = args.width;
+ bar.configured = true;
+ bar.layer_surface.ack_configure(conn, args.serial);
+ bar.frame(conn, &mut self.shared_state);
+ }
+ zwlr_layer_surface_v1::Event::Closed => {
+ let bar_index = self
+ .bars
+ .iter()
+ .position(|bar| bar.layer_surface == layer_surface)
+ .unwrap();
+ self.drop_bar(conn, bar_index);
+ }
}
}
+}
- fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
+impl Dispatch<WlBuffer> for State {
+ fn event(&mut self, _: &mut Connection<Self>, buffer: WlBuffer, event: wl_buffer::Event) {
+ let wl_buffer::Event::Release = event;
+ self.shared_state.shm.free_buffer(buffer);
+ }
}
-impl PointerHandler for State {
- fn pointer_frame(
- &mut self,
- conn: &Connection,
- _qh: &QueueHandle<Self>,
- pointer: &wl_pointer::WlPointer,
- events: &[PointerEvent],
- ) {
+impl Dispatch<WlSeat> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, seat: WlSeat, event: wl_seat::Event) {
+ if let wl_seat::Event::Capabilities(capabilities) = event {
+ let seat = self.seats.iter_mut().find(|s| s.seat == seat).unwrap();
+ match &seat.pointer {
+ Some(pointer) if !capabilities.contains(wl_seat::Capability::Pointer) => {
+ if pointer.version() >= 3 {
+ pointer.release(conn);
+ }
+ seat.pointer = None;
+ }
+ None if capabilities.contains(wl_seat::Capability::Pointer) => {
+ let pointer = seat.seat.get_pointer(conn);
+ seat.pointer = Some(pointer);
+ }
+ _ => (),
+ }
+ }
+ }
+}
+
+impl Dispatch<WlPointer> for State {
+ fn event(&mut self, conn: &mut Connection<Self>, pointer: WlPointer, event: wl_pointer::Event) {
let seat = self
.seats
.iter_mut()
- .find(|p| &p.pointer == pointer)
+ .find(|s| s.pointer == Some(pointer))
.unwrap();
- for event in events {
- if let Some(bar) = self
- .bars
- .iter_mut()
- .find(|b| b.layer.wl_surface() == &event.surface)
- {
- match event.kind {
- PointerEventKind::Enter { .. } => {
- seat.themed_pointer
- .set_cursor(
- conn,
- "default",
- self.shm_state.wl_shm(),
- &seat.pointer_surface,
- )
+
+ use wl_pointer::Event;
+ match event {
+ Event::Frame => {
+ let btn = seat.pending_button.take();
+ let scroll = seat.scroll_frame.finalize();
+ if let Some(surface) = seat.cur_surface {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|bar| bar.surface == surface)
+ .unwrap();
+
+ if let Some(btn) = btn {
+ bar.click(conn, &mut self.shared_state, btn, seat.seat, seat.x, seat.y)
.unwrap();
}
- PointerEventKind::Press { button, .. } => {
- bar.click(
- &mut self.shared_state,
- button.into(),
- &seat.seat,
- event.position.0,
- event.position.1,
- )
- .unwrap();
+
+ if scroll.is_finder && self.shared_state.config.invert_touchpad_scrolling {
+ seat.pending_scroll -= scroll.absolute;
+ } else {
+ seat.pending_scroll += scroll.absolute;
+ }
+
+ if scroll.stop {
+ seat.pending_scroll = 0.0;
}
- PointerEventKind::Axis {
- vertical, source, ..
- } => {
- if source == Some(wl_pointer::AxisSource::Finger) {
- if self.shared_state.config.invert_touchpad_scrolling {
- seat.finger_scroll -= vertical.absolute;
- } else {
- seat.finger_scroll += vertical.absolute;
- }
- if vertical.stop {
- seat.finger_scroll = 0.0;
- }
- }
- let btn = if vertical.discrete > 0 {
- PointerBtn::WheelDown
- } else if vertical.discrete < 0 {
- PointerBtn::WheelUp
- } else if seat.finger_scroll >= 15.0 {
- seat.finger_scroll = 0.0;
- PointerBtn::WheelDown
- } else if seat.finger_scroll <= -15.0 {
- seat.finger_scroll = 0.0;
- PointerBtn::WheelUp
- } else {
- continue;
- };
+ let btn = if seat.pending_scroll >= 15.0 {
+ seat.pending_scroll = 0.0;
+ Some(PointerBtn::WheelDown)
+ } else if seat.pending_scroll <= -15.0 {
+ seat.pending_scroll = 0.0;
+ Some(PointerBtn::WheelUp)
+ } else {
+ None
+ };
- bar.click(
- &mut self.shared_state,
- btn,
- &seat.seat,
- event.position.0,
- event.position.1,
- )
- .unwrap();
+ if let Some(btn) = btn {
+ bar.click(conn, &mut self.shared_state, btn, seat.seat, seat.x, seat.y)
+ .unwrap();
}
- _ => (),
}
}
+ Event::Enter(args) => {
+ let bar = self
+ .bars
+ .iter()
+ .find(|bar| bar.surface.id() == args.surface)
+ .unwrap();
+ seat.cur_surface = Some(bar.surface);
+ seat.x = args.surface_x.as_f64();
+ seat.y = args.surface_y.as_f64();
+ seat.cursor.set(
+ conn,
+ args.serial,
+ pointer,
+ bar.scale as u32,
+ &mut self.shared_state.shm,
+ );
+ }
+ Event::Leave(_) => seat.cur_surface = None,
+ Event::Motion(args) => {
+ seat.x = args.surface_x.as_f64();
+ seat.y = args.surface_y.as_f64();
+ }
+ Event::Button(args) => {
+ if args.state == wl_pointer::ButtonState::Pressed {
+ seat.pending_button = Some(args.button.into());
+ }
+ }
+ Event::Axis(args) => {
+ if args.axis == wl_pointer::Axis::VerticalScroll {
+ seat.scroll_frame.absolute += args.value.as_f64();
+ }
+ }
+ Event::AxisSource(source) => {
+ seat.scroll_frame.is_finder = source == wl_pointer::AxisSource::Finger;
+ }
+ Event::AxisStop(args) => {
+ if args.axis == wl_pointer::Axis::VerticalScroll {
+ seat.scroll_frame.stop = true;
+ }
+ }
+ Event::AxisDiscrete(_) | Event::AxisValue120(_) => (),
}
}
}
-impl ShmHandler for State {
- fn shm_state(&mut self) -> &mut ShmState {
- &mut self.shm_state
- }
-}
-
-impl RiverStatusHandler for State {
- fn river_status_state(&mut self) -> &mut RiverStatusState {
- self.river_status_state.as_mut().unwrap()
- }
-
- fn focused_tags_updated(&mut self, output: &wl_output::WlOutput, focused: u32) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.tags_info.focused = focused;
- bar.draw(&mut self.shared_state);
- }
-
- fn urgent_tags_updated(&mut self, output: &wl_output::WlOutput, urgent: u32) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.tags_info.urgent = urgent;
- bar.draw(&mut self.shared_state);
- }
+impl Dispatch<ZriverOutputStatusV1> for State {
+ fn event(
+ &mut self,
+ conn: &mut Connection<Self>,
+ status: ZriverOutputStatusV1,
+ event: zriver_output_status_v1::Event,
+ ) {
+ let bar = self
+ .bars
+ .iter_mut()
+ .find(|b| b.river_output_status == Some(status))
+ .unwrap();
- fn views_tags_updated(&mut self, output: &wl_output::WlOutput, tags: Vec<u32>) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.tags_info.active = tags.into_iter().fold(0, |a, b| a | b);
- bar.draw(&mut self.shared_state);
- }
+ use zriver_output_status_v1::Event;
+ match event {
+ Event::FocusedTags(tags) => bar.tags_info.focused = tags,
+ Event::UrgentTags(tags) => bar.tags_info.urgent = tags,
+ Event::LayoutName(name) => bar.set_layout_name(Some(name.into_string().unwrap())),
+ Event::LayoutNameClear => bar.set_layout_name(None),
+ Event::ViewTags(vt) => {
+ bar.tags_info.active = vt
+ .chunks_exact(4)
+ .map(|bytes| u32::from_ne_bytes(bytes.try_into().unwrap()))
+ .fold(0, |a, b| a | b);
+ }
+ }
- fn layout_name_updated(&mut self, output: &wl_output::WlOutput, layout_name: Option<String>) {
- let bar = self.bars.iter_mut().find(|b| &b.output == output).unwrap();
- bar.set_layout_name(layout_name);
- bar.draw(&mut self.shared_state);
+ bar.request_frame(conn);
}
}
-impl RiverControlHandler for State {
- fn river_control_state(&mut self) -> &mut RiverControlState {
- self.river_control_state.as_mut().unwrap()
- }
-
- fn command_failure(&mut self, _: &Connection, _: &QueueHandle<Self>, message: String) {
- self.set_error(format!("[river_control] {message}"));
- }
-
- fn command_success(&mut self, _: &Connection, _: &QueueHandle<Self>, message: String) {
- info!("river_control: {message}");
+impl Dispatch<ZriverCommandCallbackV1> for State {
+ fn event(
+ &mut self,
+ conn: &mut Connection<Self>,
+ _: ZriverCommandCallbackV1,
+ event: zriver_command_callback_v1::Event,
+ ) {
+ use zriver_command_callback_v1::Event;
+ match event {
+ Event::Success(msg) => info!("river_control: {msg:?}"),
+ Event::Failure(msg) => self.set_error(conn, msg.into_string().unwrap()),
+ }
}
}
-delegate_compositor!(State);
-delegate_output!(State);
-delegate_shm!(State);
-delegate_seat!(State);
-delegate_pointer!(State);
-delegate_layer!(State);
-delegate_registry!(State);
-delegate_river_status!(State);
-delegate_river_control!(State);
+// Interfaces with no events
+impl Dispatch<WlCompositor> for State {}
+impl Dispatch<ZwlrLayerShellV1> for State {}
+impl Dispatch<ZriverStatusManagerV1> for State {}
+impl Dispatch<ZriverControlV1> for State {}
+impl Dispatch<WlShmPool> for State {}
-impl ProvidesRegistryState for State {
- fn registry(&mut self) -> &mut RegistryState {
- &mut self.registry_state
- }
- registry_handlers![OutputState,];
-}
+// Dont care
+impl Dispatch<WlShm> for State {}
+impl Dispatch<WlSurface> for State {}
#[derive(Debug)]
pub struct ComputedBlock {
@@ -460,3 +502,18 @@ pub struct ComputedBlock {
pub short: Option<ComputedText>,
pub min_width: Option<f64>,
}
+
+#[derive(Debug, Default, Clone, Copy)]
+pub struct ScrollFrame {
+ stop: bool,
+ absolute: f64,
+ is_finder: bool,
+}
+
+impl ScrollFrame {
+ fn finalize(&mut self) -> Self {
+ let copy = *self;
+ *self = Self::default();
+ copy
+ }
+}
diff --git a/src/status_cmd.rs b/src/status_cmd.rs
index 4d9c453..f6c9c2c 100644
--- a/src/status_cmd.rs
+++ b/src/status_cmd.rs
@@ -1,22 +1,22 @@
-use std::io::{BufRead, BufReader, Write};
-use std::os::unix::prelude::AsRawFd;
-use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio};
+use std::io::Write;
+use std::process::{Child, ChildStdin, Command, Stdio};
use anyhow::Result;
-use tokio::io::unix::AsyncFd;
-use tokio::io::Interest;
+use tokio::io::AsyncReadExt;
+use tokio::process::ChildStdout;
use crate::i3bar_protocol::{Block, Event, Protocol};
+const INITIAL_BUF_CAPACITY: usize = 4096;
+
#[derive(Debug)]
pub struct StatusCmd {
pub child: Child,
- output: BufReader<ChildStdout>,
+ pub output: ChildStdout,
input: ChildStdin,
protocol: Protocol,
buf: Vec<u8>,
- pub async_fd: AsyncFd<i32>,
}
impl StatusCmd {
@@ -28,73 +28,45 @@ impl StatusCmd {
.spawn()?;
let output = child.stdout.take().unwrap();
let input = child.stdin.take().unwrap();
- let async_fd = AsyncFd::with_interest(output.as_raw_fd(), Interest::READABLE)?;
Ok(Self {
child,
- output: BufReader::new(output),
+ output: ChildStdout::from_std(output)?,
input,
protocol: Protocol::Unknown,
- buf: Vec::new(),
- async_fd,
+ buf: Vec::with_capacity(INITIAL_BUF_CAPACITY),
})
}
pub fn notify_available(&mut self) -> Result<Option<Vec<Block>>> {
- let buf = self.output.fill_buf()?;
- if buf.is_empty() {
- bail!("status command exited");
- }
- if self.buf.is_empty() {
- let rem = self.protocol.process_new_bytes(buf)?;
- if !rem.is_empty() {
- self.buf.extend_from_slice(rem);
- }
+ let rem = self.protocol.process_new_bytes(&self.buf)?;
+ if rem.is_empty() {
+ self.buf.clear();
} else {
- self.buf.extend_from_slice(buf);
- let rem = self.protocol.process_new_bytes(&self.buf)?;
- if rem.is_empty() {
- self.buf.clear();
- } else {
- let used = self.buf.len() - rem.len();
- self.buf.drain(..used);
- }
+ let used = self.buf.len() - rem.len();
+ self.buf.drain(..used);
}
- let consumed = buf.len();
- self.output.consume(consumed);
Ok(self.protocol.get_blocks())
}
+ pub async fn read(&mut self) -> Result<()> {
+ assert!(self.buf.capacity() >= INITIAL_BUF_CAPACITY);
+ if self.buf.capacity() == self.buf.len() {
+ // Double the capacity
+ self.buf.reserve(self.buf.len());
+ }
+
+ let read_len = self.output.read_buf(&mut self.buf).await?;
+ if read_len == 0 {
+ bail!("status command exited");
+ }
+
+ Ok(())
+ }
+
pub fn send_click_event(&mut self, event: &Event) -> Result<()> {
if self.protocol.supports_clicks() {
writeln!(self.input, "{}", serde_json::to_string(event).unwrap())?;
}
Ok(())
}
-
- // pub fn quick_insert(&self, handle: LoopHandle<BarState>) {
- // handle
- // .insert_source(
- // calloop::generic::Generic::new(
- // self.output.get_ref().as_raw_fd(),
- // calloop::Interest {
- // readable: true,
- // writable: false,
- // },
- // calloop::Mode::Level,
- // ),
- // move |ready, _, bar_state| {
- // if ready.readable {
- // bar_state.notify_available()?;
- // Ok(calloop::PostAction::Continue)
- // } else {
- // bar_state.set_error("error reading from status command");
- // if let Some(mut child) = bar_state.status_cmd.take() {
- // let _ = child.child.kill();
- // }
- // Ok(calloop::PostAction::Remove)
- // }
- // },
- // )
- // .expect("failed to inser calloop source");
- // }
}