diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..ddcbfc9
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,824 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "block"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
+
+[[package]]
+name = "cairo-rs"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62be3562254e90c1c6050a72aa638f6315593e98c5cdaba9017cedbabf0a5dee"
+dependencies = [
+ "bitflags",
+ "cairo-sys-rs",
+ "glib",
+ "libc",
+ "thiserror",
+]
+
+[[package]]
+name = "cairo-sys-rs"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c55d429bef56ac9172d25fecb85dc8068307d17acd74b377866b7a1ef25d3c8"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "cc"
+version = "1.0.73"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
+
+[[package]]
+name = "cfg-expr"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e068cb2806bbc15b439846dc16c5f89f8599f2c3e4d73d4449d38f9b2f0b6c5"
+dependencies = [
+ "smallvec",
+]
+
+[[package]]
+name = "field-offset"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92"
+dependencies = [
+ "memoffset",
+ "rustc_version",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010"
+dependencies = [
+ "futures-core",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b"
+
+[[package]]
+name = "futures-task"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a"
+
+[[package]]
+name = "futures-util"
+version = "0.3.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gdk-pixbuf"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a"
+dependencies = [
+ "bitflags",
+ "gdk-pixbuf-sys",
+ "gio",
+ "glib",
+ "libc",
+]
+
+[[package]]
+name = "gdk-pixbuf-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "140b2f5378256527150350a8346dbdb08fadc13453a7a2d73aecd5fab3c402a7"
+dependencies = [
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gdk4"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4a2fc0bd03d59383fc10b71a8cb731a1fac2998732a36a0c03e9b1de1513218"
+dependencies = [
+ "bitflags",
+ "cairo-rs",
+ "gdk-pixbuf",
+ "gdk4-sys",
+ "gio",
+ "glib",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gdk4-sys"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48a39e34abe35ee2cf54a1e29dd983accecd113ad30bdead5050418fa92f2a1b"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "pango-sys",
+ "pkg-config",
+ "system-deps",
+]
+
+[[package]]
+name = "gettext-rs"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e49ea8a8fad198aaa1f9655a2524b64b70eb06b2f3ff37da407566c93054f364"
+dependencies = [
+ "gettext-sys",
+ "locale_config",
+]
+
+[[package]]
+name = "gettext-sys"
+version = "0.21.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c63ce2e00f56a206778276704bbe38564c8695249fdc8f354b4ef71c57c3839d"
+dependencies = [
+ "cc",
+ "temp-dir",
+]
+
+[[package]]
+name = "gio"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f132be35e05d9662b9fa0fee3f349c6621f7782e0105917f4cc73c1bf47eceb"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "gio-sys",
+ "glib",
+ "libc",
+ "once_cell",
+ "thiserror",
+]
+
+[[package]]
+name = "gio-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32157a475271e2c4a023382e9cab31c4584ee30a97da41d3c4e9fdd605abcf8d"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+ "winapi",
+]
+
+[[package]]
+name = "glib"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd124026a2fa8c33a3d17a3fe59c103f2d9fa5bd92c19e029e037736729abeab"
+dependencies = [
+ "bitflags",
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-task",
+ "glib-macros",
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "once_cell",
+ "smallvec",
+ "thiserror",
+]
+
+[[package]]
+name = "glib-macros"
+version = "0.15.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "25a68131a662b04931e71891fb14aaf65ee4b44d08e8abc10f49e77418c86c64"
+dependencies = [
+ "anyhow",
+ "heck",
+ "proc-macro-crate",
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "glib-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef4b192f8e65e9cf76cbf4ea71fa8e3be4a0e18ffe3d68b8da6836974cc5bad4"
+dependencies = [
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "gobject-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d57ce44246becd17153bd035ab4d32cfee096a657fc01f2231c9278378d1e0a"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "graphene-rs"
+version = "0.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c54f9fbbeefdb62c99f892dfca35f83991e2cb5b46a8dc2a715e58612f85570"
+dependencies = [
+ "glib",
+ "graphene-sys",
+ "libc",
+]
+
+[[package]]
+name = "graphene-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa691fc7337ba1df599afb55c3bcb85c04f1b3f17362570e9bb0ff0d1bc3028a"
+dependencies = [
+ "glib-sys",
+ "libc",
+ "pkg-config",
+ "system-deps",
+]
+
+[[package]]
+name = "gsk4"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14d5a47a78c682bb67496b562495ed84972c0512ba0654888c4dc92b80a85bd3"
+dependencies = [
+ "bitflags",
+ "cairo-rs",
+ "gdk4",
+ "glib",
+ "graphene-rs",
+ "gsk4-sys",
+ "libc",
+ "pango",
+]
+
+[[package]]
+name = "gsk4-sys"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31d21d7ce02ba261bb24c50c4ab238a10b41a2c97c32afffae29471b7cca69b"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk4-sys",
+ "glib-sys",
+ "gobject-sys",
+ "graphene-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "gtk4"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb5d40303dabe4608fc260de2bd7563da6f85bc90af956323f0cd8ae0abcfe03"
+dependencies = [
+ "bitflags",
+ "cairo-rs",
+ "field-offset",
+ "futures-channel",
+ "gdk-pixbuf",
+ "gdk4",
+ "gio",
+ "glib",
+ "graphene-rs",
+ "gsk4",
+ "gtk4-macros",
+ "gtk4-sys",
+ "libc",
+ "once_cell",
+ "pango",
+]
+
+[[package]]
+name = "gtk4-macros"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f3c4aa605fb3d78205c7aef0eeaa6db61d8cc4dd05a465dc6ffdfdaee84f825"
+dependencies = [
+ "anyhow",
+ "proc-macro-crate",
+ "proc-macro-error",
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "gtk4-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c47c075e8f795c38f6e9a47b51a73eab77b325f83c0154979ed4d4245c36490d"
+dependencies = [
+ "cairo-sys-rs",
+ "gdk-pixbuf-sys",
+ "gdk4-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "graphene-sys",
+ "gsk4-sys",
+ "libc",
+ "pango-sys",
+ "system-deps",
+]
+
+[[package]]
+name = "heck"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
+
+[[package]]
+name = "hermes"
+version = "0.1.0"
+dependencies = [
+ "gettext-rs",
+ "gtk4",
+ "libadwaita",
+]
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+
+[[package]]
+name = "libadwaita"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ae453d28e3b91f03749f02b1531e8cfe315a1d0762b77a61797d2ab80bb87d"
+dependencies = [
+ "gdk-pixbuf",
+ "gdk4",
+ "gio",
+ "glib",
+ "gtk4",
+ "libadwaita-sys",
+ "libc",
+ "once_cell",
+ "pango",
+]
+
+[[package]]
+name = "libadwaita-sys"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f18b6ac4cadd252a89f5cba0a5a4e99836131795d6fad37b859ac79e8cb7d2c8"
+dependencies = [
+ "gdk4-sys",
+ "gio-sys",
+ "glib-sys",
+ "gobject-sys",
+ "gtk4-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.125"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
+
+[[package]]
+name = "locale_config"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "08d2c35b16f4483f6c26f0e4e9550717a2f6575bcd6f12a53ff0c490a94a6934"
+dependencies = [
+ "lazy_static",
+ "objc",
+ "objc-foundation",
+ "regex",
+ "winapi",
+]
+
+[[package]]
+name = "malloc_buf"
+version = "0.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "memoffset"
+version = "0.6.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "objc"
+version = "0.2.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1"
+dependencies = [
+ "malloc_buf",
+]
+
+[[package]]
+name = "objc-foundation"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9"
+dependencies = [
+ "block",
+ "objc",
+ "objc_id",
+]
+
+[[package]]
+name = "objc_id"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b"
+dependencies = [
+ "objc",
+]
+
+[[package]]
+name = "once_cell"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
+
+[[package]]
+name = "pango"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f"
+dependencies = [
+ "bitflags",
+ "glib",
+ "libc",
+ "once_cell",
+ "pango-sys",
+]
+
+[[package]]
+name = "pango-sys"
+version = "0.15.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2a00081cde4661982ed91d80ef437c20eacaf6aa1a5962c0279ae194662c3aa"
+dependencies = [
+ "glib-sys",
+ "gobject-sys",
+ "libc",
+ "system-deps",
+]
+
+[[package]]
+name = "pest"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+dependencies = [
+ "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.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
+
+[[package]]
+name = "proc-macro-crate"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e17d47ce914bf4de440332250b0edd23ce48c005f59fab39d3335866b114f11a"
+dependencies = [
+ "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.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9027b48e9d4c9175fa2218adf3557f91c1137021739951d4932f5f8268ac48aa"
+dependencies = [
+ "unicode-xid",
+]
+
+[[package]]
+name = "quick-xml"
+version = "0.22.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex"
+version = "1.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a11647b6b25ff05a515cb92c365cec08801e83423a235b51e231e1808747286"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+
+[[package]]
+name = "rustc_version"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
+dependencies = [
+ "semver",
+]
+
+[[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-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.137"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
+
+[[package]]
+name = "slab"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
+
+[[package]]
+name = "smallvec"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
+
+[[package]]
+name = "syn"
+version = "1.0.94"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a07e33e919ebcd69113d5be0e4d70c5707004ff45188910106854f38b960df4a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-xid",
+]
+
+[[package]]
+name = "system-deps"
+version = "6.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709"
+dependencies = [
+ "cfg-expr",
+ "heck",
+ "pkg-config",
+ "toml",
+ "version-compare",
+]
+
+[[package]]
+name = "temp-dir"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af547b166dd1ea4b472165569fc456cfb6818116f854690b0ff205e636523dab"
+
+[[package]]
+name = "thiserror"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04"
+
+[[package]]
+name = "version-compare"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73"
+
+[[package]]
+name = "version_check"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
+
+[[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"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..3c28335
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "hermes"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+gettext-rs = { version = "0.7", features = ["gettext-system"] }
+gtk = { version = "0.4.7", package = "gtk4" }
+
+[dependencies.adw]
+package = "libadwaita"
+version = "0.1.1"
diff --git a/build-aux/cargo.sh b/build-aux/cargo.sh
new file mode 100644
index 0000000..3c37396
--- /dev/null
+++ b/build-aux/cargo.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+export MESON_BUILD_ROOT="$1"
+export MESON_SOURCE_ROOT="$2"
+export CARGO_TARGET_DIR="$MESON_BUILD_ROOT"/target
+export CARGO_HOME="$MESON_BUILD_ROOT"/cargo-home
+export OUTPUT="$3"
+export BUILDTYPE="$4"
+export APP_BIN="$5"
+
+
+if [ $BUILDTYPE = "release" ]
+then
+ echo "RELEASE MODE"
+ cargo build --manifest-path \
+ "$MESON_SOURCE_ROOT"/Cargo.toml --release && \
+ cp "$CARGO_TARGET_DIR"/release/"$APP_BIN" "$OUTPUT"
+else
+ echo "DEBUG MODE"
+ cargo build --manifest-path \
+ "$MESON_SOURCE_ROOT"/Cargo.toml && \
+ cp "$CARGO_TARGET_DIR"/debug/"$APP_BIN" "$OUTPUT"
+fi
+
diff --git a/data/icons/hicolor/scalable/apps/software.lavender.hermes.svg b/data/icons/hicolor/scalable/apps/software.lavender.hermes.svg
new file mode 100644
index 0000000..a74c4df
--- /dev/null
+++ b/data/icons/hicolor/scalable/apps/software.lavender.hermes.svg
@@ -0,0 +1,130 @@
+
+
diff --git a/data/icons/hicolor/symbolic/apps/software.lavender.hermes-symbolic.svg b/data/icons/hicolor/symbolic/apps/software.lavender.hermes-symbolic.svg
new file mode 100644
index 0000000..0444828
--- /dev/null
+++ b/data/icons/hicolor/symbolic/apps/software.lavender.hermes-symbolic.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/data/icons/meson.build b/data/icons/meson.build
new file mode 100644
index 0000000..fa8b790
--- /dev/null
+++ b/data/icons/meson.build
@@ -0,0 +1,13 @@
+application_id = 'software.lavender.hermes'
+
+scalable_dir = join_paths('hicolor', 'scalable', 'apps')
+install_data(
+ join_paths(scalable_dir, ('@0@.svg').format(application_id)),
+ install_dir: join_paths(get_option('datadir'), 'icons', scalable_dir)
+)
+
+symbolic_dir = join_paths('hicolor', 'symbolic', 'apps')
+install_data(
+ join_paths(symbolic_dir, ('@0@-symbolic.svg').format(application_id)),
+ install_dir: join_paths(get_option('datadir'), 'icons', symbolic_dir)
+)
diff --git a/data/meson.build b/data/meson.build
new file mode 100644
index 0000000..df28430
--- /dev/null
+++ b/data/meson.build
@@ -0,0 +1,43 @@
+desktop_file = i18n.merge_file(
+ input: 'software.lavender.hermes.desktop.in',
+ output: 'software.lavender.hermes.desktop',
+ type: 'desktop',
+ po_dir: '../po',
+ install: true,
+ install_dir: join_paths(get_option('datadir'), 'applications')
+)
+
+desktop_utils = find_program('desktop-file-validate', required: false)
+if desktop_utils.found()
+ test('Validate desktop file', desktop_utils,
+ args: [desktop_file]
+ )
+endif
+
+appstream_file = i18n.merge_file(
+ input: 'software.lavender.hermes.appdata.xml.in',
+ output: 'software.lavender.hermes.appdata.xml',
+ po_dir: '../po',
+ install: true,
+ install_dir: join_paths(get_option('datadir'), 'appdata')
+)
+
+appstream_util = find_program('appstream-util', required: false)
+if appstream_util.found()
+ test('Validate appstream file', appstream_util,
+ args: ['validate', appstream_file]
+ )
+endif
+
+install_data('software.lavender.hermes.gschema.xml',
+ install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas')
+)
+
+compile_schemas = find_program('glib-compile-schemas', required: false)
+if compile_schemas.found()
+ test('Validate schema file', compile_schemas,
+ args: ['--strict', '--dry-run', meson.current_source_dir()]
+ )
+endif
+
+subdir('icons')
diff --git a/data/software.lavender.hermes.appdata.xml.in b/data/software.lavender.hermes.appdata.xml.in
new file mode 100644
index 0000000..8542e4e
--- /dev/null
+++ b/data/software.lavender.hermes.appdata.xml.in
@@ -0,0 +1,9 @@
+
+
+ software.lavender.hermes.desktop
+ CC0-1.0
+ LicenseRef-proprietary
+
+ No description
+
+
diff --git a/data/software.lavender.hermes.desktop.in b/data/software.lavender.hermes.desktop.in
new file mode 100644
index 0000000..ef51f9b
--- /dev/null
+++ b/data/software.lavender.hermes.desktop.in
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=hermes
+Exec=hermes
+Icon=software.lavender.hermes
+Terminal=false
+Type=Application
+Categories=GTK;
+StartupNotify=true
diff --git a/data/software.lavender.hermes.gschema.xml b/data/software.lavender.hermes.gschema.xml
new file mode 100644
index 0000000..cd665be
--- /dev/null
+++ b/data/software.lavender.hermes.gschema.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/meson.build b/meson.build
new file mode 100644
index 0000000..e5feb25
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,27 @@
+project('hermes', 'rust',
+ version: '0.1.0',
+ meson_version: '>= 0.59.0',
+ default_options: [ 'warning_level=2',
+ 'werror=false',
+ ],
+)
+
+i18n = import('i18n')
+
+gnome = import('gnome')
+
+
+cargo_sources = files(
+ 'Cargo.toml',
+ 'Cargo.lock',
+)
+
+subdir('data')
+subdir('src')
+subdir('po')
+
+gnome.post_install(
+ glib_compile_schemas: true,
+ gtk_update_icon_cache: true,
+ update_desktop_database: true,
+)
diff --git a/po/LINGUAS b/po/LINGUAS
new file mode 100644
index 0000000..e69de29
diff --git a/po/POTFILES b/po/POTFILES
new file mode 100644
index 0000000..ca0bc82
--- /dev/null
+++ b/po/POTFILES
@@ -0,0 +1,4 @@
+data/software.lavender.hermes.desktop.in
+data/software.lavender.hermes.appdata.xml.in
+data/software.lavender.hermes.gschema.xml
+src/window.ui
diff --git a/po/meson.build b/po/meson.build
new file mode 100644
index 0000000..9b9fd99
--- /dev/null
+++ b/po/meson.build
@@ -0,0 +1 @@
+i18n.gettext('hermes', preset: 'glib')
diff --git a/software.lavender.hermes.json b/software.lavender.hermes.json
new file mode 100644
index 0000000..25edf0a
--- /dev/null
+++ b/software.lavender.hermes.json
@@ -0,0 +1,51 @@
+{
+ "app-id" : "software.lavender.hermes",
+ "runtime" : "org.gnome.Platform",
+ "runtime-version" : "master",
+ "sdk" : "org.gnome.Sdk",
+ "sdk-extensions" : [
+ "org.freedesktop.Sdk.Extension.rust-stable"
+ ],
+ "command" : "hermes",
+ "finish-args" : [
+ "--share=network",
+ "--share=ipc",
+ "--socket=fallback-x11",
+ "--device=dri",
+ "--socket=wayland"
+ ],
+ "build-options" : {
+ "append-path" : "/usr/lib/sdk/rust-stable/bin",
+ "build-args" : [
+ "--share=network"
+ ],
+ "env" : {
+ "RUST_BACKTRACE" : "1",
+ "RUST_LOG" : "hermes=debug"
+ }
+ },
+ "cleanup" : [
+ "/include",
+ "/lib/pkgconfig",
+ "/man",
+ "/share/doc",
+ "/share/gtk-doc",
+ "/share/man",
+ "/share/pkgconfig",
+ "*.la",
+ "*.a"
+ ],
+ "modules" : [
+ {
+ "name" : "hermes",
+ "builddir" : true,
+ "buildsystem" : "meson",
+ "sources" : [
+ {
+ "type" : "git",
+ "url" : "file:///var/home/hive/Projects/Hermes"
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/application.rs b/src/application.rs
new file mode 100644
index 0000000..240b945
--- /dev/null
+++ b/src/application.rs
@@ -0,0 +1,93 @@
+use glib::clone;
+use gtk::prelude::*;
+use gtk::subclass::prelude::*;
+use gtk::{gio, glib};
+use adw::subclass::prelude::*;
+
+use crate::config::VERSION;
+use crate::HermesWindow;
+
+mod imp {
+ use super::*;
+
+ #[derive(Debug, Default)]
+ pub struct HermesApplication {}
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for HermesApplication {
+ const NAME: &'static str = "HermesApplication";
+ type Type = super::HermesApplication;
+ type ParentType = adw::Application;
+ }
+
+ impl ObjectImpl for HermesApplication {
+ fn constructed(&self, obj: &Self::Type) {
+ self.parent_constructed(obj);
+
+ obj.setup_gactions();
+ obj.set_accels_for_action("app.quit", &["q"]);
+ }
+ }
+
+ impl ApplicationImpl for HermesApplication {
+ // We connect to the activate callback to create a window when the application
+ // has been launched. Additionally, this callback notifies us when the user
+ // tries to launch a "second instance" of the application. When they try
+ // to do that, we'll just present any existing window.
+ fn activate(&self, application: &Self::Type) {
+ // Get the current window or create one if necessary
+ let window = if let Some(window) = application.active_window() {
+ window
+ } else {
+ let window = HermesWindow::new(application);
+ window.upcast()
+ };
+
+ // Ask the window manager/compositor to present the window
+ window.present();
+ }
+ }
+
+ impl GtkApplicationImpl for HermesApplication {}
+ impl AdwApplicationImpl for HermesApplication {}
+}
+
+glib::wrapper! {
+ pub struct HermesApplication(ObjectSubclass)
+ @extends gio::Application, gtk::Application, adw::Application,
+ @implements gio::ActionGroup, gio::ActionMap;
+}
+
+impl HermesApplication {
+ pub fn new(application_id: &str, flags: &gio::ApplicationFlags) -> Self {
+ glib::Object::new(&[("application-id", &application_id), ("flags", flags)])
+ .expect("Failed to create HermesApplication")
+ }
+
+ fn setup_gactions(&self) {
+ let quit_action = gio::SimpleAction::new("quit", None);
+ quit_action.connect_activate(clone!(@weak self as app => move |_, _| {
+ app.quit();
+ }));
+ self.add_action(&quit_action);
+
+ let about_action = gio::SimpleAction::new("about", None);
+ about_action.connect_activate(clone!(@weak self as app => move |_, _| {
+ app.show_about();
+ }));
+ self.add_action(&about_action);
+ }
+
+ fn show_about(&self) {
+ let window = self.active_window().unwrap();
+ let dialog = gtk::AboutDialog::builder()
+ .transient_for(&window)
+ .modal(true)
+ .program_name("hermes")
+ .version(VERSION)
+ .authors(vec!["The Hive".into()])
+ .build();
+
+ dialog.present();
+ }
+}
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000..91035e6
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,4 @@
+pub static VERSION: &str = "0.1.0";
+pub static GETTEXT_PACKAGE: &str = "hermes";
+pub static LOCALEDIR: &str = "/app/share/locale";
+pub static PKGDATADIR: &str = "/app/share/hermes";
diff --git a/src/config.rs.in b/src/config.rs.in
new file mode 100644
index 0000000..1a24858
--- /dev/null
+++ b/src/config.rs.in
@@ -0,0 +1,4 @@
+pub static VERSION: &str = @VERSION@;
+pub static GETTEXT_PACKAGE: &str = @GETTEXT_PACKAGE@;
+pub static LOCALEDIR: &str = @LOCALEDIR@;
+pub static PKGDATADIR: &str = @PKGDATADIR@;
diff --git a/src/gtk/help-overlay.ui b/src/gtk/help-overlay.ui
new file mode 100644
index 0000000..ef12f02
--- /dev/null
+++ b/src/gtk/help-overlay.ui
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/src/hermes.gresource.xml b/src/hermes.gresource.xml
new file mode 100644
index 0000000..75e70f9
--- /dev/null
+++ b/src/hermes.gresource.xml
@@ -0,0 +1,7 @@
+
+
+
+ window.ui
+ gtk/help-overlay.ui
+
+
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..87d88d1
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,35 @@
+mod application;
+mod config;
+mod window;
+
+use self::application::HermesApplication;
+use self::window::HermesWindow;
+
+use config::{GETTEXT_PACKAGE, LOCALEDIR, PKGDATADIR};
+use gettextrs::{bind_textdomain_codeset, bindtextdomain, textdomain};
+use gtk::gio;
+use gtk::prelude::*;
+
+fn main() {
+ // Set up gettext translations
+ bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR).expect("Unable to bind the text domain");
+ bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8")
+ .expect("Unable to set the text domain encoding");
+ textdomain(GETTEXT_PACKAGE).expect("Unable to switch to the text domain");
+
+ // Load resources
+ let resources = gio::Resource::load(PKGDATADIR.to_owned() + "/hermes.gresource")
+ .expect("Could not load resources");
+ gio::resources_register(&resources);
+
+ // Create a new GtkApplication. The application manages our main loop,
+ // application windows, integration with the window manager/compositor, and
+ // desktop features such as file opening and single-instance applications.
+ let app = HermesApplication::new("software.lavender.hermes", &gio::ApplicationFlags::empty());
+
+ // Run the application. This function will block until the application
+ // exits. Upon return, we have our exit code to return to the shell. (This
+ // is the code you see when you do `echo $?` after running a command in a
+ // terminal.
+ std::process::exit(app.run());
+}
diff --git a/src/meson.build b/src/meson.build
new file mode 100644
index 0000000..6083327
--- /dev/null
+++ b/src/meson.build
@@ -0,0 +1,57 @@
+pkgdatadir = join_paths(get_option('prefix'), get_option('datadir'), meson.project_name())
+gnome = import('gnome')
+
+gnome.compile_resources('hermes',
+ 'hermes.gresource.xml',
+ gresource_bundle: true,
+ install: true,
+ install_dir: pkgdatadir,
+)
+
+conf = configuration_data()
+conf.set_quoted('VERSION', meson.project_version())
+conf.set_quoted('GETTEXT_PACKAGE', 'hermes')
+conf.set_quoted('LOCALEDIR', join_paths(get_option('prefix'), get_option('localedir')))
+conf.set_quoted('PKGDATADIR', pkgdatadir)
+
+configure_file(
+ input: 'config.rs.in',
+ output: 'config.rs',
+ configuration: conf
+)
+
+# Copy the config.rs output to the source directory.
+run_command(
+ 'cp',
+ join_paths(meson.project_build_root(), 'src', 'config.rs'),
+ join_paths(meson.project_source_root(), 'src', 'config.rs'),
+ check: true
+)
+
+rust_sources = files(
+ 'application.rs',
+ 'config.rs',
+ 'main.rs',
+ 'window.rs',
+)
+
+sources = [cargo_sources, rust_sources]
+
+cargo_script = find_program(join_paths(meson.project_source_root(), 'build-aux/cargo.sh'))
+cargo_release = custom_target(
+ 'cargo-build',
+ build_by_default: true,
+ input: sources,
+ output: meson.project_name(),
+ console: true,
+ install: true,
+ install_dir: get_option('bindir'),
+ command: [
+ cargo_script,
+ meson.project_build_root(),
+ meson.project_source_root(),
+ '@OUTPUT@',
+ get_option('buildtype'),
+ meson.project_name(),
+ ]
+)
diff --git a/src/window.rs b/src/window.rs
new file mode 100644
index 0000000..0089efe
--- /dev/null
+++ b/src/window.rs
@@ -0,0 +1,50 @@
+use gtk::prelude::*;
+use gtk::subclass::prelude::*;
+use gtk::{gio, glib, CompositeTemplate};
+
+mod imp {
+ use super::*;
+
+ #[derive(Debug, Default, CompositeTemplate)]
+ #[template(resource = "/software/lavender/hermes/window.ui")]
+ pub struct HermesWindow {
+ // Template widgets
+ #[template_child]
+ pub header_bar: TemplateChild,
+ #[template_child]
+ pub label: TemplateChild,
+ }
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for HermesWindow {
+ const NAME: &'static str = "HermesWindow";
+ type Type = super::HermesWindow;
+ type ParentType = gtk::ApplicationWindow;
+
+ fn class_init(klass: &mut Self::Class) {
+ Self::bind_template(klass);
+ }
+
+ fn instance_init(obj: &glib::subclass::InitializingObject) {
+ obj.init_template();
+ }
+ }
+
+ impl ObjectImpl for HermesWindow {}
+ impl WidgetImpl for HermesWindow {}
+ impl WindowImpl for HermesWindow {}
+ impl ApplicationWindowImpl for HermesWindow {}
+}
+
+glib::wrapper! {
+ pub struct HermesWindow(ObjectSubclass)
+ @extends gtk::Widget, gtk::Window, gtk::ApplicationWindow,
+ @implements gio::ActionGroup, gio::ActionMap;
+}
+
+impl HermesWindow {
+ pub fn new>(application: &P) -> Self {
+ glib::Object::new(&[("application", application)])
+ .expect("Failed to create HermesWindow")
+ }
+}
diff --git a/src/window.ui b/src/window.ui
new file mode 100644
index 0000000..df86712
--- /dev/null
+++ b/src/window.ui
@@ -0,0 +1,44 @@
+
+
+
+
+ 600
+ 300
+
+
+
+
+
+ Hello, World!
+
+
+
+
+
+
+
+
+
+