From 990bf12b79b47684e2ce397eb0c7b7674043b2f0 Mon Sep 17 00:00:00 2001 From: Kirottu Date: Thu, 29 Dec 2022 23:56:32 +0200 Subject: [PATCH] Anyrun! --- .gitignore | 1 + Cargo.lock | 1065 ++++++++++++++++++++++++++ Cargo.toml | 9 + anyrun-interface/Cargo.toml | 9 + anyrun-interface/src/lib.rs | 67 ++ anyrun-plugin/Cargo.toml | 10 + anyrun-plugin/src/lib.rs | 101 +++ anyrun/Cargo.toml | 14 + anyrun/res/style.css | 20 + anyrun/src/main.rs | 469 ++++++++++++ plugins/applications/Cargo.toml | 14 + plugins/applications/src/lib.rs | 81 ++ plugins/applications/src/scrubber.rs | 121 +++ plugins/symbols/Cargo.toml | 13 + plugins/symbols/src/lib.rs | 27 + plugins/web-search/Cargo.toml | 13 + plugins/web-search/src/lib.rs | 35 + 17 files changed, 2069 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 anyrun-interface/Cargo.toml create mode 100644 anyrun-interface/src/lib.rs create mode 100644 anyrun-plugin/Cargo.toml create mode 100644 anyrun-plugin/src/lib.rs create mode 100644 anyrun/Cargo.toml create mode 100644 anyrun/res/style.css create mode 100644 anyrun/src/main.rs create mode 100644 plugins/applications/Cargo.toml create mode 100644 plugins/applications/src/lib.rs create mode 100644 plugins/applications/src/scrubber.rs create mode 100644 plugins/symbols/Cargo.toml create mode 100644 plugins/symbols/src/lib.rs create mode 100644 plugins/web-search/Cargo.toml create mode 100644 plugins/web-search/src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..bca10af --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1065 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "abi_stable" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f69d9465d88d24382d43fa68335a92fe9d3c53a918549c693403ed9a85eff50" +dependencies = [ + "abi_stable_derive", + "abi_stable_shared", + "const_panic", + "core_extensions", + "crossbeam-channel", + "generational-arena", + "libloading", + "lock_api", + "parking_lot", + "paste", + "repr_offset", + "rustc_version 0.4.0", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "abi_stable_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aecd3efa5a5294f5c67913d45f985ccb382b3c93327581529610eeecdf4821a" +dependencies = [ + "abi_stable_shared", + "as_derive_utils", + "core_extensions", + "proc-macro2", + "quote", + "rustc_version 0.4.0", + "syn", + "typed-arena", +] + +[[package]] +name = "abi_stable_shared" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b5df7688c123e63f4d4d649cba63f2967ba7f7861b1664fca3f77d3dad2b63" +dependencies = [ + "core_extensions", +] + +[[package]] +name = "anyhow" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" + +[[package]] +name = "anyrun" +version = "0.1.0" +dependencies = [ + "abi_stable", + "anyrun-interface", + "gtk", + "gtk-layer-shell", + "ron", + "serde", +] + +[[package]] +name = "anyrun-interface" +version = "0.1.0" +dependencies = [ + "abi_stable", +] + +[[package]] +name = "anyrun-plugin" +version = "0.1.0" +dependencies = [ + "abi_stable", + "anyrun-interface", +] + +[[package]] +name = "applications" +version = "0.1.0" +dependencies = [ + "abi_stable", + "anyrun-plugin", + "sublime_fuzzy", +] + +[[package]] +name = "as_derive_utils" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff3c96645900a44cf11941c111bd08a6573b0e2f9f69bc9264b179d8fae753c4" +dependencies = [ + "core_extensions", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atk" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39991bc421ddf72f70159011b323ff49b0f783cc676a7287c59453da2e2531cf" +dependencies = [ + "atk-sys", + "bitflags", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ad703eb64dc058024f0e57ccfa069e15a413b98dbd50a1a950e743b7f11148" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cairo-rs" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3125b15ec28b84c238f6f476c6034016a5f6cc0221cb514ca46c532139fc97d" +dependencies = [ + "bitflags", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c48f4af05fabdcfa9658178e1326efa061853f040ce7d72e33af6885196f421" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "cfg-expr" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" +dependencies = [ + "smallvec", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const_panic" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58baae561b85ca19b3122a9ddd35c8ec40c3bcd14fe89921824eae73f7baffbf" + +[[package]] +name = "core_extensions" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92c71dc07c9721607e7a16108336048ee978c3a8b129294534272e8bac96c0ee" +dependencies = [ + "core_extensions_proc_macros", +] + +[[package]] +name = "core_extensions_proc_macros" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f3b219d28b6e3b4ac87bc1fc522e0803ab22e055da177bff0068c4150c61a6" + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "field-offset" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92" +dependencies = [ + "memoffset", + "rustc_version 0.3.3", +] + +[[package]] +name = "futures-channel" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-executor" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" + +[[package]] +name = "futures-macro" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "gdk" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9cb33da481c6c040404a11f8212d193889e9b435db2c14fd86987f630d3ce1" +dependencies = [ + "bitflags", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3578c60dee9d029ad86593ed88cb40f35c1b83360e12498d055022385dd9a05" +dependencies = [ + "bitflags", + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3092cf797a5f1210479ea38070d9ae8a5b8e9f8f1be9f32f4643c529c7d70016" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76354f97a913e55b984759a997b693aa7dc71068c9e98bcce51aa167a0a5c5a" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "generational-arena" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d3b771574f62d0548cee0ad9057857e9fc25d7a3335f140c84f6acd0bf601" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "gio" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a1c84b4534a290a29160ef5c6eff2a9c95833111472e824fc5cb78b513dd092" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror", +] + +[[package]] +name = "gio-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9b693b8e39d042a95547fc258a7b07349b1f0b48f4b2fa3108ba3c51c0b5229" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.16.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd4df61a866ed7259d6189b8bcb1464989a77f1d85d25d002279bbe9dd38b2f" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "once_cell", + "smallvec", + "thiserror", +] + +[[package]] +name = "glib-macros" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e084807350b01348b6d9dbabb724d1a0bb987f47a2c85de200e98e12e30733bf" +dependencies = [ + "anyhow", + "heck", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61a4f46316d06bfa33a7ac22df6f0524c8be58e3db2d9ca99ccb1f357b62a65" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3520bb9c07ae2a12c7f2fbb24d4efc11231c8146a86956413fb1a79bb760a0f1" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4d3507d43908c866c805f74c9dd593c0ce7ba5c38e576e41846639cdcd4bee6" +dependencies = [ + "atk", + "bitflags", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "once_cell", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-layer-shell" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f0ef12cc0d52f9226cbfc83f766a52fc667002ed15ddd1c37a388abe7d03f8c" +dependencies = [ + "bitflags", + "gdk", + "glib", + "glib-sys", + "gtk", + "gtk-layer-shell-sys", + "libc", +] + +[[package]] +name = "gtk-layer-shell-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7095dbaf56028d8a238e8b8f3039a537587cb7a6efbdba2b539375fe5689295d" +dependencies = [ + "gdk-sys", + "glib-sys", + "gtk-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk-sys" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b5f8946685d5fe44497007786600c2f368ff6b1e61a16251c89f72a97520a3" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfd6557b1018b773e43c8de9d0d13581d6b36190d0501916cbec4731db5ccff" +dependencies = [ + "anyhow", + "proc-macro-crate", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "heck" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" + +[[package]] +name = "itoa" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" + +[[package]] +name = "libc" +version = "0.2.139" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi", +] + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "pango" +version = "0.16.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdff66b271861037b89d028656184059e03b0b6ccb36003820be19f7200b1e94" +dependencies = [ + "bitflags", + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e134909a9a293e04d2cc31928aa95679c5e4df954d0b85483159bd20d8f047f" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff9f3fef3968a3ec5945535ed654cb38ff72d7495a25619e2247fb15a2ed9ba" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + +[[package]] +name = "pest" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f6e86fb9e7026527a0d46bc308b841d73170ef8f443e1807f6ef88526a816d4" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "repr_offset" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "663e92f38214b9e51fa305902788f5fb45fb60027b727556618fc53ba1946112" +dependencies = [ + "tstr", +] + +[[package]] +name = "ron" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" +dependencies = [ + "base64", + "bitflags", + "serde", +] + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver 1.0.16", +] + +[[package]] +name = "ryu" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + +[[package]] +name = "serde" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "255abe9a125a985c05190d687b320c12f9b1f0b99445e608c21ba0782c719ad8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "sublime_fuzzy" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7986063f7c0ab374407e586d7048a3d5aac94f103f751088bf398e07cd5400" + +[[package]] +name = "symbols" +version = "0.1.0" +dependencies = [ + "abi_stable", + "anyrun-plugin", +] + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "6.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +dependencies = [ + "serde", +] + +[[package]] +name = "tstr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca3264971090dec0feef3b455a3c178f02762f7550cf4592991ac64b3be2d7e" +dependencies = [ + "tstr_proc_macros", +] + +[[package]] +name = "tstr_proc_macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78122066b0cb818b8afd08f7ed22f7fdbc3e90815035726f0840d0d26c0747a" + +[[package]] +name = "typed-arena" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0685c84d5d54d1c26f7d3eb96cd41550adb97baed141a761cf335d3d33bcd0ae" + +[[package]] +name = "ucd-trie" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "web-search" +version = "0.1.0" +dependencies = [ + "abi_stable", + "anyrun-plugin", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..61603a1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] +members = [ + "anyrun", + "anyrun-plugin", + "anyrun-interface", + "plugins/applications", + "plugins/symbols", + "plugins/web-search", +] \ No newline at end of file diff --git a/anyrun-interface/Cargo.toml b/anyrun-interface/Cargo.toml new file mode 100644 index 0000000..a5d6728 --- /dev/null +++ b/anyrun-interface/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "anyrun-interface" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +abi_stable = "0.11.1" diff --git a/anyrun-interface/src/lib.rs b/anyrun-interface/src/lib.rs new file mode 100644 index 0000000..7fa0707 --- /dev/null +++ b/anyrun-interface/src/lib.rs @@ -0,0 +1,67 @@ +use abi_stable::{ + declare_root_module_statics, + library::RootModule, + package_version_strings, + sabi_types::VersionStrings, + std_types::{ROption, RString, RVec}, + StableAbi, +}; + +#[repr(C)] +#[derive(StableAbi)] +#[sabi(kind(Prefix(prefix_ref = PluginRef)))] +#[sabi(missing_field(panic))] +pub struct Plugin { + pub init: extern "C" fn(RString), + pub info: extern "C" fn() -> PluginInfo, + pub get_matches: extern "C" fn(RString) -> u64, + pub poll_matches: extern "C" fn(u64) -> PollResult, + pub handle_selection: extern "C" fn(Match) -> HandleResult, +} + +/// Info of the plugin. Used for the main UI +#[repr(C)] +#[derive(StableAbi, Debug)] +pub struct PluginInfo { + pub name: RString, + /// The icon name from the icon theme in use + pub icon: RString, +} + +/// Represents a match from a plugin +#[repr(C)] +#[derive(StableAbi, Clone)] +pub struct Match { + pub title: RString, + pub description: ROption, + /// The icon name from the icon theme in use + pub icon: RString, + /// For runners to differentiate between the matches. + pub id: u64, +} + +/// For determining how anyrun should proceed after the plugin has handled a match selection +#[repr(C)] +#[derive(StableAbi)] +pub enum HandleResult { + /// Shut down the program + Close, + /// Refresh the items. Useful if the runner wants to alter results in place. + Refresh, +} + +#[repr(C)] +#[derive(StableAbi)] +pub enum PollResult { + Ready(RVec), + Pending, + Cancelled, +} + +impl RootModule for PluginRef { + declare_root_module_statics! {PluginRef} + + const BASE_NAME: &'static str = "anyrun_plugin"; + const NAME: &'static str = "anyrun_plugin"; + const VERSION_STRINGS: VersionStrings = package_version_strings!(); +} diff --git a/anyrun-plugin/Cargo.toml b/anyrun-plugin/Cargo.toml new file mode 100644 index 0000000..1bfb1b1 --- /dev/null +++ b/anyrun-plugin/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "anyrun-plugin" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +abi_stable = "0.11.1" +anyrun-interface = { path = "../anyrun-interface" } diff --git a/anyrun-plugin/src/lib.rs b/anyrun-plugin/src/lib.rs new file mode 100644 index 0000000..895f966 --- /dev/null +++ b/anyrun-plugin/src/lib.rs @@ -0,0 +1,101 @@ +pub use anyrun_interface::{self, Match, PluginInfo}; + +/** +The macro to create a plugin, handles asynchronous execution of getting the matches and the boilerplate +for integrating with `stable_abi`. + +# Arguments + +* `$init`: Function that takes an `RString` as the only argument, which points to the anyrun config directory. It returns nothing. +The path is used for plugin specific config files. +**NOTE**: Should not block or block for a long time. If this blocks the main thread will too. + +* `$info`: Function that returns the plugin info as a `PluginInfo` object. Takes no arguments. + +* `$get_matches`: Function that takes the current text input as an `RString` as the only argument, and returns an `RVec`. +This is run asynchronously automatically. + +* `$handler`: The function to handle the selection of an item. Takes a `Match` as it's only argument and returns a `HandleResult` with +the appropriate action. +**/ +#[macro_export] +macro_rules! plugin { + ($init:ident, $info:ident, $get_matches:ident, $handler:ident) => { + mod anyrun_plugin_internal { + static THREAD: ::std::sync::Mutex< + Option<( + ::std::thread::JoinHandle< + ::abi_stable::std_types::RVec<::anyrun_plugin::anyrun_interface::Match>, + >, + u64, + )>, + > = ::std::sync::Mutex::new(None); + static ID_COUNTER: ::std::sync::atomic::AtomicU64 = + ::std::sync::atomic::AtomicU64::new(0); + + #[::abi_stable::export_root_module] + fn init_root_module() -> ::anyrun_plugin::anyrun_interface::PluginRef { + use ::abi_stable::prefix_type::PrefixTypeTrait; + ::anyrun_plugin::anyrun_interface::Plugin { + init, + info, + get_matches, + poll_matches, + handle_selection, + } + .leak_into_prefix() + } + + #[::abi_stable::sabi_extern_fn] + fn init(config_dir: ::abi_stable::std_types::RString) { + super::$init(config_dir); + } + + #[::abi_stable::sabi_extern_fn] + fn info() -> ::anyrun_plugin::anyrun_interface::PluginInfo { + super::$info() + } + + #[::abi_stable::sabi_extern_fn] + fn get_matches(input: ::abi_stable::std_types::RString) -> u64 { + let current_id = ID_COUNTER.load(::std::sync::atomic::Ordering::Relaxed); + ID_COUNTER.store(current_id + 1, ::std::sync::atomic::Ordering::Relaxed); + + let handle = ::std::thread::spawn(move || super::$get_matches(input)); + + *THREAD.lock().unwrap() = Some((handle, current_id)); + + current_id + } + + #[::abi_stable::sabi_extern_fn] + fn poll_matches(id: u64) -> ::anyrun_plugin::anyrun_interface::PollResult { + match THREAD.try_lock() { + Ok(thread) => match thread.as_ref() { + Some((thread, task_id)) => { + if *task_id == id { + if !thread.is_finished() { + return ::anyrun_plugin::anyrun_interface::PollResult::Pending; + } + } else { + return ::anyrun_plugin::anyrun_interface::PollResult::Cancelled; + } + } + None => return ::anyrun_plugin::anyrun_interface::PollResult::Cancelled, + }, + Err(_) => return ::anyrun_plugin::anyrun_interface::PollResult::Pending, + } + + let (thread, _) = THREAD.lock().unwrap().take().unwrap(); + ::anyrun_plugin::anyrun_interface::PollResult::Ready(thread.join().unwrap()) + } + + #[::abi_stable::sabi_extern_fn] + fn handle_selection( + selection: ::anyrun_plugin::anyrun_interface::Match, + ) -> ::anyrun_plugin::anyrun_interface::HandleResult { + super::$handler(selection) + } + } + }; +} diff --git a/anyrun/Cargo.toml b/anyrun/Cargo.toml new file mode 100644 index 0000000..10719d5 --- /dev/null +++ b/anyrun/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "anyrun" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +abi_stable = "0.11.1" +gtk = "0.16.2" +gtk-layer-shell = { version = "0.5.0", features = ["v0_6"] } +ron = "0.8.0" +serde = { version = "1.0.151", features = ["derive"] } +anyrun-interface = { path = "../anyrun-interface" } diff --git a/anyrun/res/style.css b/anyrun/res/style.css new file mode 100644 index 0000000..9b35f1f --- /dev/null +++ b/anyrun/res/style.css @@ -0,0 +1,20 @@ +#window { + border-radius: 10px; +} + +list#main { + background-color: rgba(0, 0, 0, 0); + border-radius: 10px; +} + +list#plugin { + background-color: rgba(0, 0, 0, 0); +} + +label#match-desc { + font-size: 10px; +} + +label#plugin { + font-size: 14px; +} \ No newline at end of file diff --git a/anyrun/src/main.rs b/anyrun/src/main.rs new file mode 100644 index 0000000..f23df66 --- /dev/null +++ b/anyrun/src/main.rs @@ -0,0 +1,469 @@ +use std::{ + cell::RefCell, + env, fs, + path::{Path, PathBuf}, + rc::Rc, + time::Duration, +}; + +use abi_stable::std_types::{ROption, RVec}; +use gtk::{gdk, glib, prelude::*}; +use serde::Deserialize; +use anyrun_interface::{HandleResult, Match, PluginInfo, PluginRef, PollResult}; + +#[derive(Deserialize)] +struct Config { + width: u32, + plugins: Vec, +} + +#[derive(Clone)] +struct PluginView { + plugin: PluginRef, + row: gtk::ListBoxRow, + list: gtk::ListBox, +} + +struct Args { + override_plugins: Option>, + config_dir: Option, +} + +mod style_names { + pub const ENTRY: &str = "entry"; + pub const MAIN: &str = "main"; + pub const WINDOW: &str = "window"; + pub const PLUGIN: &str = "plugin"; + pub const MATCH: &str = "match"; + + pub const MATCH_TITLE: &str = "match-title"; + pub const MATCH_DESC: &str = "match-desc"; + pub const TITLE_DESC_BOX: &str = "title-desc-box"; +} + +fn main() { + let app = gtk::Application::new(Some("com.kirottu.anyrun"), Default::default()); + let args: Rc>> = Rc::new(RefCell::new(None)); + + app.add_main_option( + "override-plugins", + glib::Char('o' as i8), + glib::OptionFlags::IN_MAIN, + glib::OptionArg::StringArray, + "Override plugins. Provide paths in same format as in the config file", + None, + ); + app.add_main_option( + "config-dir", + glib::Char('c' as i8), + glib::OptionFlags::IN_MAIN, + glib::OptionArg::String, + "Override the config directory from the default (~/.config/anyrun/)", + None, + ); + + let args_clone = args.clone(); + app.connect_handle_local_options(move |_app, dict| { + let override_plugins = dict.lookup::>("override-plugins").unwrap(); + let config_dir = dict.lookup::("config-dir").unwrap(); + + *args_clone.borrow_mut() = Some(Args { + override_plugins, + config_dir, + }); + -1 // Magic GTK number to continue running + }); + + let args_clone = args.clone(); + app.connect_activate(move |app| activate(app, args_clone.clone())); + + app.run(); +} + +fn activate(app: >k::Application, args: Rc>>) { + // Figure out the config dir + let config_dir = args + .borrow() + .as_ref() + .unwrap() + .config_dir + .clone() + .unwrap_or(format!( + "{}/.config/anyrun", + env::var("HOME").expect("Could not determine home directory! Is $HOME set?") + )); + + // Load config + let config: Config = ron::from_str( + &fs::read_to_string(format!("{}/config.ron", config_dir)) + .expect("Unable to read config file!"), + ) + .expect("Config file malformed!"); + + // Create the main window + let window = gtk::ApplicationWindow::builder() + .application(app) + .name(style_names::WINDOW) + .width_request(config.width as i32) + .build(); + + // Init GTK layer shell + gtk_layer_shell::init_for_window(&window); + gtk_layer_shell::set_anchor(&window, gtk_layer_shell::Edge::Top, true); + gtk_layer_shell::set_keyboard_mode(&window, gtk_layer_shell::KeyboardMode::Exclusive); + + // Try to load custom CSS, if it fails load the default CSS + let provider = gtk::CssProvider::new(); + if let Err(why) = provider.load_from_path(&format!("{}/style.css", config_dir)) { + println!("Failed to load custom CSS: {}", why); + provider + .load_from_data(include_bytes!("../res/style.css")) + .unwrap(); + } + gtk::StyleContext::add_provider_for_screen( + &gdk::Screen::default().expect("Failed to get GDK screen for CSS provider!"), + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, + ); + + // Use the plugins in the config file, or the plugins specified with the override + let plugins = match &args.borrow().as_ref().unwrap().override_plugins { + Some(plugins) => plugins.iter().map(|path| PathBuf::from(path)).collect(), + None => config.plugins, + }; + + // Make sure at least one plugin is specified + if plugins.len() == 0 { + println!("At least one plugin needs to be enabled!"); + app.quit(); + } + + // Create the main list of plugin views + let main_list = gtk::ListBox::builder() + .selection_mode(gtk::SelectionMode::None) + .name(style_names::MAIN) + .build(); + + // Load plugins from the paths specified in the config file + let plugins = Rc::new( + plugins + .iter() + .map(|plugin_path| { + // Load the plugin's dynamic library. + let plugin = abi_stable::library::lib_header_from_path( + if plugin_path.is_absolute() { + plugin_path.clone() + } else { + let mut path = PathBuf::from(&format!("{}/plugins", config_dir)); + path.extend(plugin_path.iter()); + path + } + .as_path(), + ) + .and_then(|plugin| plugin.init_root_module::()) + .unwrap(); + + // Run the plugin's init code to init static resources etc. + plugin.init()(config_dir.clone().into()); + + let plugin_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .spacing(10) + .name(style_names::PLUGIN) + .build(); + plugin_box.add(&create_info_box(&plugin.info()())); + plugin_box.add( + >k::Separator::builder() + .orientation(gtk::Orientation::Horizontal) + .name(style_names::PLUGIN) + .build(), + ); + let list = gtk::ListBox::builder() + .name(style_names::PLUGIN) + .hexpand(true) + .build(); + + plugin_box.add(&list); + + let row = gtk::ListBoxRow::builder().name(style_names::PLUGIN).build(); + row.add(&plugin_box); + + main_list.add(&row); + + PluginView { plugin, row, list } + }) + .collect::>(), + ); + + // Connect selection events to avoid completely messing up selection logic + for plugin_view in plugins.iter() { + let plugins_clone = plugins.clone(); + plugin_view.list.connect_row_selected(move |list, row| { + if let Some(_) = row { + let combined_matches = plugins_clone + .iter() + .map(|view| { + view.list.children().into_iter().map(|child| { + ( + child.dynamic_cast::().unwrap(), + view.list.clone(), + ) + }) + }) + .flatten() + .collect::>(); + + for (_, _list) in combined_matches { + if _list != *list { + _list.select_row(None::<>k::ListBoxRow>); + } + } + } + }); + } + + // Text entry box + let entry = gtk::Entry::builder() + .hexpand(true) + .has_focus(true) + .name(style_names::ENTRY) + .build(); + + // Refresh the matches when text input changes + let plugins_clone = plugins.clone(); + entry.connect_changed(move |entry| { + refresh_matches(entry.text().to_string(), plugins_clone.clone()) + }); + + // Handle other key presses for selection control and all other things that may be needed + let entry_clone = entry.clone(); + let plugins_clone = plugins.clone(); + window.connect_key_press_event(move |window, event| { + use gdk::keys::constants; + match event.keyval() { + constants::Escape => { + window.close(); + Inhibit(true) + } + constants::Down | constants::Tab | constants::Up => { + let combined_matches = plugins_clone + .iter() + .map(|view| { + view.list.children().into_iter().map(|child| { + ( + child.dynamic_cast::().unwrap(), + view.list.clone(), + ) + }) + }) + .flatten() + .collect::>(); + + let (selected_match, selected_list) = match plugins_clone + .iter() + .find_map(|view| view.list.selected_row().map(|row| (row, view.list.clone()))) + { + Some(selected) => selected, + None => { + if event.keyval() != constants::Up { + combined_matches[0] + .1 + .select_row(Some(&combined_matches[0].0)); + } + return Inhibit(true); + } + }; + + selected_list.select_row(None::<>k::ListBoxRow>); + + let index = combined_matches + .iter() + .position(|(row, _)| *row == selected_match) + .unwrap(); + + match event.keyval() { + constants::Down | constants::Tab => { + if index < combined_matches.len() - 1 { + combined_matches[index + 1] + .1 + .select_row(Some(&combined_matches[index + 1].0)); + } else { + combined_matches[0] + .1 + .select_row(Some(&combined_matches[0].0)); + } + } + constants::Up => { + if index > 0 { + combined_matches[index - 1] + .1 + .select_row(Some(&combined_matches[index - 1].0)); + } else { + combined_matches[combined_matches.len() - 1] + .1 + .select_row(Some(&combined_matches[combined_matches.len() - 1].0)); + } + } + _ => unreachable!(), + } + + Inhibit(true) + } + constants::Return => { + let (selected_match, plugin) = match plugins_clone.iter().find_map(|view| { + view.list + .selected_row() + .map(|row| (row, view.plugin.clone())) + }) { + Some(selected) => selected, + None => { + return Inhibit(false); + } + }; + + match plugin.handle_selection()(unsafe { + (*selected_match.data::("match").unwrap().as_ptr()).clone() + }) { + HandleResult::Close => { + window.close(); + Inhibit(true) + } + HandleResult::Refresh => { + refresh_matches(entry_clone.text().to_string(), plugins_clone.clone()); + Inhibit(false) + } + } + } + _ => Inhibit(false), + } + }); + + let main_vbox = gtk::Box::new(gtk::Orientation::Vertical, 10); + main_vbox.add(&entry); + window.add(&main_vbox); + window.show_all(); + // Add and show the list later, to avoid showing empty plugin categories on launch + main_vbox.add(&main_list); + main_list.show(); +} + +fn handle_matches(matches: RVec, plugin_view: PluginView) { + for widget in plugin_view.list.children() { + plugin_view.list.remove(&widget); + } + + if matches.len() == 0 { + plugin_view.row.hide(); + return; + } + + for _match in matches { + let hbox = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .spacing(10) + .name(style_names::MATCH) + .hexpand(true) + .build(); + hbox.add( + >k::Image::builder() + .icon_name(&_match.icon) + .name(style_names::MATCH) + .pixel_size(32) + .build(), + ); + let title = gtk::Label::builder() + .name(style_names::MATCH_TITLE) + .halign(gtk::Align::Start) + .valign(gtk::Align::Center) + .label(&_match.title) + .build(); + + // If a description is present, make a box with it and the title + match &_match.description { + ROption::RSome(desc) => { + let title_desc_box = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .name(style_names::TITLE_DESC_BOX) + .hexpand(true) + .vexpand(true) + .build(); + title_desc_box.add(&title); + title_desc_box.add( + >k::Label::builder() + .name(style_names::MATCH_DESC) + .halign(gtk::Align::Start) + .valign(gtk::Align::Center) + .label(desc) + .build(), + ); + hbox.add(&title_desc_box); + } + ROption::RNone => { + hbox.add(&title); + } + } + let row = gtk::ListBoxRow::builder().name(style_names::MATCH).build(); + row.add(&hbox); + // GTK data setting is not type checked, so it is unsafe. + // Only `Match` objects are stored though. + unsafe { + row.set_data("match", _match); + } + plugin_view.list.add(&row); + } + + // Refresh the items in the view + plugin_view.row.show_all(); +} + +fn create_info_box(info: &PluginInfo) -> gtk::Box { + let info_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .name(style_names::PLUGIN) + .width_request(200) + .expand(false) + .spacing(10) + .build(); + info_box.add( + >k::Image::builder() + .icon_name(&info.icon) + .name(style_names::PLUGIN) + .pixel_size(48) + .halign(gtk::Align::Start) + .valign(gtk::Align::Start) + .build(), + ); + info_box.add( + >k::Label::builder() + .label(&info.name) + .name(style_names::PLUGIN) + .halign(gtk::Align::End) + .valign(gtk::Align::Start) + .hexpand(true) + .build(), + ); + info_box +} + +/// Refresh the matches from the plugins +fn refresh_matches(input: String, plugins: Rc>) { + for plugin_view in plugins.iter() { + let id = plugin_view.plugin.get_matches()(input.clone().into()); + let plugin_view = plugin_view.clone(); + glib::timeout_add_local(Duration::from_micros(1000), move || { + async_match(plugin_view.clone(), id) + }); + } +} + +/// Handle the asynchronously running match task +fn async_match(plugin_view: PluginView, id: u64) -> glib::Continue { + match plugin_view.plugin.poll_matches()(id) { + PollResult::Ready(matches) => { + handle_matches(matches, plugin_view); + glib::Continue(false) + } + PollResult::Pending => glib::Continue(true), + PollResult::Cancelled => glib::Continue(false), + } +} diff --git a/plugins/applications/Cargo.toml b/plugins/applications/Cargo.toml new file mode 100644 index 0000000..b0a02e4 --- /dev/null +++ b/plugins/applications/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "applications" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyrun-plugin = { path = "../../anyrun-plugin" } +abi_stable = "0.11.1" +sublime_fuzzy = "0.7.0" diff --git a/plugins/applications/src/lib.rs b/plugins/applications/src/lib.rs new file mode 100644 index 0000000..2272476 --- /dev/null +++ b/plugins/applications/src/lib.rs @@ -0,0 +1,81 @@ +use abi_stable::std_types::{ROption, RString, RVec}; +use scrubber::DesktopEntry; +use std::{process::Command, sync::RwLock, thread}; +use anyrun_plugin::{anyrun_interface::HandleResult, *}; + +mod scrubber; + +static ENTRIES: RwLock> = RwLock::new(Vec::new()); + +pub fn handler(selection: Match) -> HandleResult { + let entries = ENTRIES.read().unwrap(); + let entry = entries + .iter() + .find_map(|(entry, id)| { + if *id == selection.id { + Some(entry) + } else { + None + } + }) + .unwrap(); + + if let Err(why) = Command::new("sh").arg("-c").arg(&entry.exec).spawn() { + println!("Error running desktop entry: {}", why); + } + + HandleResult::Close +} + +pub fn init(_config_dir: RString) { + thread::spawn(|| { + *ENTRIES.write().unwrap() = match scrubber::scrubber() { + Ok(results) => results, + Err(why) => { + println!("Error reading desktop entries: {}", why); + return; + } + }; + }); +} + +pub fn get_matches(input: RString) -> RVec { + if input.len() == 0 { + return RVec::new(); + } + + let mut entries = ENTRIES + .read() + .unwrap() + .clone() + .into_iter() + .filter_map(|(entry, id)| { + match sublime_fuzzy::best_match(&input.to_lowercase(), &entry.name.to_lowercase()) { + Some(val) => Some((entry, id, val.score())), + None => None, + } + }) + .collect::>(); + + entries.sort_by(|a, b| b.1.cmp(&a.1)); + + entries.truncate(5); + entries + .into_iter() + .map(|(entry, id, _)| Match { + title: entry.name.into(), + icon: entry.icon.into(), + description: ROption::RNone, + id, + }) + .collect() +} + +pub fn info() -> PluginInfo { + PluginInfo { + name: "Applications".into(), + icon: "application-x-executable".into(), + } +} + +plugin!(init, info, get_matches, handler); diff --git a/plugins/applications/src/scrubber.rs b/plugins/applications/src/scrubber.rs new file mode 100644 index 0000000..218341f --- /dev/null +++ b/plugins/applications/src/scrubber.rs @@ -0,0 +1,121 @@ +use std::{collections::HashMap, env, ffi::OsStr, fs, io}; + +#[derive(Clone, Debug)] +pub struct DesktopEntry { + pub exec: String, + pub name: String, + pub icon: String, +} + +const FIELD_CODE_LIST: &[&str] = &[ + "%f", "%F", "%u", "%U", "%d", "%D", "%n", "%N", "%i", "%c", "%k", "%v", "%m", +]; + +impl DesktopEntry { + fn from_dir_entry(entry: &fs::DirEntry) -> Option { + if entry.path().extension() == Some(OsStr::new("desktop")) { + let content = match fs::read_to_string(entry.path()) { + Ok(content) => content, + Err(_) => return None, + }; + + let mut map = HashMap::new(); + for line in content.lines() { + if line.starts_with("[") && line != "[Desktop Entry]" { + break; + } + let (key, val) = match line.split_once("=") { + Some(keyval) => keyval, + None => continue, + }; + map.insert(key, val); + } + + if map.get("Type")? == &"Application" + && match map.get("NoDisplay") { + Some(no_display) => !no_display.parse::().unwrap_or(true), + None => true, + } + { + Some(DesktopEntry { + exec: { + let mut exec = map.get("Exec")?.to_string(); + for field_code in FIELD_CODE_LIST { + exec = exec.replace(field_code, ""); + } + exec + }, + name: map.get("Name")?.to_string(), + icon: map + .get("Icon") + .unwrap_or(&"application-x-executable") + .to_string(), + }) + } else { + None + } + } else { + None + } + } +} + +pub fn scrubber() -> Result, Box> { + // Create iterator over all the files in the XDG_DATA_DIRS + // XDG compliancy is cool + let mut paths: Vec> = match env::var("XDG_DATA_DIRS") { + Ok(data_dirs) => { + // The vec for all the DirEntry objects + let mut paths = Vec::new(); + // Parse the XDG_DATA_DIRS variable and list files of all the paths + for dir in data_dirs.split(":") { + match fs::read_dir(format!("{}/applications/", dir)) { + Ok(dir) => { + paths.extend(dir); + } + Err(why) => { + eprintln!("Error reading directory {}: {}", dir, why); + } + } + } + // Make sure the list of paths isn't empty + if paths.is_empty() { + return Err("No valid desktop file dirs found!".into()); + } + + // Return it + paths + } + Err(_) => fs::read_dir("/usr/share/applications")?.collect(), + }; + + // Go through user directory desktop files for overrides + let user_path = match env::var("XDG_DATA_HOME") { + Ok(data_home) => { + format!("{}/applications/", data_home) + } + Err(_) => { + format!( + "{}/.local/share/applications/", + env::var("HOME").expect("Unable to determine home directory!") + ) + } + }; + + paths.extend(fs::read_dir(&user_path)?); + + // Keeping track of the entries + let mut id = 0; + + Ok(paths + .iter() + .filter_map(|entry| { + id += 1; + let entry = match entry { + Ok(entry) => entry, + Err(_why) => return None, + }; + DesktopEntry::from_dir_entry(&entry).map(|val| (val, id)) + }) + .collect()) +} diff --git a/plugins/symbols/Cargo.toml b/plugins/symbols/Cargo.toml new file mode 100644 index 0000000..ca14af2 --- /dev/null +++ b/plugins/symbols/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "symbols" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyrun-plugin = { path = "../../anyrun-plugin" } +abi_stable = "0.11.1" diff --git a/plugins/symbols/src/lib.rs b/plugins/symbols/src/lib.rs new file mode 100644 index 0000000..30f81c4 --- /dev/null +++ b/plugins/symbols/src/lib.rs @@ -0,0 +1,27 @@ +use abi_stable::std_types::{ROption, RString, RVec}; +use anyrun_plugin::{plugin, anyrun_interface::HandleResult, Match, PluginInfo}; + +pub fn init(_config_dir: RString) {} + +pub fn info() -> PluginInfo { + PluginInfo { + name: "Symbols".into(), + icon: "emblem-mail".into(), + } +} + +pub fn get_matches(input: RString) -> RVec { + vec![Match { + title: "Test".into(), + description: ROption::RNone, + icon: "dialog-warning".into(), + id: 0, + }] + .into() +} + +pub fn handler(selection: Match) -> HandleResult { + HandleResult::Close +} + +plugin!(init, info, get_matches, handler); diff --git a/plugins/web-search/Cargo.toml b/plugins/web-search/Cargo.toml new file mode 100644 index 0000000..2ce323e --- /dev/null +++ b/plugins/web-search/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "web-search" +version = "0.1.0" +edition = "2021" + +[lib] +crate-type = ["cdylib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyrun-plugin = { path = "../../anyrun-plugin" } +abi_stable = "0.11.1" diff --git a/plugins/web-search/src/lib.rs b/plugins/web-search/src/lib.rs new file mode 100644 index 0000000..8903f57 --- /dev/null +++ b/plugins/web-search/src/lib.rs @@ -0,0 +1,35 @@ +use abi_stable::std_types::{ROption, RString, RVec}; +use anyrun_plugin::{plugin, anyrun_interface::HandleResult, Match, PluginInfo}; + +pub fn init(_config_dir: RString) {} + +pub fn info() -> PluginInfo { + PluginInfo { + name: "Web search".into(), + icon: "system-search".into(), + } +} + +pub fn get_matches(input: RString) -> RVec { + vec![ + Match { + title: "DDG it!".into(), + description: ROption::RSome(format!(r#"Look up "{}" with DuckDuckGo"#, input).into()), + icon: "emblem-web".into(), + id: 0, + }, + Match { + title: "Startpage it!".into(), + description: ROption::RSome(format!(r#"Look up "{}" with Startpage"#, input).into()), + icon: "emblem-web".into(), + id: 0, + }, + ] + .into() +} + +pub fn handler(selection: Match) -> HandleResult { + HandleResult::Close +} + +plugin!(init, info, get_matches, handler);