Repository: j0ru/kickoff (language: rust)

Issue:
Unable to run on latest sway git version
```
$ kickoff --version
kickoff 0.7.0
```
OS: Linux/NixOS, Wayland, sway-git-bb91b7f5fa7fddb582b8dddf208cc335d39da9e7

When trying to run kickoff it crashes with 

```
$ kickoff
interface 'wl_surface' has no event 2
warning: queue 0x7f4f18000ca0 destroyed while proxies still attached:
  wl_registry@20 still attached
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 14, kind: Uncategorized, message: "Bad address" }', src/main.rs:387:25
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: queue 0x55d9769b1110 destroyed while proxies still attached:
  wl_keyboard@19 still attached
  wl_pointer@18 still attached
  zwlr_layer_surface_v1@17 still attached
  wl_surface@16 still attached
  wl_shm_pool@15 still attached
  wl_shm_pool@14 still attached
  zwp_primary_selection_device_v1@13 still attached
  zwp_primary_selection_device_manager_v1@3 still attached
  wl_data_device@12 still attached
  wl_output@11 still attached
  wl_output@10 still attached
  wl_seat@9 still attached
  zwlr_layer_shell_v1@8 still attached
  wl_data_device_manager@7 still attached
  wl_subcompositor@6 still attached
  wl_compositor@5 still attached
  wl_shm@4 still attached
  wl_registry@2 still attached
```

The `interface 'wl_surface' has no event 2` error surfaces recently in quite some applications like here: https://github.com/alacritty/alacritty/issues/7083

The author of this issue suspects that this is the cause for the problem: https://github.com/swaywm/sway/commit/fc16fb65491bda0d059b8c9aeae3dc9962e5c690 


Discussion / hints:
Thanks for the research!
Unfortunately this will take a bit to fix, since I'm currently stuck on an older version of
the library that interacts with wayland (they did some MAJOR api changes and I have to rewrite half of my codebase). 
No worries. I just stick to an older version of sway now. 
Thank you so much for your work. For me its the best native wayland launcher.

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0686fe4..4d564ae 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -23,7 +23,7 @@ jobs:
           toolchain: ${{ matrix.rust }}
           override: true
       - run: rustup component add clippy
-      - run: sudo apt-get install -y libfontconfig-dev pkgconf
+      - run: sudo apt-get install -y libfontconfig-dev pkgconf libxkbcommon-dev
 
       - name: clippy
         uses: actions-rs/cargo@v1
diff --git a/Cargo.lock b/Cargo.lock
index 1389115..e118185 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
 
 [[package]]
 name = "addr2line"
-version = "0.19.0"
+version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97"
+checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3"
 dependencies = [
  "gimli",
 ]
@@ -30,19 +30,13 @@ dependencies = [
 
 [[package]]
 name = "aho-corasick"
-version = "1.0.1"
+version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04"
+checksum = "6748e8def348ed4d14996fa801f4122cd763fff530258cdc03f64b25f89d3a5a"
 dependencies = [
  "memchr",
 ]
 
-[[package]]
-name = "anes"
-version = "0.1.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
-
 [[package]]
 name = "anstream"
 version = "0.3.2"
@@ -60,15 +54,15 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
+checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.0"
+version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
 dependencies = [
  "utf8parse",
 ]
@@ -79,19 +73,25 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
 dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "1.0.1"
+version = "1.0.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
+checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
 dependencies = [
  "anstyle",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
+[[package]]
+name = "anyhow"
+version = "1.0.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
+
 [[package]]
 name = "async-broadcast"
 version = "0.5.1"
@@ -104,9 +104,9 @@ dependencies = [
 
 [[package]]
 name = "async-channel"
-version = "1.8.0"
+version = "1.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833"
+checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
 dependencies = [
  "concurrent-queue",
  "event-listener",
@@ -122,7 +122,7 @@ dependencies = [
  "async-lock",
  "async-task",
  "concurrent-queue",
- "fastrand",
+ "fastrand 1.9.0",
  "futures-lite",
  "slab",
 ]
@@ -153,7 +153,7 @@ dependencies = [
  "log",
  "parking",
  "polling",
- "rustix",
+ "rustix 0.37.23",
  "slab",
  "socket2",
  "waker-fn",
@@ -161,11 +161,29 @@ dependencies = [
 
 [[package]]
 name = "async-lock"
-version = "2.7.0"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b"
+dependencies = [
+ "event-listener",
+]
+
+[[package]]
+name = "async-process"
+version = "1.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa24f727524730b077666307f2734b4a1a1c57acb79193127dcc8914d5242dd7"
+checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9"
 dependencies = [
+ "async-io",
+ "async-lock",
+ "autocfg",
+ "blocking",
+ "cfg-if",
  "event-listener",
+ "futures-lite",
+ "rustix 0.37.23",
+ "signal-hook",
+ "windows-sys",
 ]
 
 [[package]]
@@ -176,7 +194,7 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -187,13 +205,13 @@ checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae"
 
 [[package]]
 name = "async-trait"
-version = "0.1.68"
+version = "0.1.73"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842"
+checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -210,9 +228,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "backtrace"
-version = "0.3.67"
+version = "0.3.68"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca"
+checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12"
 dependencies = [
  "addr2line",
  "cc",
@@ -229,6 +247,12 @@ version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
+[[package]]
+name = "bitflags"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+
 [[package]]
 name = "block"
 version = "0.1.6"
@@ -254,16 +278,16 @@ dependencies = [
  "async-lock",
  "async-task",
  "atomic-waker",
- "fastrand",
+ "fastrand 1.9.0",
  "futures-lite",
  "log",
 ]
 
 [[package]]
-name = "bumpalo"
-version = "3.13.0"
+name = "bytecount"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c"
 
 [[package]]
 name = "bytemuck"
@@ -285,25 +309,26 @@ checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
 
 [[package]]
 name = "calloop"
-version = "0.9.3"
+version = "0.10.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82"
+checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8"
 dependencies = [
+ "bitflags 1.3.2",
  "log",
- "nix 0.22.3",
+ "nix 0.25.1",
+ "slotmap",
+ "thiserror",
+ "vec_map",
 ]
 
-[[package]]
-name = "cast"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
-
 [[package]]
 name = "cc"
-version = "1.0.79"
+version = "1.0.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "305fe645edc1442a0fa8b6726ba61d422798d37a52e12eaecf4b022ebbb88f01"
+dependencies = [
+ "libc",
+]
 
 [[package]]
 name = "cfg-if"
@@ -311,38 +336,11 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
-[[package]]
-name = "ciborium"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
-dependencies = [
- "ciborium-io",
- "ciborium-ll",
- "serde",
-]
-
-[[package]]
-name = "ciborium-io"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
-
-[[package]]
-name = "ciborium-ll"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
-dependencies = [
- "ciborium-io",
- "half",
-]
-
 [[package]]
 name = "clap"
-version = "4.3.0"
+version = "4.3.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
+checksum = "b417ae4361bca3f5de378294fc7472d3c4ed86a5ef9f49e93ae722f432aae8d2"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -351,27 +349,26 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.3.0"
+version = "4.3.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
+checksum = "9c90dc0f0e42c64bff177ca9d7be6fcc9ddb0f26a6e062174a61c84dd6c644d4"
 dependencies = [
  "anstream",
  "anstyle",
- "bitflags",
  "clap_lex",
  "strsim",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.3.0"
+version = "4.3.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b"
+checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
 dependencies = [
- "heck 0.4.1",
+ "heck",
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -409,90 +406,18 @@ checksum = "ed3d0b5ff30645a68f35ece8cea4556ca14ef8a1651455f789a099a0513532a6"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.7"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
+checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1"
 dependencies = [
  "libc",
 ]
 
-[[package]]
-name = "criterion"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f9c16c823fba76d9643cc387e9677d9771abe0827561381815215c47f808da9"
-dependencies = [
- "anes",
- "cast",
- "ciborium",
- "clap",
- "criterion-plot",
- "futures",
- "is-terminal",
- "itertools",
- "num-traits",
- "once_cell",
- "oorandom",
- "plotters",
- "rayon",
- "regex",
- "serde",
- "serde_derive",
- "serde_json",
- "tinytemplate",
- "tokio",
- "walkdir",
-]
-
-[[package]]
-name = "criterion-plot"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
-dependencies = [
- "cast",
- "itertools",
-]
-
-[[package]]
-name = "crossbeam-channel"
-version = "0.5.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
-dependencies = [
- "cfg-if",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-deque"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
-dependencies = [
- "cfg-if",
- "crossbeam-epoch",
- "crossbeam-utils",
-]
-
-[[package]]
-name = "crossbeam-epoch"
-version = "0.9.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
-dependencies = [
- "autocfg",
- "cfg-if",
- "crossbeam-utils",
- "memoffset 0.8.0",
- "scopeguard",
-]
-
 [[package]]
 name = "crossbeam-utils"
-version = "0.8.15"
+version = "0.8.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
+checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
 dependencies = [
  "cfg-if",
 ]
@@ -515,9 +440,9 @@ checksum = "d101c65424c856131a3cb818da2ddde03500dc3656972269cdf79f018ef77eb4"
 
 [[package]]
 name = "csv"
-version = "1.2.1"
+version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad"
+checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
 dependencies = [
  "csv-core",
  "itoa",
@@ -534,6 +459,12 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "deranged"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7684a49fb1af197853ef7b2ee694bc1f5b4179556f1e5710e1760c5db6f5e929"
+
 [[package]]
 name = "derivative"
 version = "2.2.0"
@@ -545,6 +476,17 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "derive-new"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "digest"
 version = "0.10.7"
@@ -578,9 +520,9 @@ dependencies = [
 
 [[package]]
 name = "dlib"
-version = "0.5.0"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
 dependencies = [
  "libloading",
 ]
@@ -591,12 +533,6 @@ version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
 
-[[package]]
-name = "either"
-version = "1.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
-
 [[package]]
 name = "enumflags2"
 version = "0.7.7"
@@ -615,7 +551,7 @@ checksum = "5e9a1f9f7d83e59740248a6e14ecf93929ade55027844dfcea78beafccc15745"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -631,6 +567,12 @@ dependencies = [
  "termcolor",
 ]
 
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
 [[package]]
 name = "errno"
 version = "0.2.8"
@@ -644,13 +586,13 @@ dependencies = [
 
 [[package]]
 name = "errno"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
+checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f"
 dependencies = [
  "errno-dragonfly",
  "libc",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -688,6 +630,24 @@ dependencies = [
  "instant",
 ]
 
+[[package]]
+name = "fastrand"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+
+[[package]]
+name = "fixedbitset"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
 [[package]]
 name = "fontconfig"
 version = "0.6.0"
@@ -761,7 +721,7 @@ version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce"
 dependencies = [
- "fastrand",
+ "fastrand 1.9.0",
  "futures-core",
  "futures-io",
  "memchr",
@@ -778,7 +738,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -832,9 +792,9 @@ dependencies = [
 
 [[package]]
 name = "getrandom"
-version = "0.2.9"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
 dependencies = [
  "cfg-if",
  "libc",
@@ -847,12 +807,6 @@ version = "0.27.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
 
-[[package]]
-name = "half"
-version = "1.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
-
 [[package]]
 name = "hashbrown"
 version = "0.12.3"
@@ -869,13 +823,10 @@ dependencies = [
 ]
 
 [[package]]
-name = "heck"
-version = "0.3.3"
+name = "hashbrown"
+version = "0.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
-dependencies = [
- "unicode-segmentation",
-]
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
 
 [[package]]
 name = "heck"
@@ -885,18 +836,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
 
 [[package]]
 name = "hermit-abi"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "hermit-abi"
-version = "0.3.1"
+version = "0.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
 
 [[package]]
 name = "hex"
@@ -904,15 +846,6 @@ version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
-[[package]]
-name = "home"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
-dependencies = [
- "windows-sys 0.48.0",
-]
-
 [[package]]
 name = "humantime"
 version = "2.1.0"
@@ -921,9 +854,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
 
 [[package]]
 name = "image"
-version = "0.24.6"
+version = "0.24.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
+checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
 dependencies = [
  "bytemuck",
  "byteorder",
@@ -942,6 +875,16 @@ dependencies = [
  "hashbrown 0.12.3",
 ]
 
+[[package]]
+name = "indexmap"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.0",
+]
+
 [[package]]
 name = "instant"
 version = "0.1.12"
@@ -957,53 +900,34 @@ version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
 dependencies = [
- "hermit-abi 0.3.1",
+ "hermit-abi",
  "libc",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
 name = "is-terminal"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
-dependencies = [
- "hermit-abi 0.3.1",
- "io-lifetimes",
- "rustix",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "itertools"
-version = "0.10.5"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
+checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
 dependencies = [
- "either",
+ "hermit-abi",
+ "rustix 0.38.8",
+ "windows-sys",
 ]
 
 [[package]]
 name = "itoa"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
-
-[[package]]
-name = "js-sys"
-version = "0.3.63"
+version = "1.0.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790"
-dependencies = [
- "wasm-bindgen",
-]
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
 
 [[package]]
 name = "kickoff"
 version = "0.7.0"
 dependencies = [
+ "anyhow",
  "clap",
- "criterion",
  "css-color",
  "csv",
  "env_logger",
@@ -1018,10 +942,11 @@ dependencies = [
  "nix 0.26.2",
  "notify-rust",
  "serde",
- "smithay-client-toolkit 0.15.4",
- "smithay-clipboard",
+ "smithay-client-toolkit",
  "tokio",
  "toml",
+ "wayland-client 0.30.2",
+ "wl-clipboard-rs",
  "x11-keysymdef",
  "xdg",
 ]
@@ -1034,18 +959,18 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
 
 [[package]]
 name = "libc"
-version = "0.2.144"
+version = "0.2.147"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
 
 [[package]]
 name = "libloading"
-version = "0.7.4"
+version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
+checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
 dependencies = [
  "cfg-if",
- "winapi",
+ "windows-sys",
 ]
 
 [[package]]
@@ -1054,20 +979,23 @@ version = "0.3.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
 
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+
 [[package]]
 name = "log"
-version = "0.4.17"
+version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
-dependencies = [
- "cfg-if",
-]
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 
 [[package]]
 name = "mac-notification-sys"
-version = "0.5.6"
+version = "0.5.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e72d50edb17756489e79d52eb146927bec8eba9dd48faadf9ef08bca3791ad5"
+checksum = "9402858e87f85f88bf518bd2e68450640df1c99b1ddb3ea28c4d5d823bb54cdd"
 dependencies = [
  "cc",
  "dirs-next",
@@ -1093,18 +1021,18 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
 
 [[package]]
 name = "memmap2"
-version = "0.3.1"
+version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357"
+checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
 dependencies = [
  "libc",
 ]
 
 [[package]]
 name = "memmap2"
-version = "0.5.10"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
+checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
 dependencies = [
  "libc",
 ]
@@ -1127,15 +1055,6 @@ dependencies = [
  "autocfg",
 ]
 
-[[package]]
-name = "memoffset"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
-dependencies = [
- "autocfg",
-]
-
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -1144,21 +1063,20 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "miniz_oxide"
-version = "0.6.2"
+version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
 dependencies = [
  "adler",
 ]
 
 [[package]]
 name = "nix"
-version = "0.22.3"
+version = "0.24.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf"
+checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
 dependencies = [
- "bitflags",
- "cc",
+ "bitflags 1.3.2",
  "cfg-if",
  "libc",
  "memoffset 0.6.5",
@@ -1166,11 +1084,12 @@ dependencies = [
 
 [[package]]
 name = "nix"
-version = "0.24.3"
+version = "0.25.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
+checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
 dependencies = [
- "bitflags",
+ "autocfg",
+ "bitflags 1.3.2",
  "cfg-if",
  "libc",
  "memoffset 0.6.5",
@@ -1182,7 +1101,7 @@ version = "0.26.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "cfg-if",
  "libc",
  "memoffset 0.7.1",
@@ -1235,20 +1154,20 @@ dependencies = [
 
 [[package]]
 name = "num-traits"
-version = "0.2.15"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
 dependencies = [
  "autocfg",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "1.15.0"
+version = "1.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
 dependencies = [
- "hermit-abi 0.2.6",
+ "hermit-abi",
  "libc",
 ]
 
@@ -1283,24 +1202,18 @@ dependencies = [
 
 [[package]]
 name = "object"
-version = "0.30.4"
+version = "0.31.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385"
+checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1"
 dependencies = [
  "memchr",
 ]
 
 [[package]]
 name = "once_cell"
-version = "1.17.1"
+version = "1.18.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
-
-[[package]]
-name = "oorandom"
-version = "11.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
+checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
 
 [[package]]
 name = "ordered-stream"
@@ -1312,17 +1225,37 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "os_pipe"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177"
+dependencies = [
+ "libc",
+ "windows-sys",
+]
+
 [[package]]
 name = "parking"
 version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e"
 
+[[package]]
+name = "petgraph"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
+dependencies = [
+ "fixedbitset",
+ "indexmap 1.9.3",
+]
+
 [[package]]
 name = "pin-project-lite"
-version = "0.2.9"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
 
 [[package]]
 name = "pin-utils"
@@ -1336,34 +1269,6 @@ version = "0.3.27"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
 
-[[package]]
-name = "plotters"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
-dependencies = [
- "num-traits",
- "plotters-backend",
- "plotters-svg",
- "wasm-bindgen",
- "web-sys",
-]
-
-[[package]]
-name = "plotters-backend"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
-
-[[package]]
-name = "plotters-svg"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
-dependencies = [
- "plotters-backend",
-]
-
 [[package]]
 name = "polling"
 version = "2.8.0"
@@ -1371,13 +1276,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce"
 dependencies = [
  "autocfg",
- "bitflags",
+ "bitflags 1.3.2",
  "cfg-if",
  "concurrent-queue",
  "libc",
  "log",
  "pin-project-lite",
- "windows-sys 0.48.0",
+ "windows-sys",
 ]
 
 [[package]]
@@ -1398,9 +1303,9 @@ dependencies = [
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.58"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa1fb82fc0c281dd9671101b66b771ebbe1eaf967b96ac8740dcba4b70005ca8"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
 dependencies = [
  "unicode-ident",
 ]
@@ -1414,11 +1319,20 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "quick-xml"
+version = "0.28.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1"
+dependencies = [
+ "memchr",
+]
+
 [[package]]
 name = "quote"
-version = "1.0.27"
+version = "1.0.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
 dependencies = [
  "proc-macro2",
 ]
@@ -1453,35 +1367,13 @@ dependencies = [
  "getrandom",
 ]
 
-[[package]]
-name = "rayon"
-version = "1.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
-dependencies = [
- "either",
- "rayon-core",
-]
-
-[[package]]
-name = "rayon-core"
-version = "1.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
-dependencies = [
- "crossbeam-channel",
- "crossbeam-deque",
- "crossbeam-utils",
- "num_cpus",
-]
-
 [[package]]
 name = "redox_syscall"
 version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
@@ -1490,7 +1382,7 @@ version = "0.3.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
 ]
 
 [[package]]
@@ -1506,9 +1398,21 @@ dependencies = [
 
 [[package]]
 name = "regex"
-version = "1.8.2"
+version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d1a59b5d8e97dee33696bf13c5ba8ab85341c002922fba050069326b9c498974"
+checksum = "81bc1d4caf89fac26a70747fe603c130093b53c773888797a6329091246d651a"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.3.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fed1ceff11a1dddaee50c9dc8e4938bd106e9d89ae372f192311e7da498e3b69"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1517,9 +1421,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.7.2"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
 
 [[package]]
 name = "rustc-demangle"
@@ -1529,32 +1433,36 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
 
 [[package]]
 name = "rustix"
-version = "0.37.19"
+version = "0.37.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
+checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
 dependencies = [
- "bitflags",
- "errno 0.3.1",
+ "bitflags 1.3.2",
+ "errno 0.3.2",
  "io-lifetimes",
  "libc",
- "linux-raw-sys",
- "windows-sys 0.48.0",
+ "linux-raw-sys 0.3.8",
+ "windows-sys",
 ]
 
 [[package]]
-name = "ryu"
-version = "1.0.13"
+name = "rustix"
+version = "0.38.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f"
+dependencies = [
+ "bitflags 2.4.0",
+ "errno 0.3.2",
+ "libc",
+ "linux-raw-sys 0.4.5",
+ "windows-sys",
+]
 
 [[package]]
-name = "same-file"
-version = "1.0.6"
+name = "ryu"
+version = "1.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
 
 [[package]]
 name = "scoped-tls"
@@ -1562,37 +1470,31 @@ version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
 
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
 [[package]]
 name = "serde"
-version = "1.0.163"
+version = "1.0.183"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.163"
+version = "1.0.183"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8c805777e3930c8883389c602315a24224bcc738b63905ef87cd1420353ea93e"
+checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.96"
+version = "1.0.105"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
 dependencies = [
  "itoa",
  "ryu",
@@ -1601,20 +1503,20 @@ dependencies = [
 
 [[package]]
 name = "serde_repr"
-version = "0.1.12"
+version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
+checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
 name = "serde_spanned"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
+checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
 dependencies = [
  "serde",
 ]
@@ -1630,6 +1532,25 @@ dependencies = [
  "digest",
 ]
 
+[[package]]
+name = "signal-hook"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
+dependencies = [
+ "libc",
+ "signal-hook-registry",
+]
+
+[[package]]
+name = "signal-hook-registry"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
+dependencies = [
+ "libc",
+]
+
 [[package]]
 name = "slab"
 version = "0.4.8"
@@ -1640,56 +1561,42 @@ dependencies = [
 ]
 
 [[package]]
-name = "smallvec"
-version = "1.10.0"
+name = "slotmap"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
+dependencies = [
+ "version_check",
+]
 
 [[package]]
-name = "smithay-client-toolkit"
-version = "0.15.4"
+name = "smallvec"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3"
-dependencies = [
- "bitflags",
- "calloop",
- "dlib",
- "lazy_static",
- "log",
- "memmap2 0.3.1",
- "nix 0.22.3",
- "pkg-config",
- "wayland-client",
- "wayland-cursor",
- "wayland-protocols",
-]
+checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
 
 [[package]]
 name = "smithay-client-toolkit"
-version = "0.16.0"
+version = "0.17.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454"
+checksum = "e1476c3d89bb67079264b88aaf4f14358353318397e083b7c4e8c14517f55de7"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
+ "calloop",
  "dlib",
  "lazy_static",
  "log",
  "memmap2 0.5.10",
- "nix 0.24.3",
+ "nix 0.26.2",
  "pkg-config",
- "wayland-client",
+ "thiserror",
+ "wayland-backend",
+ "wayland-client 0.30.2",
  "wayland-cursor",
- "wayland-protocols",
-]
-
-[[package]]
-name = "smithay-clipboard"
-version = "0.6.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a345c870a1fae0b1b779085e81b51e614767c239e93503588e54c5b17f4b0e8"
-dependencies = [
- "smithay-client-toolkit 0.16.0",
- "wayland-client",
+ "wayland-protocols 0.30.1",
+ "wayland-protocols-wlr",
+ "wayland-scanner 0.30.1",
+ "xkbcommon",
 ]
 
 [[package]]
@@ -1714,27 +1621,6 @@ version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
-[[package]]
-name = "strum"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7ac893c7d471c8a21f31cfe213ec4f6d9afeed25537c772e08ef3f005f8729e"
-dependencies = [
- "strum_macros",
-]
-
-[[package]]
-name = "strum_macros"
-version = "0.22.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "339f799d8b549e3744c7ac7feb216383e4005d94bdb22561b3ab8f3b808ae9fb"
-dependencies = [
- "heck 0.3.3",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "syn"
 version = "1.0.109"
@@ -1748,9 +1634,9 @@ dependencies = [
 
 [[package]]
 name = "syn"
-version = "2.0.16"
+version = "2.0.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6f671d4b5ffdb8eadec19c0ae67fe2639df8684bd7bc4b83d986b8db549cf01"
+checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1759,26 +1645,25 @@ dependencies = [
 
 [[package]]
 name = "tauri-winrt-notification"
-version = "0.1.0"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c58de036c4d2e20717024de2a3c4bf56c301f07b21bc8ef9b57189fce06f1f3b"
+checksum = "4f5bff1d532fead7c43324a0fa33643b8621a47ce2944a633be4cb6c0240898f"
 dependencies = [
- "quick-xml",
- "strum",
+ "quick-xml 0.23.1",
  "windows",
 ]
 
 [[package]]
 name = "tempfile"
-version = "3.5.0"
+version = "3.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998"
+checksum = "dc02fddf48964c42031a0b3fe0428320ecf3a73c401040fc0096f97794310651"
 dependencies = [
  "cfg-if",
- "fastrand",
+ "fastrand 2.0.0",
  "redox_syscall 0.3.5",
- "rustix",
- "windows-sys 0.45.0",
+ "rustix 0.38.8",
+ "windows-sys",
 ]
 
 [[package]]
@@ -1792,22 +1677,22 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "1.0.40"
+version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.40"
+version = "1.0.47"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1822,10 +1707,11 @@ dependencies = [
 
 [[package]]
 name = "time"
-version = "0.3.21"
+version = "0.3.25"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f3403384eaacbca9923fa06940178ac13e4edb725486d70e8e15881d0c836cc"
+checksum = "b0fdd63d58b18d663fbdf70e049f00a22c8e42be082203be7f26589213cd75ea"
 dependencies = [
+ "deranged",
  "serde",
  "time-core",
 ]
@@ -1836,23 +1722,12 @@ version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
 
-[[package]]
-name = "tinytemplate"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
-dependencies = [
- "serde",
- "serde_json",
-]
-
 [[package]]
 name = "tokio"
-version = "1.29.0"
+version = "1.32.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "374442f06ee49c3a28a8fc9f01a2596fed7559c6b99b31279c3261778e77d84f"
+checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9"
 dependencies = [
- "autocfg",
  "backtrace",
  "bytes",
  "num_cpus",
@@ -1868,14 +1743,14 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
 name = "toml"
-version = "0.7.4"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
+checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
 dependencies = [
  "serde",
  "serde_spanned",
@@ -1885,20 +1760,20 @@ dependencies = [
 
 [[package]]
 name = "toml_datetime"
-version = "0.6.2"
+version = "0.6.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
 dependencies = [
  "serde",
 ]
 
 [[package]]
 name = "toml_edit"
-version = "0.19.10"
+version = "0.19.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
+checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
 dependencies = [
- "indexmap",
+ "indexmap 2.0.0",
  "serde",
  "serde_spanned",
  "toml_datetime",
@@ -1919,13 +1794,13 @@ dependencies = [
 
 [[package]]
 name = "tracing-attributes"
-version = "0.1.24"
+version = "0.1.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
+checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn 2.0.16",
+ "syn 2.0.29",
 ]
 
 [[package]]
@@ -1937,6 +1812,20 @@ dependencies = [
  "once_cell",
 ]
 
+[[package]]
+name = "tree_magic_mini"
+version = "3.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91adfd0607cacf6e4babdb870e9bec4037c1c4b151cfd279ccefc5e0c7feaa6d"
+dependencies = [
+ "bytecount",
+ "fnv",
+ "lazy_static",
+ "nom",
+ "once_cell",
+ "petgraph",
+]
+
 [[package]]
 name = "ttf-parser"
 version = "0.15.2"
@@ -1961,15 +1850,9 @@ dependencies = [
 
 [[package]]
 name = "unicode-ident"
-version = "1.0.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
-
-[[package]]
-name = "unicode-segmentation"
-version = "1.10.1"
+version = "1.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
+checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
 
 [[package]]
 name = "utf8parse"
@@ -1977,6 +1860,12 @@ version = "0.2.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
 
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+
 [[package]]
 name = "version_check"
 version = "0.9.4"
@@ -1989,16 +1878,6 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
 
-[[package]]
-name = "walkdir"
-version = "2.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
 [[package]]
 name = "wasi"
 version = "0.11.0+wasi-snapshot-preview1"
@@ -2006,73 +1885,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
-name = "wasm-bindgen"
-version = "0.2.86"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73"
-dependencies = [
- "cfg-if",
- "wasm-bindgen-macro",
-]
-
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.86"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb"
-dependencies = [
- "bumpalo",
- "log",
- "once_cell",
- "proc-macro2",
- "quote",
- "syn 2.0.16",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.86"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.86"
+name = "wayland-backend"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
+checksum = "41b48e27457e8da3b2260ac60d0a94512f5cba36448679f3747c0865b7893ed8"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.16",
- "wasm-bindgen-backend",
- "wasm-bindgen-shared",
+ "cc",
+ "downcast-rs",
+ "io-lifetimes",
+ "nix 0.26.2",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys 0.30.1",
 ]
 
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.86"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93"
-
 [[package]]
 name = "wayland-client"
 version = "0.29.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
 dependencies = [
- "bitflags",
+ "bitflags 1.3.2",
  "downcast-rs",
  "libc",
  "nix 0.24.3",
- "scoped-tls",
  "wayland-commons",
- "wayland-scanner",
- "wayland-sys",
+ "wayland-scanner 0.29.5",
+ "wayland-sys 0.29.5",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.30.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "489c9654770f674fc7e266b3c579f4053d7551df0ceb392f153adb1f9ed06ac8"
+dependencies = [
+ "bitflags 1.3.2",
+ "calloop",
+ "nix 0.26.2",
+ "wayland-backend",
+ "wayland-scanner 0.30.1",
 ]
 
 [[package]]
@@ -2084,17 +1936,17 @@ dependencies = [
  "nix 0.24.3",
  "once_cell",
  "smallvec",
- "wayland-sys",
+ "wayland-sys 0.29.5",
 ]
 
 [[package]]
 name = "wayland-cursor"
-version = "0.29.5"
+version = "0.30.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
+checksum = "2d0c3a0d5b4b688b07b0442362d3ed6bf04724fcc16cd69ab6285b90dbc487aa"
 dependencies = [
- "nix 0.24.3",
- "wayland-client",
+ "nix 0.26.2",
+ "wayland-client 0.30.2",
  "xcursor",
 ]
 
@@ -2104,10 +1956,35 @@ version = "0.29.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
 dependencies = [
- "bitflags",
- "wayland-client",
+ "bitflags 1.3.2",
+ "wayland-client 0.29.5",
  "wayland-commons",
- "wayland-scanner",
+ "wayland-scanner 0.29.5",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b28101e5ca94f70461a6c2d610f76d85ad223d042dd76585ab23d3422dd9b4d"
+dependencies = [
+ "bitflags 1.3.2",
+ "wayland-backend",
+ "wayland-client 0.30.2",
+ "wayland-scanner 0.30.1",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fce991093320e4a6a525876e6b629ab24da25f9baef0c2e0080ad173ec89588a"
+dependencies = [
+ "bitflags 1.3.2",
+ "wayland-backend",
+ "wayland-client 0.30.2",
+ "wayland-protocols 0.30.1",
+ "wayland-scanner 0.30.1",
 ]
 
 [[package]]
@@ -2121,25 +1998,35 @@ dependencies = [
  "xml-rs",
 ]
 
+[[package]]
+name = "wayland-scanner"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9b873b257fbc32ec909c0eb80dea312076a67014e65e245f5eb69a6b8ab330e"
+dependencies = [
+ "proc-macro2",
+ "quick-xml 0.28.2",
+ "quote",
+]
+
 [[package]]
 name = "wayland-sys"
 version = "0.29.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
 dependencies = [
- "dlib",
- "lazy_static",
  "pkg-config",
 ]
 
 [[package]]
-name = "web-sys"
-version = "0.3.63"
+name = "wayland-sys"
+version = "0.30.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3bdd9ef4e984da1187bf8110c5cf5b845fbc87a23602cdf912386a76fcd3a7c2"
+checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06"
 dependencies = [
- "js-sys",
- "wasm-bindgen",
+ "dlib",
+ "log",
+ "pkg-config",
 ]
 
 [[package]]
@@ -2186,65 +2073,35 @@ dependencies = [
  "windows_x86_64_msvc 0.39.0",
 ]
 
-[[package]]
-name = "windows-sys"
-version = "0.45.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
-dependencies = [
- "windows-targets 0.42.2",
-]
-
 [[package]]
 name = "windows-sys"
 version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
 dependencies = [
- "windows-targets 0.48.0",
+ "windows-targets",
 ]
 
 [[package]]
 name = "windows-targets"
-version = "0.42.2"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+checksum = "27f51fb4c64f8b770a823c043c7fad036323e1c48f55287b7bbb7987b2fcdf3b"
 dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
-dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc 0.48.3",
+ "windows_i686_gnu 0.48.3",
+ "windows_i686_msvc 0.48.3",
+ "windows_x86_64_gnu 0.48.3",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc 0.48.3",
 ]
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.42.2"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "fde1bb55ae4ce76a597a8566d82c57432bc69c039449d61572a7a353da28f68c"
 
 [[package]]
 name = "windows_aarch64_msvc"
@@ -2254,15 +2111,9 @@ checksum = "ec7711666096bd4096ffa835238905bb33fb87267910e154b18b44eaabb340f2"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.42.2"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "1513e8d48365a78adad7322fd6b5e4c4e99d92a69db8df2d435b25b1f1f286d4"
 
 [[package]]
 name = "windows_i686_gnu"
@@ -2272,15 +2123,9 @@ checksum = "763fc57100a5f7042e3057e7e8d9bdd7860d330070251a73d003563a3bb49e1b"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
-[[package]]
-name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "60587c0265d2b842298f5858e1a5d79d146f9ee0c37be5782e92a6eb5e1d7a83"
 
 [[package]]
 name = "windows_i686_msvc"
@@ -2290,15 +2135,9 @@ checksum = "7bc7cbfe58828921e10a9f446fcaaf649204dcfe6c1ddd712c5eebae6bda1106"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
-[[package]]
-name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "224fe0e0ffff5d2ea6a29f82026c8f43870038a0ffc247aa95a52b47df381ac4"
 
 [[package]]
 name = "windows_x86_64_gnu"
@@ -2308,27 +2147,15 @@ checksum = "6868c165637d653ae1e8dc4d82c25d4f97dd6605eaa8d784b5c6e0ab2a252b65"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "62fc52a0f50a088de499712cbc012df7ebd94e2d6eb948435449d76a6287e7ad"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.42.2"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "2093925509d91ea3d69bcd20238f4c2ecdb1a29d3c281d026a09705d0dd35f3d"
 
 [[package]]
 name = "windows_x86_64_msvc"
@@ -2338,23 +2165,35 @@ checksum = "5e4d40883ae9cae962787ca76ba76390ffa29214667a111db9e0a1ad8377e809"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.42.2"
+version = "0.48.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+checksum = "b6ade45bc8bf02ae2aa34a9d54ba660a1a58204da34ba793c00d83ca3730b5f1"
 
 [[package]]
-name = "windows_x86_64_msvc"
-version = "0.48.0"
+name = "winnow"
+version = "0.5.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "83817bbecf72c73bad717ee86820ebf286203d2e04c3951f3cd538869c897364"
+dependencies = [
+ "memchr",
+]
 
 [[package]]
-name = "winnow"
-version = "0.4.6"
+name = "wl-clipboard-rs"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
+checksum = "981a303dfbb75d659f6612d05a14b2e363c103d24f676a2d44a00d18507a1ad9"
 dependencies = [
- "memchr",
+ "derive-new",
+ "libc",
+ "log",
+ "nix 0.24.3",
+ "os_pipe",
+ "tempfile",
+ "thiserror",
+ "tree_magic_mini",
+ "wayland-client 0.29.5",
+ "wayland-protocols 0.29.5",
 ]
 
 [[package]]
@@ -2379,12 +2218,9 @@ dependencies = [
 
 [[package]]
 name = "xdg"
-version = "2.5.0"
+version = "2.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee"
-dependencies = [
- "home",
-]
+checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
 
 [[package]]
 name = "xdg-home"
@@ -2396,11 +2232,21 @@ dependencies = [
  "winapi",
 ]
 
+[[package]]
+name = "xkbcommon"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52db25b599e92bf6e3904134618728eeb7b49a5a4f38f107f92399bb9c496b88"
+dependencies = [
+ "libc",
+ "memmap2 0.7.1",
+]
+
 [[package]]
 name = "xml-rs"
-version = "0.8.13"
+version = "0.8.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d8f380ae16a37b30e6a2cf67040608071384b1450c189e61bea3ff57cde922d"
+checksum = "47430998a7b5d499ccee752b41567bc3afc57e1327dc855b1a2aa44ce29b5fa1"
 
 [[package]]
 name = "yeslogic-fontconfig-sys"
@@ -2416,18 +2262,20 @@ dependencies = [
 
 [[package]]
 name = "zbus"
-version = "3.12.0"
+version = "3.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29242fa5ec5693629ae74d6eb1f69622a9511f600986d6d9779bccf36ac316e3"
+checksum = "31de390a2d872e4cd04edd71b425e29853f786dc99317ed72d73d6fcf5ebb948"
 dependencies = [
  "async-broadcast",
  "async-executor",
  "async-fs",
  "async-io",
  "async-lock",
+ "async-process",
  "async-recursion",
  "async-task",
  "async-trait",
+ "blocking",
  "byteorder",
  "derivative",
  "enumflags2",
@@ -2455,9 +2303,9 @@ dependencies = [
 
 [[package]]
 name = "zbus_macros"
-version = "3.12.0"
+version = "3.14.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "537793e26e9af85f774801dc52c6f6292352b2b517c5cf0449ffd3735732a53a"
+checksum = "41d1794a946878c0e807f55a397187c11fc7a038ba5d868e7db4f3bd7760bc9d"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -2469,9 +2317,9 @@ dependencies = [
 
 [[package]]
 name = "zbus_names"
-version = "2.5.1"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82441e6033be0a741157a72951a3e4957d519698f3a824439cc131c5ba77ac2a"
+checksum = "fb80bb776dbda6e23d705cf0123c3b95df99c4ebeaec6c2599d4a5419902b4a9"
 dependencies = [
  "serde",
  "static_assertions",
@@ -2480,9 +2328,9 @@ dependencies = [
 
 [[package]]
 name = "zvariant"
-version = "3.13.0"
+version = "3.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cb36cd95352132911c9c99fdcc1635de5c2c139bd34cbcf6dfb8350ee8ff6a7"
+checksum = "44b291bee0d960c53170780af148dca5fa260a63cdd24f1962fa82e03e53338c"
 dependencies = [
  "byteorder",
  "enumflags2",
@@ -2494,9 +2342,9 @@ dependencies = [
 
 [[package]]
 name = "zvariant_derive"
-version = "3.13.0"
+version = "3.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b34951e1ac64f3a1443fe7181256b9ed6a811a1631917566c3d5ca718d8cf33"
+checksum = "934d7a7dfc310d6ee06c87ffe88ef4eca7d3e37bb251dece2ef93da8f17d8ecd"
 dependencies = [
  "proc-macro-crate",
  "proc-macro2",
@@ -2507,9 +2355,9 @@ dependencies = [
 
 [[package]]
 name = "zvariant_utils"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53b22993dbc4d128a17a3b6c92f1c63872dd67198537ee728d8b5d7c40640a8b"
+checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200"
 dependencies = [
  "proc-macro2",
  "quote",
diff --git a/Cargo.toml b/Cargo.toml
index c4ded97..91becd1 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,7 +11,7 @@ readme = "README.md"
 keywords = ["wayland", "launcher", "wlroots"]
 
 [dependencies]
-smithay-client-toolkit = "0.15"
+smithay-client-toolkit = "0.17"
 fontdue = "0.7"
 image = { version = "0.24", default-features = false }
 fuzzy-matcher = "0.3"
@@ -22,7 +22,6 @@ xdg = "2.5"
 toml = "0.7"
 serde = { version = "1.0", features = ["derive"] }
 hex = "0.4"
-smithay-clipboard = "0.6"
 log = "0.4"
 env_logger = "0.10"
 x11-keysymdef = "0.2.0"
@@ -31,26 +30,22 @@ notify-rust = "4.8"
 clap = { version = "4.3", features = ["derive"] }
 csv = "1.2"
 futures = "0.3"
+wayland-client = "0.30.2"
+anyhow = "1.0.75"
+wl-clipboard-rs = "0.7.0"
 
 [dependencies.tokio]
-version = "1.29"
+version = "1.32"
 features = [
-    "fs",
-    "rt-multi-thread",
-    "io-util",
-    "time",
-    "rt",
-    "macros",
-    "io-std",
+  "fs",
+  "rt-multi-thread",
+  "io-util",
+  "time",
+  "rt",
+  "macros",
+  "io-std",
 ]
 default-features = false
 
 [profile.release]
 lto = true
-
-[dev-dependencies]
-criterion = { version = "0.5", features = ["async_tokio"] }
-
-[[bench]]
-name = "selection"
-harness = false
diff --git a/benches/selection.rs b/benches/selection.rs
deleted file mode 100644
index ce8c6ad..0000000
--- a/benches/selection.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-use criterion::{criterion_group, criterion_main, Criterion};
-use tokio::runtime::Builder;
-
-use kickoff::selection::*;
-
-fn bench_build(c: &mut Criterion) {
-    c.bench_function("build_path", |b| {
-        b.to_async(Builder::new_multi_thread().enable_all().build().unwrap())
-            .iter(|| async {
-                let mut element_build = ElementListBuilder::new();
-                element_build.add_path();
-                element_build.build().await.unwrap();
-            })
-    });
-}
-
-criterion_group!(benches, bench_build);
-criterion_main!(benches);
diff --git a/src/app.rs b/src/app.rs
new file mode 100644
index 0000000..a460294
--- /dev/null
+++ b/src/app.rs
@@ -0,0 +1,280 @@
+use std::time::Duration;
+use std::{cmp, process};
+
+use crate::config::{Config, History};
+use crate::font::Font;
+use crate::selection::{Element, ElementList};
+use crate::Args;
+use image::{ImageBuffer, RgbaImage};
+use log::*;
+use nix::{
+    sys::wait::{waitpid, WaitPidFlag, WaitStatus},
+    unistd::{fork, ForkResult},
+};
+use notify_rust::Notification;
+
+pub struct App {
+    pub config: Config,
+    pub select_index: usize,
+    pub select_input: bool,
+    pub all_entries: ElementList,
+    pub query: String,
+    pub font: Font,
+    pub history: Option<History>,
+    pub last_search_result: Vec<usize>,
+    pub args: Args,
+}
+
+impl App {
+    pub fn new(
+        args: Args,
+        config: Config,
+        all_entries: ElementList,
+        font: Font,
+        history: Option<History>,
+    ) -> Self {
+        let mut app = Self {
+            args,
+            config,
+            font,
+            select_index: 0,
+            select_input: false,
+            history,
+            all_entries,
+            query: String::new(),
+            last_search_result: Vec::new(),
+        };
+        app.search();
+
+        app
+    }
+
+    pub fn complete(&mut self) {
+        if !self.select_input {
+            let app = (*self
+                .all_entries
+                .as_ref_vec()
+                .get(*self.last_search_result.get(self.select_index).unwrap())
+                .unwrap())
+            .clone();
+            if self.query == app.name {
+                self.select_index = if self.select_index < self.last_search_result.len() - 1 {
+                    self.select_index + 1
+                } else {
+                    self.select_index
+                };
+            }
+            self.query.clear();
+            self.query.push_str(&app.name);
+        }
+    }
+
+    pub fn nav_up(&mut self, distance: usize) {
+        if self.select_index > 0 {
+            self.select_index = self.select_index.saturating_sub(distance);
+        } else if !self.query.is_empty() {
+            self.select_input = true;
+        }
+    }
+
+    pub fn nav_down(&mut self, distance: usize) {
+        if self.select_input && !self.last_search_result.is_empty() {
+            self.select_input = false;
+            self.select_index = 0;
+        } else if !self.last_search_result.is_empty()
+            && self.select_index < self.last_search_result.len() - distance
+        {
+            self.select_index += distance;
+        }
+    }
+
+    pub fn delete(&mut self) {
+        self.query.pop();
+        self.search();
+    }
+
+    pub fn delete_word(&mut self) {
+        self.query.pop();
+        loop {
+            let removed_char = self.query.pop();
+            if removed_char.unwrap_or(' ') == ' ' {
+                break;
+            }
+        }
+        self.search();
+    }
+
+    pub fn execute(&mut self) {
+        let element = if self.select_input {
+            Element {
+                name: self.query.to_string(),
+                value: self.query.to_string(),
+                base_score: 0,
+            }
+        } else {
+            (*self
+                .all_entries
+                .as_ref_vec()
+                .get(*self.last_search_result.get(self.select_index).unwrap())
+                .unwrap())
+            .clone()
+        };
+        if self.args.stdout {
+            print!("{}", element.value);
+            if let Some(mut history) = self.history.take() {
+                history.inc(&element);
+                history.save().unwrap();
+            }
+        } else {
+            execute(&element, self.history.take());
+        }
+    }
+
+    pub fn insert(&mut self, input: &str) {
+        self.query.push_str(input);
+        self.search();
+    }
+
+    pub fn search(&mut self) {
+        self.last_search_result = Vec::new();
+        let search_results = self.all_entries.search(&self.query);
+
+        self.select_input = false;
+        self.select_index = 0;
+        if search_results.is_empty() {
+            self.select_input = true;
+        }
+
+        // Build list of indices to search results
+        let all_entries = self.all_entries.as_ref_vec();
+        for entry in search_results {
+            let index = all_entries.iter().position(|x| x == &entry);
+            if let Some(i) = index {
+                self.last_search_result.push(i);
+            }
+        }
+    }
+
+    pub fn draw(&mut self, width: u32, height: u32, scale: i32) -> RgbaImage {
+        let search_results: Vec<&Element> = self
+            .last_search_result
+            .iter()
+            .map(|index| *self.all_entries.as_ref_vec().get(*index).unwrap())
+            .collect();
+
+        self.font.set_scale(scale);
+        let padding = self.config.padding * scale as u32;
+        let font_size = self.config.font_size * scale as f32;
+
+        let mut img =
+            ImageBuffer::from_pixel(width, height, self.config.colors.background.to_rgba());
+        let prompt = &self.config.prompt;
+        let prompt_width = if !prompt.is_empty() {
+            let (width, _) = self.font.render(
+                prompt,
+                &self.config.colors.prompt,
+                &mut img,
+                padding,
+                padding,
+                None,
+            );
+            width
+        } else {
+            0
+        };
+
+        if !self.query.is_empty() {
+            let color = if self.select_input {
+                &self.config.colors.text_selected
+            } else {
+                &self.config.colors.text_query
+            };
+            self.font.render(
+                &self.query,
+                color,
+                &mut img,
+                padding + prompt_width,
+                padding,
+                None,
+            );
+        }
+
+        let spacer = (1.5 * font_size) as u32;
+        let max_entries = ((height.saturating_sub(2 * padding).saturating_sub(spacer)) as f32
+            / (font_size * 1.2)) as usize;
+        let offset = if self.select_index > (max_entries / 2) {
+            self.select_index - max_entries / 2
+        } else {
+            0
+        };
+
+        for (i, matched) in search_results
+            .iter()
+            .enumerate()
+            .take(cmp::min(max_entries + offset, search_results.len()))
+            .skip(offset)
+        {
+            let color = if i == self.select_index && !self.select_input {
+                &self.config.colors.text_selected
+            } else {
+                &self.config.colors.text
+            };
+            self.font.render(
+                &matched.name,
+                color,
+                &mut img,
+                padding,
+                padding + spacer + (i - offset) as u32 * (font_size * 1.2) as u32,
+                Some((width - (padding * 2)) as usize),
+            );
+        }
+
+        img
+    }
+}
+
+fn execute(elem: &Element, history: Option<History>) {
+    match unsafe { fork() } {
+        Ok(ForkResult::Parent { child }) => {
+            // We can't make that to long, since for some reason, even if this would be after a fork and the main programm exits,
+            // wayland keeps the window alive
+            std::thread::sleep(Duration::new(0, 100_000_000));
+            match waitpid(child, Some(WaitPidFlag::WNOHANG)) {
+                Ok(WaitStatus::StillAlive | WaitStatus::Exited(_, 0)) => {
+                    if let Some(mut history) = history {
+                        history.inc(elem);
+                        match history.save() {
+                            Ok(()) => {}
+                            Err(e) => {
+                                error!("{e}");
+                            }
+                        };
+                    }
+                }
+                Ok(_) => {
+                    /* Every non 0 statuscode holds no information since it's
+                    origin can be the started application or a file not found error.
+                    In either case the error has already been logged and does not
+                    need to be handled here. */
+                }
+                Err(err) => error!("{err}"),
+            }
+        }
+
+        Ok(ForkResult::Child) => {
+            let err = exec::Command::new("sh").args(&["-c", &elem.value]).exec();
+
+            // Won't be executed when exec was successful
+            error!("{err}");
+
+            Notification::new()
+                .summary("Kickoff")
+                .body(&format!("{err}"))
+                .timeout(5000)
+                .show()
+                .unwrap();
+            process::exit(2);
+        }
+        Err(e) => error!("{e}"),
+    }
+}
diff --git a/src/color.rs b/src/color.rs
index 84697ca..b125411 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -4,12 +4,12 @@ use serde::{Deserialize, Deserializer};
 use std::fmt;
 use std::str::FromStr;
 
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 pub struct Color(pub u8, pub u8, pub u8, pub u8);
 
 impl From<css_color::Rgba> for Color {
     fn from(c: css_color::Rgba) -> Self {
-        Color(
+        Self(
             (c.red * 255. * c.alpha) as u8,
             (c.green * 255. * c.alpha) as u8,
             (c.blue * 255. * c.alpha) as u8,
@@ -19,13 +19,13 @@ impl From<css_color::Rgba> for Color {
 }
 
 impl Color {
-    pub fn to_rgba(&self) -> Rgba<u8> {
+    pub const fn to_rgba(&self) -> Rgba<u8> {
         Rgba([self.0, self.1, self.2, self.3])
     }
 }
 
 impl<'de> Deserialize<'de> for Color {
-    fn deserialize<D>(deserializer: D) -> Result<Color, D::Error>
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
     {
@@ -46,10 +46,6 @@ impl<'de> Visitor<'de> for ColorVisitor {
         E: de::Error,
     {
         let c = css_color::Rgba::from_str(value);
-        if let Ok(c) = c {
-            Ok(Color::from(c))
-        } else {
-            Err(de::Error::custom(""))
-        }
+        c.map_or_else(|_| Err(de::Error::custom("")), |c| Ok(Color::from(c)))
     }
 }
diff --git a/src/config.rs b/src/config.rs
index a3ceeb9..cd15b5a 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,14 +1,17 @@
 use crate::color::Color;
 use crate::keybinds::{KeyCombo, Modifiers};
-use smithay_client_toolkit::seat::keyboard::{keysyms, ModifiersState};
+use crate::selection::Element;
+use log::*;
+use smithay_client_toolkit::seat::keyboard::{keysyms, Modifiers as ModifiersState};
 use std::fmt::Debug;
 use std::fs::{read_to_string, write};
 use std::path::PathBuf;
+use std::time::SystemTime;
 use xdg::BaseDirectories;
 
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
 
-#[derive(Deserialize, Clone)]
+#[derive(Deserialize, Clone, Debug)]
 #[serde(default)]
 pub struct ColorConfig {
     pub background: Color,
@@ -31,13 +34,13 @@ pub struct KeybindingsConfig {
     pub exit: Vec<KeyCombo>,
 }
 
-#[derive(Deserialize, Clone)]
+#[derive(Deserialize, Clone, Debug)]
 #[serde(default)]
 pub struct HistoryConfig {
     pub decrease_interval: u64,
 }
 
-#[derive(Deserialize, Clone)]
+#[derive(Deserialize, Clone, Debug)]
 #[serde(default)]
 pub struct Config {
     pub prompt: String,
@@ -52,7 +55,7 @@ pub struct Config {
 
 impl Default for KeybindingsConfig {
     fn default() -> Self {
-        KeybindingsConfig {
+        Self {
             delete: vec![
                 KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_BackSpace),
                 KeyCombo::new(Modifiers::default(), keysyms::XKB_KEY_Delete),
@@ -111,7 +114,7 @@ impl Default for KeybindingsConfig {
 }
 impl Default for ColorConfig {
     fn default() -> Self {
-        ColorConfig {
+        Self {
             background: Color(40, 44, 52, 170),
             prompt: Color(171, 178, 191, 255),
             text: Color(255, 255, 255, 255),
@@ -122,8 +125,8 @@ impl Default for ColorConfig {
 }
 impl Default for Config {
     fn default() -> Self {
-        Config {
-            prompt: "".to_owned(),
+        Self {
+            prompt: String::new(),
             padding: 100,
             font: None,
             fonts: vec![],
@@ -136,7 +139,7 @@ impl Default for Config {
 }
 impl Default for HistoryConfig {
     fn default() -> Self {
-        HistoryConfig {
+        Self {
             decrease_interval: 48,
         }
     }
@@ -159,3 +162,110 @@ impl Config {
         }
     }
 }
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct HistoryEntry {
+    pub name: String,
+    pub value: String,
+    pub num_used: usize,
+}
+
+#[derive(Debug)]
+pub struct History {
+    entries: Vec<HistoryEntry>,
+    path: PathBuf,
+}
+
+impl Default for History {
+    fn default() -> Self {
+        let xdg_dirs = BaseDirectories::with_prefix("kickoff")
+            .expect("Failed to determine xdg base directory");
+        Self {
+            entries: Vec::new(),
+            path: xdg_dirs
+                .place_cache_file("default.csv")
+                .expect("Failed to place history file"),
+        }
+    }
+}
+
+impl History {
+    pub const fn as_vec(&self) -> &Vec<HistoryEntry> {
+        &self.entries
+    }
+
+    pub fn load(path: Option<PathBuf>, decrease_interval: u64) -> Result<Self, std::io::Error> {
+        let history_path = if let Some(path) = path {
+            path
+        } else {
+            let xdg_dirs = BaseDirectories::with_prefix("kickoff")?;
+            if let Some(path) = xdg_dirs.find_cache_file("default.csv") {
+                path
+            } else {
+                return Ok(Self {
+                    entries: Vec::new(),
+                    path: xdg_dirs.place_cache_file("default.csv")?,
+                });
+            }
+        };
+
+        let mut res = Self {
+            entries: Vec::new(),
+            path: history_path.clone(),
+        };
+
+        if history_path.exists() {
+            let last_modified = history_path.metadata()?.modified()?;
+            let interval_diff = if decrease_interval > 0 {
+                SystemTime::now()
+                    .duration_since(SystemTime::UNIX_EPOCH)
+                    .unwrap()
+                    .as_secs()
+                    / (3600 * decrease_interval)
+                    - last_modified
+                        .duration_since(SystemTime::UNIX_EPOCH)
+                        .unwrap()
+                        .as_secs()
+                        / (3600 * decrease_interval)
+            } else {
+                0
+            };
+
+            let mut rdr = csv::Reader::from_path(history_path).unwrap();
+            for result in rdr.deserialize() {
+                let mut record: HistoryEntry = result?;
+                record.num_used = record.num_used.saturating_sub(interval_diff as usize);
+                if record.num_used > 0 {
+                    res.entries.push(record);
+                }
+            }
+        } else {
+            info!("History file does not exists, will be created on saving");
+        }
+
+        Ok(res)
+    }
+
+    pub fn inc(&mut self, element: &Element) {
+        if let Some(entry) = self.entries.iter_mut().find(|x| x.name == element.name) {
+            entry.num_used += 1;
+            entry.value = element.value.clone();
+        } else {
+            self.entries.push(HistoryEntry {
+                name: element.name.clone(),
+                value: element.value.clone(),
+                num_used: 1,
+            });
+        }
+    }
+
+    pub fn save(&self) -> Result<(), std::io::Error> {
+        let mut wtr = csv::Writer::from_path(&self.path)?;
+        for entry in &self.entries {
+            wtr.serialize(entry)?;
+        }
+        wtr.flush()?;
+
+        Ok(())
+    }
+}
diff --git a/src/font.rs b/src/font.rs
index 2d71401..a8bbd9f 100644
--- a/src/font.rs
+++ b/src/font.rs
@@ -24,7 +24,7 @@ pub struct Font {
 }
 
 impl Font {
-    pub async fn new(font_names: Vec<String>, size: f32) -> io::Result<Font> {
+    pub async fn new(font_names: Vec<String>, size: f32) -> io::Result<Self> {
         let fc = Fontconfig::new().expect("Couldn't load fontconfig");
         let font_names = if font_names.is_empty() {
             vec![String::new()]
@@ -48,7 +48,7 @@ impl Font {
             );
         }
 
-        Ok(Font {
+        Ok(Self {
             fonts: font_data,
             layout: RefCell::new(Layout::new(CoordinateSystem::PositiveYDown)),
             size,
@@ -64,6 +64,8 @@ impl Font {
 
     fn render_glyph(&self, conf: GlyphRasterConfig) -> (Metrics, Vec<u8>) {
         let mut glyph_cache = self.glyph_cache.borrow_mut();
+
+        #[allow(clippy::option_if_let_else)]
         if let Some(bitmap) = glyph_cache.get(&conf) {
             bitmap.clone()
         } else {
@@ -138,7 +140,7 @@ impl Font {
 
                     match image.get_pixel_mut_checked(x as u32, y as u32) {
                         Some(pixel) => {
-                            pixel.blend(&image::Rgba([color.0, color.1, color.2, *alpha]))
+                            pixel.blend(&image::Rgba([color.0, color.1, color.2, *alpha]));
                         }
                         None => continue,
                     }
diff --git a/src/gui.rs b/src/gui.rs
index db0b63e..ba615f2 100644
--- a/src/gui.rs
+++ b/src/gui.rs
@@ -1,342 +1,448 @@
+use crate::{keybinds::Keybindings, App};
+use image::{Pixel, Rgba};
+use log::*;
 use smithay_client_toolkit::{
-    get_surface_scale_factor,
-    reexports::{
-        calloop,
-        client::protocol::{
-            wl_output,
-            wl_pointer::{ButtonState, Event as PEvent},
-            wl_seat, wl_shm, wl_surface,
-        },
-        client::{Attached, DispatchData, Display, Main},
-        protocols::wlr::unstable::layer_shell::v1::client::{
-            zwlr_layer_shell_v1, zwlr_layer_surface_v1,
-            zwlr_layer_surface_v1::KeyboardInteractivity,
-        },
-    },
+    compositor::{CompositorHandler, CompositorState},
+    delegate_compositor, delegate_keyboard, delegate_layer, delegate_output, delegate_pointer,
+    delegate_registry, delegate_seat, delegate_shm,
+    output::{OutputHandler, OutputState},
+    registry::{ProvidesRegistryState, RegistryState},
+    registry_handlers,
     seat::{
-        keyboard::{
-            keysyms, map_keyboard_repeat, Event as KbEvent, KeyState, ModifiersState, RepeatKind,
+        keyboard::{KeyEvent, KeyboardHandler, Modifiers},
+        pointer::{PointerEvent, PointerEventKind, PointerHandler},
+        Capability, SeatHandler, SeatState,
+    },
+    shell::{
+        wlr_layer::{
+            Anchor, KeyboardInteractivity, Layer, LayerShell, LayerShellHandler, LayerSurface,
+            LayerSurfaceConfigure,
         },
-        with_seat_data,
+        WaylandSurface,
     },
-    shm::DoubleMemPool,
+    shm::{slot::SlotPool, Shm, ShmHandler},
+};
+use std::io::{BufWriter, Read, Write};
+use wayland_client::{
+    globals::registry_queue_init,
+    protocol::{wl_keyboard, wl_output, wl_pointer, wl_seat, wl_shm, wl_surface},
+    Connection, QueueHandle,
 };
+use wl_clipboard_rs::paste::{get_contents, ClipboardType, Error, MimeType, Seat};
+
+#[derive(Clone)]
+pub enum Action {
+    Execute,
+    Exit,
+    Complete,
+    NavUp,
+    NavDown,
+    Delete,
+    DeleteWord,
+    Paste,
+    Insert(String),
+}
 
-use smithay_clipboard::Clipboard;
+pub fn run(app: App) {
+    let conn = Connection::connect_to_env().unwrap();
 
-use log::*;
-use std::cell::Cell;
-use std::io::{BufWriter, ErrorKind, Seek, Write};
-use std::rc::Rc;
+    let (globals, mut event_queue) = registry_queue_init(&conn).unwrap();
+    let qh = event_queue.handle();
 
-use image::{Pixel, Rgba, RgbaImage};
+    let compositor = CompositorState::bind(&globals, &qh).expect("wl_compositor is not available");
+    let layer_shell = LayerShell::bind(&globals, &qh).expect("layer shell is not available");
+    let shm = Shm::bind(&globals, &qh).expect("wl_shm is not available");
 
-use crate::keybinds::Keybindings;
+    let surface = compositor.create_surface(&qh);
 
-#[derive(PartialEq, Eq, Copy, Clone)]
-pub enum RenderEvent {
-    Configure { width: u32, height: u32 },
-    Closed,
-}
+    let layer = layer_shell.create_layer_surface(&qh, surface, Layer::Top, Some("kickoff"), None);
 
-pub struct Surface {
-    surface: wl_surface::WlSurface,
-    layer_surface: Main<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>,
-    pub next_render_event: Rc<Cell<Option<RenderEvent>>>,
-    pools: DoubleMemPool,
-    pub dimensions: (u32, u32),
-}
+    layer.set_anchor(Anchor::all());
+    layer.set_keyboard_interactivity(KeyboardInteractivity::Exclusive);
 
-impl Surface {
-    pub fn set_dimensions(&mut self, width: u32, height: u32) -> bool {
-        if self.dimensions != (width, height) {
-            self.dimensions = (width, height);
-            true
-        } else {
-            false
-        }
-    }
-    pub fn new(
-        output: Option<&wl_output::WlOutput>,
-        surface: wl_surface::WlSurface,
-        layer_shell: &Attached<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
-        pools: DoubleMemPool,
-    ) -> Self {
-        let layer_surface = layer_shell.get_layer_surface(
-            &surface,
-            output,
-            zwlr_layer_shell_v1::Layer::Overlay,
-            "launcher".to_owned(),
-        );
-
-        // Anchor to all corners of the output to archive fullscreen
-        layer_surface.set_anchor(zwlr_layer_surface_v1::Anchor::all());
-
-        // Enable Keyboard interactivity
-        layer_surface.set_keyboard_interactivity(KeyboardInteractivity::Exclusive);
-
-        let next_render_event = Rc::new(Cell::new(None::<RenderEvent>));
-        let next_render_event_handle = Rc::clone(&next_render_event);
-        layer_surface.quick_assign(move |layer_surface, event, _| {
-            match (event, next_render_event_handle.get()) {
-                (zwlr_layer_surface_v1::Event::Closed, _) => {
-                    next_render_event_handle.set(Some(RenderEvent::Closed));
-                }
-                (
-                    zwlr_layer_surface_v1::Event::Configure {
-                        serial,
-                        width,
-                        height,
-                    },
-                    next,
-                ) if next != Some(RenderEvent::Closed) => {
-                    layer_surface.ack_configure(serial);
-                    next_render_event_handle.set(Some(RenderEvent::Configure { width, height }));
+    layer.commit();
+
+    let pool = SlotPool::new(256 * 256 * 4, &shm).expect("Failed to create pool");
+
+    let mut gui_layer = GuiLayer {
+        registry_state: RegistryState::new(&globals),
+        seat_state: SeatState::new(&globals, &qh),
+        output_state: OutputState::new(&globals, &qh),
+        shm,
+
+        exit: false,
+        first_configure: true,
+        pool,
+        width: 256,
+        height: 256,
+        layer,
+        keyboard: None,
+        pointer: None,
+        scale_factor: 1,
+        modifiers: Modifiers::default(),
+        keybindings: Keybindings::from(app.config.keybindings.clone()),
+        app,
+        next_action: None,
+    };
+
+    loop {
+        event_queue.blocking_dispatch(&mut gui_layer).unwrap();
+        match &gui_layer.next_action.take() {
+            Some(Action::Exit) => gui_layer.exit = true,
+            Some(Action::Complete) => gui_layer.app.complete(),
+            Some(Action::Delete) => gui_layer.app.delete(),
+            Some(Action::DeleteWord) => gui_layer.app.delete_word(),
+            Some(Action::NavUp) => gui_layer.app.nav_up(1),
+            Some(Action::NavDown) => gui_layer.app.nav_down(1),
+            Some(Action::Insert(s)) => gui_layer.app.insert(s),
+            Some(Action::Execute) => {
+                gui_layer.app.execute();
+                gui_layer.exit = true;
+            }
+            Some(Action::Paste) => {
+                let result =
+                    get_contents(ClipboardType::Regular, Seat::Unspecified, MimeType::Text);
+                match result {
+                    Ok((mut pipe, _)) => {
+                        let mut contents = vec![];
+                        pipe.read_to_end(&mut contents).unwrap();
+                        let input = String::from_utf8(contents).unwrap();
+                        gui_layer.app.insert(&input);
+                    }
+                    Err(Error::NoSeats | Error::ClipboardEmpty | Error::NoMimeType) => {}
+                    Err(e) => error!("{e}"),
                 }
-                _ => todo!(),
             }
-        });
-
-        // Commit so that the server will send a configure event
-        surface.commit();
+            _ => {}
+        }
 
-        Self {
-            surface,
-            layer_surface,
-            next_render_event,
-            pools,
-            dimensions: (0, 0),
+        if gui_layer.exit {
+            debug!("exiting kickoff");
+            break;
         }
     }
+}
 
-    pub fn draw(&mut self, mut image: RgbaImage, scale: i32) -> Result<(), std::io::Error> {
-        if let Some(pool) = self.pools.pool() {
-            let width = self.dimensions.0 as i32 * scale;
-            let height = self.dimensions.1 as i32 * scale;
-            let stride = 4 * width;
-
-            // First make sure the pool is the right size
-            pool.resize((stride * height) as usize)?;
-
-            // Create a new buffer from the pool
-            let buffer = pool.buffer(0, width, height, stride, wl_shm::Format::Argb8888);
-            image.pixels_mut().for_each(|pixel| {
-                let channels = pixel.channels_mut();
-                *pixel = *Rgba::from_slice(&[channels[2], channels[1], channels[0], channels[3]]);
-            });
-
-            // Write the color to all bytes of the pool
-            pool.rewind()?;
-            {
-                let mut writer = BufWriter::new(&mut *pool);
-                writer.write_all(image.as_raw())?;
-                writer.flush()?;
-            }
+struct GuiLayer {
+    registry_state: RegistryState,
+    seat_state: SeatState,
+    output_state: OutputState,
+    shm: Shm,
 
-            // Attach the buffer to the surface and mark the entire surface as damaged
-            self.surface.attach(Some(&buffer), 0, 0);
-            self.surface.damage_buffer(0, 0, width, height);
+    exit: bool,
+    first_configure: bool,
+    pool: SlotPool,
+    width: u32,
+    height: u32,
+    layer: LayerSurface,
+    keyboard: Option<wl_keyboard::WlKeyboard>,
+    pointer: Option<wl_pointer::WlPointer>,
+    scale_factor: i32,
+    modifiers: Modifiers,
+    app: App,
+    next_action: Option<Action>,
+    keybindings: Keybindings,
+}
 
-            // Finally, commit the surface
-            self.surface.commit();
-            Ok(())
-        } else {
-            Err(std::io::Error::new(
-                ErrorKind::Other,
-                "All pools are in use by Wayland",
-            ))
-        }
+impl CompositorHandler for GuiLayer {
+    fn scale_factor_changed(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _surface: &wl_surface::WlSurface,
+        new_factor: i32,
+    ) {
+        self.scale_factor = new_factor;
+        self.layer.set_buffer_scale(new_factor as u32).unwrap();
     }
 
-    pub fn get_scale(&self) -> i32 {
-        get_surface_scale_factor(&self.surface)
+    fn frame(
+        &mut self,
+        _conn: &Connection,
+        qh: &QueueHandle<Self>,
+        _surface: &wl_surface::WlSurface,
+        _time: u32,
+    ) {
+        self.draw(qh);
     }
+}
 
-    pub fn set_scale(&mut self, scale: i32) {
-        self.surface.set_buffer_scale(scale);
+impl OutputHandler for GuiLayer {
+    fn output_state(&mut self) -> &mut OutputState {
+        &mut self.output_state
     }
-}
 
-impl Drop for Surface {
-    fn drop(&mut self) {
-        self.layer_surface.destroy();
-        self.surface.destroy();
+    fn new_output(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _output: wl_output::WlOutput,
+    ) {
     }
-}
 
-#[derive(Copy, Clone)]
-pub enum Action {
-    Execute,
-    Exit,
-    Complete,
-    NavUp,
-    NavDown,
-    Search,
-    Delete,
-    DeleteWord,
-    Paste,
-}
+    fn update_output(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _output: wl_output::WlOutput,
+    ) {
+    }
 
-pub struct DData {
-    pub query: String,
-    pub action: Option<Action>,
-    pub modifiers: ModifiersState,
-    pub clipboard: Clipboard,
-    keybindings: Keybindings,
+    fn output_destroyed(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _output: wl_output::WlOutput,
+    ) {
+    }
 }
 
-impl DData {
-    pub fn new(display: &Display, keybindings: Keybindings) -> DData {
-        let clipboard = unsafe { Clipboard::new(display.get_display_ptr() as *mut _) };
-        DData {
-            query: "".to_string(),
-            action: None,
-            modifiers: ModifiersState::default(),
-            clipboard,
-            keybindings,
+impl LayerShellHandler for GuiLayer {
+    fn closed(&mut self, _conn: &Connection, _qh: &QueueHandle<Self>, _layer: &LayerSurface) {
+        self.exit = true;
+    }
+
+    fn configure(
+        &mut self,
+        _conn: &Connection,
+        qh: &QueueHandle<Self>,
+        _layer: &LayerSurface,
+        configure: LayerSurfaceConfigure,
+        _serial: u32,
+    ) {
+        if configure.new_size.0 == 0 || configure.new_size.1 == 0 {
+            self.width = 256;
+            self.height = 256;
+        } else {
+            self.width = configure.new_size.0;
+            self.height = configure.new_size.1;
+        }
+
+        // Initiate the first draw.
+        if self.first_configure {
+            self.first_configure = false;
+            self.draw(qh);
         }
     }
 }
 
-pub fn register_inputs(
-    seats: &[Attached<wl_seat::WlSeat>],
-    event_loop: &calloop::EventLoop<DData>,
-) {
-    for seat in seats {
-        if let Some((has_ptr, _name)) = with_seat_data(seat, |seat_data| {
-            (
-                seat_data.has_pointer && !seat_data.defunct,
-                seat_data.name.clone(),
-            )
-        }) {
-            if has_ptr {
-                let pointer = seat.get_pointer();
-                pointer.quick_assign(move |_, event, ddata| process_pointer_event(event, ddata));
-            }
+impl SeatHandler for GuiLayer {
+    fn seat_state(&mut self) -> &mut SeatState {
+        &mut self.seat_state
+    }
+
+    fn new_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
+
+    fn new_capability(
+        &mut self,
+        _conn: &Connection,
+        qh: &QueueHandle<Self>,
+        seat: wl_seat::WlSeat,
+        capability: Capability,
+    ) {
+        if capability == Capability::Keyboard && self.keyboard.is_none() {
+            debug!("Set keyboard capability");
+            let keyboard = self
+                .seat_state
+                .get_keyboard(qh, &seat, None)
+                .expect("Failed to create keyboard");
+            self.keyboard = Some(keyboard);
+        }
+
+        if capability == Capability::Pointer && self.pointer.is_none() {
+            debug!("Set pointer capability");
+            let pointer = self
+                .seat_state
+                .get_pointer(qh, &seat)
+                .expect("Failed to create pointer");
+            self.pointer = Some(pointer);
         }
     }
 
-    for seat in seats {
-        if let Some((has_kbd, name)) = with_seat_data(seat, |seat_data| {
-            (
-                seat_data.has_keyboard && !seat_data.defunct,
-                seat_data.name.clone(),
-            )
-        }) {
-            if has_kbd {
-                if let Err(err) = map_keyboard_repeat(
-                    event_loop.handle(),
-                    seat,
-                    None,
-                    RepeatKind::System,
-                    move |event, _, ddata| process_keyboard_event(event, ddata),
-                ) {
-                    error!("Failed to map keyboard on seat {name} : {err:?}.")
-                }
-            }
+    fn remove_capability(
+        &mut self,
+        _conn: &Connection,
+        _: &QueueHandle<Self>,
+        _: wl_seat::WlSeat,
+        capability: Capability,
+    ) {
+        if capability == Capability::Keyboard && self.keyboard.is_some() {
+            debug!("Unset keyboard capability");
+            self.keyboard.take().unwrap().release();
+        }
+
+        if capability == Capability::Pointer && self.pointer.is_some() {
+            debug!("Unset pointer capability");
+            self.pointer.take().unwrap().release();
         }
     }
+
+    fn remove_seat(&mut self, _: &Connection, _: &QueueHandle<Self>, _: wl_seat::WlSeat) {}
 }
 
-fn process_pointer_event(event: PEvent, mut data: DispatchData) {
-    let DData {
-        query,
-        action,
-        clipboard,
-        ..
-    } = data.get::<DData>().unwrap();
-    if let PEvent::Button { button, state, .. } = event {
-        if button == 274 && state == ButtonState::Pressed {
-            if let Ok(txt) = clipboard.load_primary() {
-                query.push_str(&txt);
-                *action = Some(Action::Search);
-            }
+impl KeyboardHandler for GuiLayer {
+    fn enter(
+        &mut self,
+        _: &Connection,
+        _: &QueueHandle<Self>,
+        _: &wl_keyboard::WlKeyboard,
+        _: &wl_surface::WlSurface,
+        _: u32,
+        _: &[u32],
+        _keysyms: &[u32],
+    ) {
+    }
+
+    fn leave(
+        &mut self,
+        _: &Connection,
+        _: &QueueHandle<Self>,
+        _: &wl_keyboard::WlKeyboard,
+        _: &wl_surface::WlSurface,
+        _: u32,
+    ) {
+        self.next_action = Some(Action::Exit);
+    }
+
+    fn press_key(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _: &wl_keyboard::WlKeyboard,
+        _: u32,
+        event: KeyEvent,
+    ) {
+        debug!("Key press: {event:?}");
+        if let Some(action) = self.keybindings.get(self.modifiers, event.keysym) {
+            self.next_action = Some(action.clone());
+        } else if let Some(input) = event.utf8 {
+            self.next_action = Some(Action::Insert(input));
         }
     }
+
+    fn release_key(
+        &mut self,
+        _: &Connection,
+        _: &QueueHandle<Self>,
+        _: &wl_keyboard::WlKeyboard,
+        _: u32,
+        event: KeyEvent,
+    ) {
+        debug!("Key release: {event:?}");
+    }
+
+    fn update_modifiers(
+        &mut self,
+        _: &Connection,
+        _: &QueueHandle<Self>,
+        _: &wl_keyboard::WlKeyboard,
+        _serial: u32,
+        modifiers: Modifiers,
+    ) {
+        debug!("Update modifiers: {modifiers:?}");
+        self.modifiers = modifiers;
+    }
 }
 
-fn process_keyboard_event(event: KbEvent, mut data: DispatchData) {
-    let DData {
-        query,
-        action,
-        modifiers,
-        clipboard,
-        keybindings,
-        ..
-    } = data.get::<DData>().unwrap();
-    match event {
-        KbEvent::Enter { .. } => {}
-        KbEvent::Leave { .. } => {
-            *action = Some(Action::Exit);
-        }
-        KbEvent::Key {
-            keysym,
-            state,
-            utf8,
-            ..
-        } => {
-            if state == KeyState::Pressed {
-                if let Some(a) = keybindings.get(modifiers, keysym) {
-                    match a {
-                        &Action::Delete => {
-                            query.pop();
-                            *action = Some(Action::Search)
-                        }
-                        &Action::DeleteWord => {
-                            query.pop();
-                            loop {
-                                let removed_char = query.pop();
-                                if removed_char.unwrap_or(' ') == ' ' {
-                                    break;
-                                }
-                            }
-                            *action = Some(Action::Search)
-                        }
-                        &Action::Paste => {
-                            if let (KeyState::Pressed, keysyms::XKB_KEY_v, Ok(txt)) =
-                                (state, keysym, clipboard.load())
-                            {
-                                query.push_str(&txt);
-                                *action = Some(Action::Search);
-                            }
-                        }
-                        a => *action = Some(a.to_owned()),
-                    }
-                } else if let Some(txt) = utf8 {
-                    let t_sanitized = txt
-                        .chars()
-                        .filter(|c| c.is_ascii() && !c.is_ascii_control())
-                        .collect::<String>();
-
-                    query.push_str(&t_sanitized);
-                    *action = Some(Action::Search);
-                }
+impl PointerHandler for GuiLayer {
+    fn pointer_frame(
+        &mut self,
+        _conn: &Connection,
+        _qh: &QueueHandle<Self>,
+        _pointer: &wl_pointer::WlPointer,
+        events: &[PointerEvent],
+    ) {
+        use PointerEventKind::Press;
+        for event in events {
+            // Ignore events for other surfaces
+            if &event.surface != self.layer.wl_surface() {
+                continue;
             }
-        }
-        KbEvent::Modifiers { modifiers: m } => *modifiers = m,
-        KbEvent::Repeat { keysym, utf8, .. } => {
-            if let Some(a) = keybindings.get(modifiers, keysym) {
-                match a {
-                    &Action::Delete => {
-                        query.pop();
-                        *action = Some(Action::Search)
-                    }
-                    &Action::DeleteWord => {
-                        query.pop();
-                        loop {
-                            let removed_char = query.pop();
-                            if removed_char.unwrap_or(' ') == ' ' {
-                                break;
-                            }
-                        }
-                        *action = Some(Action::Search)
+
+            if let Press { button: 274, .. } = event.kind {
+                let result =
+                    get_contents(ClipboardType::Primary, Seat::Unspecified, MimeType::Text);
+                match result {
+                    Ok((mut pipe, _)) => {
+                        let mut contents = vec![];
+                        pipe.read_to_end(&mut contents).unwrap();
+                        let input = String::from_utf8(contents).unwrap();
+                        self.next_action = Some(Action::Insert(input));
                     }
-                    a => *action = Some(a.to_owned()),
+                    Err(Error::NoSeats | Error::ClipboardEmpty | Error::NoMimeType) => {}
+                    Err(e) => error!("{e}"),
                 }
-            } else if let Some(txt) = utf8 {
-                query.push_str(&txt);
-                *action = Some(Action::Search);
             }
         }
     }
 }
+
+impl ShmHandler for GuiLayer {
+    fn shm_state(&mut self) -> &mut Shm {
+        &mut self.shm
+    }
+}
+
+impl GuiLayer {
+    pub fn draw(&mut self, qh: &QueueHandle<Self>) {
+        let width = self.width * self.scale_factor as u32;
+        let height = self.height * self.scale_factor as u32;
+        let stride = width as i32 * 4;
+
+        let (buffer, canvas) = self
+            .pool
+            .create_buffer(
+                width as i32,
+                height as i32,
+                stride,
+                wl_shm::Format::Argb8888,
+            )
+            .expect("create buffer");
+
+        // Draw to the window:
+        let mut image = self.app.draw(width, height, self.scale_factor);
+        image.pixels_mut().for_each(|pixel| {
+            let channels = pixel.channels_mut();
+            *pixel = *Rgba::from_slice(&[channels[2], channels[1], channels[0], channels[3]]);
+        });
+
+        let mut writer = BufWriter::new(&mut *canvas);
+        writer.write_all(image.as_raw()).unwrap();
+        writer.flush().unwrap();
+
+        // Damage the entire window
+        self.layer
+            .wl_surface()
+            .damage_buffer(0, 0, width as i32, height as i32);
+
+        // Request our next frame
+        self.layer
+            .wl_surface()
+            .frame(qh, self.layer.wl_surface().clone());
+
+        // Attach and commit to present.
+        buffer
+            .attach_to(self.layer.wl_surface())
+            .expect("buffer attach");
+        self.layer.commit();
+    }
+}
+
+delegate_compositor!(GuiLayer);
+delegate_output!(GuiLayer);
+delegate_shm!(GuiLayer);
+
+delegate_seat!(GuiLayer);
+delegate_keyboard!(GuiLayer);
+delegate_pointer!(GuiLayer);
+
+delegate_layer!(GuiLayer);
+
+delegate_registry!(GuiLayer);
+
+impl ProvidesRegistryState for GuiLayer {
+    fn registry(&mut self) -> &mut RegistryState {
+        &mut self.registry_state
+    }
+    registry_handlers![OutputState, SeatState];
+}
diff --git a/src/history.rs b/src/history.rs
deleted file mode 100644
index b6d7c6c..0000000
--- a/src/history.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-extern crate xdg;
-
-use log::*;
-use serde::{Deserialize, Serialize};
-use std::path::PathBuf;
-use std::time::SystemTime;
-use xdg::BaseDirectories;
-
-use crate::selection::Element;
-
-#[derive(Debug, Serialize, Deserialize)]
-pub struct HistoryEntry {
-    pub name: String,
-    pub value: String,
-    pub num_used: usize,
-}
-
-#[derive(Debug)]
-pub struct History {
-    entries: Vec<HistoryEntry>,
-    path: PathBuf,
-}
-
-impl History {
-    pub fn as_vec(&self) -> &Vec<HistoryEntry> {
-        &self.entries
-    }
-
-    pub fn load(path: Option<PathBuf>, decrease_interval: u64) -> Result<Self, std::io::Error> {
-        let history_path = if let Some(path) = path {
-            path
-        } else {
-            let xdg_dirs = BaseDirectories::with_prefix("kickoff")?;
-            if let Some(path) = xdg_dirs.find_cache_file("default.csv") {
-                path
-            } else {
-                return Ok(History {
-                    entries: Vec::new(),
-                    path: xdg_dirs.place_cache_file("default.csv")?,
-                });
-            }
-        };
-
-        let mut res = History {
-            entries: Vec::new(),
-            path: history_path.clone(),
-        };
-
-        if history_path.exists() {
-            let last_modified = history_path.metadata()?.modified()?;
-            let interval_diff = if decrease_interval > 0 {
-                SystemTime::now()
-                    .duration_since(SystemTime::UNIX_EPOCH)
-                    .unwrap()
-                    .as_secs()
-                    / (3600 * decrease_interval)
-                    - last_modified
-                        .duration_since(SystemTime::UNIX_EPOCH)
-                        .unwrap()
-                        .as_secs()
-                        / (3600 * decrease_interval)
-            } else {
-                0
-            };
-
-            let mut rdr = csv::Reader::from_path(history_path).unwrap();
-            for result in rdr.deserialize() {
-                let mut record: HistoryEntry = result?;
-                record.num_used = record.num_used.saturating_sub(interval_diff as usize);
-                if record.num_used > 0 {
-                    res.entries.push(record);
-                }
-            }
-        } else {
-            info!("History file does not exists, will be created on saving");
-        }
-
-        Ok(res)
-    }
-
-    pub fn inc(&mut self, element: &Element) {
-        if let Some(entry) = self.entries.iter_mut().find(|x| x.name == element.name) {
-            entry.num_used += 1;
-            entry.value = element.value.to_owned();
-        } else {
-            self.entries.push(HistoryEntry {
-                name: element.name.to_owned(),
-                value: element.value.to_owned(),
-                num_used: 1,
-            })
-        }
-    }
-
-    pub fn save(&self) -> Result<(), std::io::Error> {
-        let mut wtr = csv::Writer::from_path(&self.path)?;
-        for entry in &self.entries {
-            wtr.serialize(entry)?;
-        }
-        wtr.flush()?;
-
-        Ok(())
-    }
-}
diff --git a/src/keybinds.rs b/src/keybinds.rs
index 7a42788..35f649f 100644
--- a/src/keybinds.rs
+++ b/src/keybinds.rs
@@ -1,7 +1,7 @@
 use crate::gui::Action;
 use serde::de::{self, Visitor};
 use serde::{Deserialize, Deserializer};
-use smithay_client_toolkit::seat::keyboard::ModifiersState;
+use smithay_client_toolkit::seat::keyboard::Modifiers as ModifiersState;
 use std::collections::HashMap;
 use std::fmt;
 use std::hash::{Hash, Hasher};
@@ -15,18 +15,18 @@ pub struct Keybindings {
 
 impl From<KeybindingsConfig> for Keybindings {
     fn from(config: KeybindingsConfig) -> Self {
-        let mut res = Keybindings {
+        let mut res = Self {
             inner: HashMap::new(),
         };
 
-        res.add_key_combos(Action::Complete, &config.complete);
-        res.add_key_combos(Action::Execute, &config.execute);
-        res.add_key_combos(Action::Exit, &config.exit);
-        res.add_key_combos(Action::Delete, &config.delete);
-        res.add_key_combos(Action::DeleteWord, &config.delete_word);
-        res.add_key_combos(Action::NavUp, &config.nav_up);
-        res.add_key_combos(Action::NavDown, &config.nav_down);
-        res.add_key_combos(Action::Paste, &config.paste);
+        res.add_key_combos(&Action::Complete, &config.complete);
+        res.add_key_combos(&Action::Execute, &config.execute);
+        res.add_key_combos(&Action::Exit, &config.exit);
+        res.add_key_combos(&Action::Delete, &config.delete);
+        res.add_key_combos(&Action::DeleteWord, &config.delete_word);
+        res.add_key_combos(&Action::NavUp, &config.nav_up);
+        res.add_key_combos(&Action::NavDown, &config.nav_down);
+        res.add_key_combos(&Action::Paste, &config.paste);
 
         res
     }
@@ -37,7 +37,7 @@ pub struct Modifiers(ModifiersState);
 
 impl From<ModifiersState> for Modifiers {
     fn from(modifiers: ModifiersState) -> Self {
-        Modifiers(modifiers)
+        Self(modifiers)
     }
 }
 
@@ -51,7 +51,7 @@ impl Hash for Modifiers {
 }
 
 impl PartialEq for Modifiers {
-    fn eq(&self, other: &Modifiers) -> bool {
+    fn eq(&self, other: &Self) -> bool {
         self.0.ctrl == other.0.ctrl
             && self.0.alt == other.0.alt
             && self.0.shift == other.0.shift
@@ -67,23 +67,23 @@ pub struct KeyCombo {
 }
 
 impl Keybindings {
-    pub fn get(&self, modifiers: &ModifiersState, keysym: u32) -> Option<&Action> {
+    pub fn get(&self, modifiers: ModifiersState, keysym: u32) -> Option<&Action> {
         self.inner.get(&KeyCombo {
-            modifiers: Modifiers(*modifiers),
+            modifiers: Modifiers(modifiers),
             key: keysym,
         })
     }
 
-    fn add_key_combos(&mut self, action: Action, key_combos: &[KeyCombo]) {
-        key_combos.iter().for_each(|entry| {
-            self.inner.insert(entry.to_owned(), action);
-        });
+    fn add_key_combos(&mut self, action: &Action, key_combos: &[KeyCombo]) {
+        for entry in key_combos.iter() {
+            self.inner.insert(entry.clone(), action.clone());
+        }
     }
 }
 
 impl KeyCombo {
-    pub fn new(modifiers: Modifiers, key: u32) -> Self {
-        KeyCombo { modifiers, key }
+    pub const fn new(modifiers: Modifiers, key: u32) -> Self {
+        Self { modifiers, key }
     }
 }
 
@@ -120,13 +120,14 @@ impl<'de> Visitor<'de> for KeyComboVisitor {
                 }
             }
         });
-        if let Some(key) = key {
-            Ok(KeyCombo {
-                modifiers: Modifiers(modifiers),
-                key,
-            })
-        } else {
-            Err(de::Error::custom("No key given or unable to parse"))
-        }
+        key.map_or_else(
+            || Err(de::Error::custom("No key given or unable to parse")),
+            |key| {
+                Ok(KeyCombo {
+                    modifiers: Modifiers(modifiers),
+                    key,
+                })
+            },
+        )
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index e6da591..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-pub mod history;
-pub mod selection;
diff --git a/src/main.rs b/src/main.rs
index 1f697e7..c8fca63 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,46 +1,27 @@
-use crate::config::Config;
-use crate::gui::{Action, DData, RenderEvent};
+#![warn(clippy::nursery)]
+
+use anyhow::Result;
+use app::App;
 use clap::Parser;
-use history::History;
-use image::ImageBuffer;
+use config::{Config, History};
 use log::*;
-use nix::{
-    sys::wait::{waitpid, WaitPidFlag, WaitStatus},
-    unistd::{fork, ForkResult},
-};
-use notify_rust::Notification;
-use smithay_client_toolkit::{
-    default_environment,
-    environment::SimpleGlobal,
-    new_default_environment,
-    reexports::{calloop, protocols::wlr::unstable::layer_shell::v1::client::zwlr_layer_shell_v1},
-    WaylandSource,
+use std::{
+    io::{Read, Write},
+    {fs, io::ErrorKind},
+    {path::PathBuf, process},
 };
-use std::fs;
-use std::io::{ErrorKind, Read, Write};
-use std::{cmp, error::Error, path::PathBuf, process, time::Duration};
-use tokio::task::JoinHandle;
 use xdg::BaseDirectories;
+
+mod app;
 mod color;
 mod config;
 mod font;
 mod gui;
-mod history;
 mod keybinds;
 mod selection;
 
-default_environment!(Env,
-    fields = [
-        layer_shell: SimpleGlobal<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
-    ],
-    singles = [
-        zwlr_layer_shell_v1::ZwlrLayerShellV1 => layer_shell
-    ],
-);
-
 #[derive(Parser, Debug)]
-#[clap(author, version, about)]
-struct Args {
+pub struct Args {
     #[clap(short, long)]
     config: Option<PathBuf>,
 
@@ -70,18 +51,12 @@ struct Args {
 
 #[cfg(target_os = "linux")]
 #[tokio::main]
-async fn main() -> Result<(), Box<dyn Error>> {
+async fn main() -> Result<()> {
     env_logger::init();
-    let args = Args::parse();
 
     match put_pid() {
         Ok(()) => {
-            if let Some(child_handle) = run(args).await? {
-                /* wait for check if comand exec was successful
-                   and history has been written
-                */
-                child_handle.await?;
-            }
+            run().await?;
             del_pid()?;
             Ok(())
         }
@@ -94,14 +69,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
 
 #[cfg(not(target_os = "linux"))]
 #[tokio::main]
-async fn main() -> Result<(), Box<dyn Error>> {
-    if let Some(child_handle) = run().await? {
-        /* wait for check if comand exec was successful
-           and history has been written
-        */
-        child_handle.await?;
-    }
-    Ok(())
+async fn main() -> Result<()> {
+    run().await
 }
 
 #[cfg(target_os = "linux")]
@@ -142,8 +111,9 @@ fn del_pid() -> std::io::Result<()> {
     Ok(())
 }
 
-async fn run(args: Args) -> Result<Option<JoinHandle<()>>, Box<dyn Error>> {
-    let config = match Config::load(args.config) {
+async fn run() -> Result<()> {
+    let args = Args::parse();
+    let config = match Config::load(args.config.clone()) {
         Ok(c) => c,
         Err(e) => {
             error!("{e}");
@@ -151,18 +121,6 @@ async fn run(args: Args) -> Result<Option<JoinHandle<()>>, Box<dyn Error>> {
         }
     };
 
-    let mut apps = selection::ElementListBuilder::new();
-    if args.from_path || (!args.from_stdin && args.from_file.is_empty()) {
-        apps.add_path();
-    }
-    if !args.from_file.is_empty() {
-        apps.add_files(&args.from_file);
-    }
-    if args.from_stdin {
-        apps.add_stdin();
-    }
-    let apps = apps.build();
-
     let history = if (!args.from_stdin && args.from_file.is_empty()) || args.history.is_some() {
         let path = args.history.clone();
         let decrease_interval = config.history.decrease_interval;
@@ -173,19 +131,27 @@ async fn run(args: Args) -> Result<Option<JoinHandle<()>>, Box<dyn Error>> {
         None
     };
 
-    let font = if let Some(font_name) = config.font {
+    let font = if let Some(font_name) = config.font.clone() {
         let mut font_names = config.fonts.clone();
         font_names.insert(0, font_name);
         font::Font::new(font_names, config.font_size)
     } else {
-        font::Font::new(config.fonts, config.font_size)
+        font::Font::new(config.fonts.clone(), config.font_size)
     };
 
-    let (env, display, queue) =
-        new_default_environment!(Env, fields = [layer_shell: SimpleGlobal::new(),])
-            .expect("Initial roundtrip failed!");
-
+    let mut apps = selection::ElementListBuilder::new();
+    if args.from_path || (!args.from_stdin && args.from_file.is_empty()) {
+        apps.add_path();
+    }
+    if !args.from_file.is_empty() {
+        apps.add_files(&args.from_file);
+    }
+    if args.from_stdin {
+        apps.add_stdin();
+    }
+    let apps = apps.build();
     let mut apps = apps.await?;
+
     let history = match history {
         Some(history) => {
             let history = history.await??;
@@ -196,239 +162,7 @@ async fn run(args: Args) -> Result<Option<JoinHandle<()>>, Box<dyn Error>> {
     };
     apps.sort_score();
 
-    let layer_shell = env.require_global::<zwlr_layer_shell_v1::ZwlrLayerShellV1>();
-    let pools = env
-        .create_double_pool(|_| {})
-        .expect("Failed to create a memory pool!");
-    let surface = env.create_surface().detach();
-    let mut surface = gui::Surface::new(None, surface, &layer_shell, pools);
-
-    let mut event_loop = calloop::EventLoop::<DData>::try_new().unwrap();
-    WaylandSource::new(queue)
-        .quick_insert(event_loop.handle())
-        .unwrap();
-
-    gui::register_inputs(&env.get_all_seats(), &event_loop);
-
-    let mut search_results = apps.as_ref_vec();
-    let mut need_redraw = false;
-    let mut data = DData::new(&display, config.keybindings.clone().into());
-    let mut selection = 0;
-    let mut select_query = false;
-    let mut font = font.await?;
-
-    loop {
-        let gui::DData { query, action, .. } = &mut data;
-        match surface.next_render_event.take() {
-            Some(RenderEvent::Closed) => break,
-            Some(RenderEvent::Configure { width, height }) => {
-                need_redraw = true;
-                surface.set_dimensions(width, height);
-            }
-            None => {}
-        }
-        if let Some(action) = action.take() {
-            match action {
-                Action::NavUp => {
-                    need_redraw = true;
-                    if selection > 0 {
-                        selection -= 1;
-                    } else if !query.is_empty() {
-                        select_query = true;
-                    }
-                }
-                Action::NavDown => {
-                    need_redraw = true;
-                    if select_query && !search_results.is_empty() {
-                        select_query = false;
-                    } else if !search_results.is_empty() && selection < search_results.len() - 1 {
-                        selection += 1;
-                    }
-                }
-                Action::Search => {
-                    need_redraw = true;
-                    search_results = apps.search(query);
-                    select_query = false;
-                    selection = 0;
-                    if search_results.is_empty() {
-                        select_query = true
-                    }
-                }
-                Action::Complete => {
-                    if !select_query {
-                        let app = search_results.get(selection).unwrap();
-                        if query == &app.name {
-                            selection = if selection < search_results.len() - 1 {
-                                selection + 1
-                            } else {
-                                selection
-                            };
-                        }
-                        query.clear();
-                        query.push_str(&search_results.get(selection).unwrap().name);
-                        need_redraw = true;
-                    }
-                }
-                Action::Execute => {
-                    let element = if select_query {
-                        selection::Element {
-                            name: query.to_string(),
-                            value: query.to_string(),
-                            base_score: 0,
-                        }
-                    } else {
-                        (*search_results.get(selection).unwrap()).clone()
-                    };
-                    if args.stdout {
-                        print!("{}", element.value);
-                        if let Some(mut history) = history {
-                            history.inc(&element);
-                            history.save()?;
-                        }
-                        return Ok(None);
-                    } else {
-                        return Ok(Some(exec(element, history)?));
-                    }
-                }
-                Action::Exit => break,
-                _ => {}
-            }
-        }
-
-        if need_redraw {
-            need_redraw = false;
-
-            // adjust all components for Hidpi
-            let scale = surface.get_scale();
-            surface.set_scale(scale);
-            font.set_scale(scale);
-            let (width, height) = (
-                surface.dimensions.0 * scale as u32,
-                surface.dimensions.1 * scale as u32,
-            );
-            let padding = config.padding * scale as u32;
-            let font_size = config.font_size * scale as f32;
-
-            let mut img =
-                ImageBuffer::from_pixel(width, height, config.colors.background.to_rgba());
-            let prompt = args.prompt.as_ref().unwrap_or(&config.prompt);
-            let prompt_width = if !prompt.is_empty() {
-                let (width, _) = font.render(
-                    prompt,
-                    &config.colors.prompt,
-                    &mut img,
-                    padding,
-                    padding,
-                    None,
-                );
-                width
-            } else {
-                0
-            };
-
-            if !query.is_empty() {
-                let color = if select_query {
-                    &config.colors.text_selected
-                } else {
-                    &config.colors.text_query
-                };
-                font.render(
-                    query,
-                    color,
-                    &mut img,
-                    padding + prompt_width,
-                    padding,
-                    None,
-                );
-            }
+    gui::run(App::new(args, config, apps, font.await?, history));
 
-            let spacer = (1.5 * font_size) as u32;
-            let max_entries = ((height - 2 * padding - spacer) as f32 / (font_size * 1.2)) as usize;
-            let offset = if selection > (max_entries / 2) {
-                selection - max_entries / 2
-            } else {
-                0
-            };
-
-            for (i, matched) in search_results
-                .iter()
-                .enumerate()
-                .take(cmp::min(max_entries + offset, search_results.len()))
-                .skip(offset)
-            {
-                let color = if i == selection && !select_query {
-                    &config.colors.text_selected
-                } else {
-                    &config.colors.text
-                };
-                font.render(
-                    &matched.name,
-                    color,
-                    &mut img,
-                    padding,
-                    padding + spacer + (i - offset) as u32 * (font_size * 1.2) as u32,
-                    Some((width - (padding * 2)) as usize),
-                );
-            }
-
-            match surface.draw(img, scale) {
-                Ok(_) => {}
-                Err(e) => {
-                    error!("{e}");
-                    need_redraw = false;
-                }
-            };
-        }
-
-        display.flush().unwrap();
-        event_loop.dispatch(None, &mut data).unwrap();
-    }
-    Ok(None)
-}
-
-fn exec(
-    elem: selection::Element,
-    history: Option<History>,
-) -> Result<tokio::task::JoinHandle<()>, Box<dyn Error>> {
-    match unsafe { fork() } {
-        Ok(ForkResult::Parent { child }) => {
-            Ok(tokio::spawn(async move {
-                tokio::time::sleep(Duration::new(1, 0)).await;
-                match waitpid(child, Some(WaitPidFlag::WNOHANG)) {
-                    Ok(WaitStatus::StillAlive) | Ok(WaitStatus::Exited(_, 0)) => {
-                        if let Some(mut history) = history {
-                            history.inc(&elem);
-                            match history.save() {
-                                Ok(()) => {}
-                                Err(e) => {
-                                    error!("{e}");
-                                }
-                            };
-                        }
-                    }
-                    Ok(_) => {
-                        /* Every non 0 statuscode holds no information since it's
-                        origin can be the started application or a file not found error.
-                        In either case the error has already been logged and does not
-                        need to be handled here. */
-                    }
-                    Err(err) => error!("{err}"),
-                }
-            }))
-        }
-        Ok(ForkResult::Child) => {
-            let err = exec::Command::new("sh").args(&["-c", &elem.value]).exec();
-
-            // Won't be executed when exec was successful
-            error!("{err}");
-
-            Notification::new()
-                .summary("Kickoff")
-                .body(&format!("{err}"))
-                .timeout(5000)
-                .show()?;
-            process::exit(2);
-        }
-        Err(e) => Err(Box::new(e)),
-    }
+    Ok(())
 }
diff --git a/src/selection.rs b/src/selection.rs
index db9cd8a..232e1f6 100644
--- a/src/selection.rs
+++ b/src/selection.rs
@@ -1,4 +1,4 @@
-use crate::history::History;
+use crate::config::History;
 use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
 use log::*;
 use std::fs::File;
@@ -21,7 +21,7 @@ pub struct Element {
 }
 
 impl Ord for Element {
-    fn cmp(&self, other: &Element) -> Ordering {
+    fn cmp(&self, other: &Self) -> Ordering {
         match other.base_score.cmp(&self.base_score) {
             Ordering::Equal => self.name.cmp(&other.name),
             e => e,
@@ -30,7 +30,7 @@ impl Ord for Element {
 }
 
 impl PartialOrd for Element {
-    fn partial_cmp(&self, other: &Element) -> Option<Ordering> {
+    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         match other.base_score.cmp(&self.base_score) {
             Ordering::Equal => Some(self.name.cmp(&other.name)),
             e => Some(e),
@@ -50,16 +50,16 @@ impl ElementList {
                 elem.base_score = entry.num_used;
             } else {
                 self.inner.push(Element {
-                    name: entry.name.to_owned(),
-                    value: entry.value.to_owned(),
+                    name: entry.name.clone(),
+                    value: entry.value.clone(),
                     base_score: entry.num_used,
-                })
+                });
             }
         }
     }
 
     pub fn sort_score(&mut self) {
-        self.inner.sort_by(|a, b| b.base_score.cmp(&a.base_score))
+        self.inner.sort_by(|a, b| b.base_score.cmp(&a.base_score));
     }
 
     pub fn search(&self, pattern: &str) -> Vec<&Element> {
@@ -94,8 +94,8 @@ pub struct ElementListBuilder {
 }
 
 impl ElementListBuilder {
-    pub fn new() -> ElementListBuilder {
-        ElementListBuilder::default()
+    pub fn new() -> Self {
+        Self::default()
     }
 
     pub fn add_path(&mut self) {
@@ -108,19 +108,17 @@ impl ElementListBuilder {
         self.from_stdin = true;
     }
 
-    pub async fn build(&self) -> Result<ElementList, Box<dyn std::error::Error>> {
+    pub async fn build(&self) -> Result<ElementList, std::io::Error> {
         let mut fut = Vec::new();
         if self.from_stdin {
-            fut.push(spawn(ElementListBuilder::build_stdin()))
+            fut.push(spawn(Self::build_stdin()));
         }
         if !self.from_file.is_empty() {
             let files = self.from_file.clone();
-            fut.push(spawn_blocking(move || {
-                ElementListBuilder::build_files(&files)
-            }))
+            fut.push(spawn_blocking(move || Self::build_files(&files)));
         }
         if self.from_path {
-            fut.push(spawn_blocking(ElementListBuilder::build_path))
+            fut.push(spawn_blocking(Self::build_path));
         }
 
         let finished = futures::future::join_all(fut).await;
@@ -149,7 +147,7 @@ impl ElementListBuilder {
                 match kv_pair {
                     ("%base_score", Some(value)) => {
                         if let Ok(value) = value.parse::<usize>() {
-                            base_score = value
+                            base_score = value;
                         }
                     }
                     (key, Some(value)) => res.push(Element {
@@ -240,10 +238,7 @@ impl ElementListBuilder {
 #[allow(clippy::type_complexity)]
 fn parse_line(input: &str) -> Option<(&str, Option<&str>)> {
     let input = input.trim();
-    let parts = input
-        .splitn(2, '=')
-        .map(|s| s.trim())
-        .collect::<Vec<&str>>();
+    let parts = input.splitn(2, '=').map(str::trim).collect::<Vec<&str>>();
 
     if parts.is_empty() {
         warn!("Failed to pares line: {input}");
@@ -275,6 +270,6 @@ mod tests {
                     r#"/usr/lib/firefox-developer-edition/firefox --class="firefoxdeveloperedition" --new-window %u"#
                 )
             ))
-        )
+        );
     }
 }


Test patch: