Initial Wayland greeter source
This commit is contained in:
commit
c302251fef
5 files changed
+1324
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
/target/
|
||||||
Generated
+708
@@ -0,0 +1,708 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "2.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cairo-rs"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b01fe135c0bd16afe262b6dea349bd5ea30e6de50708cec639aae7c5c14cc7e4"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cairo-sys-rs",
|
||||||
|
"glib",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cairo-sys-rs"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06c28280c6b12055b5e39e4554271ae4e6630b27c0da9148c4cf6485fc6d245c"
|
||||||
|
dependencies = [
|
||||||
|
"glib-sys",
|
||||||
|
"libc",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-expr"
|
||||||
|
version = "0.20.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ba9e9ec16c447027685b1f897b720e18e9a8afd00bd7332c483537e38086c9f"
|
||||||
|
dependencies = [
|
||||||
|
"smallvec",
|
||||||
|
"target-lexicon",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "field-offset"
|
||||||
|
version = "0.3.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f"
|
||||||
|
dependencies = [
|
||||||
|
"memoffset",
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-channel"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-core"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-executor"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-io"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-macro"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-task"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-util"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
|
||||||
|
dependencies = [
|
||||||
|
"futures-core",
|
||||||
|
"futures-macro",
|
||||||
|
"futures-task",
|
||||||
|
"pin-project-lite",
|
||||||
|
"slab",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdk-pixbuf"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "debb0d39e3cdd84626edfd54d6e4a6ba2da9a0ef2e796e691c4e9f8646fda00c"
|
||||||
|
dependencies = [
|
||||||
|
"gdk-pixbuf-sys",
|
||||||
|
"gio",
|
||||||
|
"glib",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdk-pixbuf-sys"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd95ad50b9a3d2551e25dd4f6892aff0b772fe5372d84514e9d0583af60a0ce7"
|
||||||
|
dependencies = [
|
||||||
|
"gio-sys",
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"libc",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdk4"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "756564212bbe4a4ce05d88ffbd2582581ac6003832d0d32822d0825cca84bfbf"
|
||||||
|
dependencies = [
|
||||||
|
"cairo-rs",
|
||||||
|
"gdk-pixbuf",
|
||||||
|
"gdk4-sys",
|
||||||
|
"gio",
|
||||||
|
"glib",
|
||||||
|
"libc",
|
||||||
|
"pango",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gdk4-sys"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6d4e5b3ccf591826a4adcc83f5f57b4e59d1925cb4bf620b0d645f79498b034"
|
||||||
|
dependencies = [
|
||||||
|
"cairo-sys-rs",
|
||||||
|
"gdk-pixbuf-sys",
|
||||||
|
"gio-sys",
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"libc",
|
||||||
|
"pango-sys",
|
||||||
|
"pkg-config",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gio"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c5ff48bf600c68b476e61dc6b7c762f2f4eb91deef66583ba8bb815c30b5811a"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-io",
|
||||||
|
"futures-util",
|
||||||
|
"gio-sys",
|
||||||
|
"glib",
|
||||||
|
"libc",
|
||||||
|
"pin-project-lite",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gio-sys"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22"
|
||||||
|
dependencies = [
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"libc",
|
||||||
|
"system-deps",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glib"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
"gio-sys",
|
||||||
|
"glib-macros",
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"libc",
|
||||||
|
"memchr",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glib-macros"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glib-sys"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gobject-sys"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294"
|
||||||
|
dependencies = [
|
||||||
|
"glib-sys",
|
||||||
|
"libc",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-rs"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2730030ac9db663fd8bfe1e7093742c1cafb92db9c315c9417c29032341fe2f9"
|
||||||
|
dependencies = [
|
||||||
|
"glib",
|
||||||
|
"graphene-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "graphene-sys"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "915e32091ea9ad241e4b044af62b7351c2d68aeb24f489a0d7f37a0fc484fd93"
|
||||||
|
dependencies = [
|
||||||
|
"glib-sys",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gsk4"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e755de9d8c5896c5beaa028b89e1969d067f1b9bf1511384ede971f5983aa153"
|
||||||
|
dependencies = [
|
||||||
|
"cairo-rs",
|
||||||
|
"gdk4",
|
||||||
|
"glib",
|
||||||
|
"graphene-rs",
|
||||||
|
"gsk4-sys",
|
||||||
|
"libc",
|
||||||
|
"pango",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gsk4-sys"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7ce91472391146f482065f1041876d8f869057b195b95399414caa163d72f4f7"
|
||||||
|
dependencies = [
|
||||||
|
"cairo-sys-rs",
|
||||||
|
"gdk4-sys",
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"graphene-sys",
|
||||||
|
"libc",
|
||||||
|
"pango-sys",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gtk4"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acb21d53cfc6f7bfaf43549731c43b67ca47d87348d81c8cfc4dcdd44828e1a4"
|
||||||
|
dependencies = [
|
||||||
|
"cairo-rs",
|
||||||
|
"field-offset",
|
||||||
|
"futures-channel",
|
||||||
|
"gdk-pixbuf",
|
||||||
|
"gdk4",
|
||||||
|
"gio",
|
||||||
|
"glib",
|
||||||
|
"graphene-rs",
|
||||||
|
"gsk4",
|
||||||
|
"gtk4-macros",
|
||||||
|
"gtk4-sys",
|
||||||
|
"libc",
|
||||||
|
"pango",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gtk4-macros"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3ccfb5a14a3d941244815d5f8101fa12d4577b59cc47245778d8d907b0003e42"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.119",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gtk4-sys"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "842577fe5a1ee15d166cd3afe804ce0cab6173bc789ca32e21308834f20088dd"
|
||||||
|
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 = "hashbrown"
|
||||||
|
version = "0.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.14.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.190"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pango"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "52d1d85e2078077a065bb7fc072783d5bcd4e51b379f22d67107d0a16937eb69"
|
||||||
|
dependencies = [
|
||||||
|
"gio",
|
||||||
|
"glib",
|
||||||
|
"libc",
|
||||||
|
"pango-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pango-sys"
|
||||||
|
version = "0.21.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4f06627d36ed5ff303d2df65211fc2e52ba5b17bf18dd80ff3d9628d6e06cfd"
|
||||||
|
dependencies = [
|
||||||
|
"glib-sys",
|
||||||
|
"gobject-sys",
|
||||||
|
"libc",
|
||||||
|
"system-deps",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pin-project-lite"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.34"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "3.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
|
||||||
|
dependencies = [
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.107"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.47"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.28"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_core"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.229"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 3.0.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.154"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"serde",
|
||||||
|
"serde_core",
|
||||||
|
"zmij",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "1.16.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b3dc8af474f516a851ff4bd12db780f948b9250ad37211e4eec0bccea54e01b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.119"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "3.0.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d78c8dee4c7bf0e14673097256fed6142ce9d3b85a408189d07482442145823b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "system-deps"
|
||||||
|
version = "7.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-expr",
|
||||||
|
"heck",
|
||||||
|
"pkg-config",
|
||||||
|
"toml",
|
||||||
|
"version-compare",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "target-lexicon"
|
||||||
|
version = "0.13.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "1.1.8+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde_core",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"toml_writer",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "1.1.2+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053"
|
||||||
|
dependencies = [
|
||||||
|
"serde_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.25.17+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_parser",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_parser"
|
||||||
|
version = "1.1.5+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c"
|
||||||
|
dependencies = [
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_writer"
|
||||||
|
version = "1.1.3+spec-1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version-compare"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wayland-greeter"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"gtk4",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zmij"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "wayland-greeter"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
rust-version = "1.85"
|
||||||
|
description = "A compact GTK4 greetd greeter for Wayland sessions"
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gtk = { package = "gtk4", version = "0.10" }
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
# Wayland Greeter
|
||||||
|
|
||||||
|
A compact Rust + GTK4 graphical greeter for greetd. It uses greetd for PAM authentication and launches installed Wayland session entries, including Hyprland.
|
||||||
|
|
||||||
|
## Current status
|
||||||
|
|
||||||
|
Early project build. The greeter UI and greetd IPC client are implemented; it has not yet been activated as the system login path. The target is a Gentoo amd64 musl + LLVM host with GTK4 installed.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
Build on the target Gentoo host so the binary links against the host's musl and GTK libraries:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --release
|
||||||
|
```
|
||||||
|
|
||||||
|
Native build requirements: Rust/Cargo, pkg-config, GTK4 development files, and a working Wayland GTK backend. This produces `target/release/wayland-greeter`; install it through a local ebuild or a reviewed manual installation step after confirming the output's dynamic dependencies.
|
||||||
|
|
||||||
|
## Run under greetd
|
||||||
|
|
||||||
|
The application needs `GREETD_SOCK`, which greetd provides. A graphical greeter also needs a compositor. A typical setup is greetd → Cage → this app, then greetd starts Hyprland after authentication. Example only (adapt the executable path and service/init setup to the host):
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[terminal]
|
||||||
|
vt = 1
|
||||||
|
|
||||||
|
[default_session]
|
||||||
|
command = "cage -s -- /usr/local/bin/wayland-greeter"
|
||||||
|
user = "greeter"
|
||||||
|
```
|
||||||
|
|
||||||
|
Cage and a greeter account must be installed/configured separately. Gentoo's compositor listing places Cage in the `guru` overlay (`gui-wm/cage::guru`), rather than the main Gentoo repository; inspect that overlay and the host's `emerge -pv` plan before considering it. Keep a working TTY login route before changing the greetd default session. Do not activate this configuration until the binary, Cage startup, PAM flow, session descriptors, VT switching, and rollback have been checked locally.
|
||||||
|
|
||||||
|
The session picker is hidden by default; press **F2** to show or hide it. The login surface starts with only the username and login button; PAM prompts reveal the response field when needed. The picker reads `*.desktop` entries from `/usr/share/wayland-sessions` and `/usr/local/share/wayland-sessions`. If neither has entries, it offers a `Hyprland` fallback command. The selected entry's `Exec` is parsed into argv and passed to greetd directly, without shell expansion.
|
||||||
|
|
||||||
|
## Design notes
|
||||||
|
|
||||||
|
- PAM prompts come from greetd IPC and are not assumed to be a single username/password exchange.
|
||||||
|
- Secrets are never written to disk or logged.
|
||||||
|
- The card is centered by GTK's center container in both dimensions. Its UI colors come only from `~/.config/foot/foot.ini`; it uses Maple Mono and GTK4 system libraries.
|
||||||
|
- See the project skill `hyprland-greetd-greeter` for protocol, host, and maintenance guidance.
|
||||||
+561
@@ -0,0 +1,561 @@
|
|||||||
|
use gtk::glib;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::fs;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::os::unix::net::UnixStream;
|
||||||
|
use std::path::Path;
|
||||||
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
enum Request {
|
||||||
|
CreateSession { username: String },
|
||||||
|
PostAuthMessageResponse { response: Option<String> },
|
||||||
|
StartSession { cmd: Vec<String>, env: Vec<String> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[serde(tag = "type", rename_all = "snake_case")]
|
||||||
|
enum Response {
|
||||||
|
Success,
|
||||||
|
Error {
|
||||||
|
error_type: String,
|
||||||
|
description: String,
|
||||||
|
},
|
||||||
|
AuthMessage {
|
||||||
|
auth_message_type: String,
|
||||||
|
auth_message: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Ipc(UnixStream);
|
||||||
|
|
||||||
|
impl Ipc {
|
||||||
|
fn connect() -> Result<Self, String> {
|
||||||
|
let path = std::env::var_os("GREETD_SOCK")
|
||||||
|
.ok_or("GREETD_SOCK is not set; start this program from greetd")?;
|
||||||
|
UnixStream::connect(path)
|
||||||
|
.map(Self)
|
||||||
|
.map_err(|e| format!("Could not connect to greetd: {e}"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request(&mut self, req: &Request) -> Result<Response, String> {
|
||||||
|
let payload = serde_json::to_vec(req).map_err(|e| e.to_string())?;
|
||||||
|
let size = u32::try_from(payload.len()).map_err(|_| "greetd request is too large")?;
|
||||||
|
self.0
|
||||||
|
.write_all(&size.to_ne_bytes())
|
||||||
|
.and_then(|_| self.0.write_all(&payload))
|
||||||
|
.map_err(|e| format!("Could not send greetd request: {e}"))?;
|
||||||
|
|
||||||
|
let mut len = [0_u8; 4];
|
||||||
|
self.0
|
||||||
|
.read_exact(&mut len)
|
||||||
|
.map_err(|e| format!("Could not read greetd response: {e}"))?;
|
||||||
|
let size = u32::from_ne_bytes(len) as usize;
|
||||||
|
if size > 1024 * 1024 {
|
||||||
|
return Err("greetd response exceeded the 1 MiB safety limit".into());
|
||||||
|
}
|
||||||
|
let mut payload = vec![0; size];
|
||||||
|
self.0
|
||||||
|
.read_exact(&mut payload)
|
||||||
|
.map_err(|e| format!("Could not read greetd response: {e}"))?;
|
||||||
|
serde_json::from_slice(&payload).map_err(|e| format!("Invalid greetd response: {e}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum UiEvent {
|
||||||
|
Prompt {
|
||||||
|
message: String,
|
||||||
|
secret: bool,
|
||||||
|
input: bool,
|
||||||
|
},
|
||||||
|
Status(String),
|
||||||
|
Started,
|
||||||
|
Failed(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum WorkerAction {
|
||||||
|
Answer(Option<String>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Session {
|
||||||
|
name: String,
|
||||||
|
command: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let app = gtk::Application::builder()
|
||||||
|
.application_id("dev.wfalkowski.WaylandGreeter")
|
||||||
|
.flags(gtk::gio::ApplicationFlags::NON_UNIQUE)
|
||||||
|
.build();
|
||||||
|
app.connect_activate(build_ui);
|
||||||
|
app.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_ui(app: >k::Application) {
|
||||||
|
install_style();
|
||||||
|
let sessions = discover_sessions();
|
||||||
|
|
||||||
|
let window = gtk::ApplicationWindow::builder()
|
||||||
|
.application(app)
|
||||||
|
.title("Sign in")
|
||||||
|
.default_width(1000)
|
||||||
|
.default_height(700)
|
||||||
|
.build();
|
||||||
|
window.fullscreen();
|
||||||
|
|
||||||
|
let backdrop = gtk::CenterBox::new();
|
||||||
|
backdrop.set_orientation(gtk::Orientation::Vertical);
|
||||||
|
backdrop.set_hexpand(true);
|
||||||
|
backdrop.set_vexpand(true);
|
||||||
|
backdrop.add_css_class("backdrop");
|
||||||
|
|
||||||
|
let card = gtk::Box::new(gtk::Orientation::Vertical, 10);
|
||||||
|
card.set_halign(gtk::Align::Center);
|
||||||
|
card.set_valign(gtk::Align::Center);
|
||||||
|
card.set_width_request(340);
|
||||||
|
card.set_margin_top(16);
|
||||||
|
card.set_margin_bottom(16);
|
||||||
|
card.set_margin_start(16);
|
||||||
|
card.set_margin_end(16);
|
||||||
|
card.add_css_class("card");
|
||||||
|
|
||||||
|
let username = gtk::Entry::builder()
|
||||||
|
.placeholder_text("Username")
|
||||||
|
.hexpand(true)
|
||||||
|
.build();
|
||||||
|
username.add_css_class("field");
|
||||||
|
username.set_activates_default(true);
|
||||||
|
card.append(&username);
|
||||||
|
|
||||||
|
let response_entry = gtk::Entry::builder()
|
||||||
|
.placeholder_text("Password")
|
||||||
|
.visibility(false)
|
||||||
|
.hexpand(true)
|
||||||
|
.build();
|
||||||
|
response_entry.add_css_class("field");
|
||||||
|
response_entry.set_activates_default(true);
|
||||||
|
response_entry.set_sensitive(false);
|
||||||
|
response_entry.set_visible(false);
|
||||||
|
card.append(&response_entry);
|
||||||
|
|
||||||
|
let session_picker = gtk::ComboBoxText::new();
|
||||||
|
for (i, session) in sessions.iter().enumerate() {
|
||||||
|
session_picker.append(Some(&i.to_string()), &session.name);
|
||||||
|
}
|
||||||
|
if !sessions.is_empty() {
|
||||||
|
session_picker.set_active(Some(0));
|
||||||
|
}
|
||||||
|
session_picker.add_css_class("field");
|
||||||
|
let session_options = gtk::Box::new(gtk::Orientation::Vertical, 6);
|
||||||
|
session_options.add_css_class("session-options");
|
||||||
|
session_options.append(&field_label("Session"));
|
||||||
|
session_options.append(&session_picker);
|
||||||
|
session_options.set_visible(false);
|
||||||
|
card.append(&session_options);
|
||||||
|
|
||||||
|
let status = gtk::Label::new(None);
|
||||||
|
status.set_xalign(0.0);
|
||||||
|
status.set_wrap(true);
|
||||||
|
status.add_css_class("status");
|
||||||
|
status.set_visible(false);
|
||||||
|
card.append(&status);
|
||||||
|
|
||||||
|
let login = gtk::Button::with_label("Log in");
|
||||||
|
login.add_css_class("login-button");
|
||||||
|
window.set_default_widget(Some(&login));
|
||||||
|
card.append(&login);
|
||||||
|
|
||||||
|
backdrop.set_center_widget(Some(&card));
|
||||||
|
window.set_child(Some(&backdrop));
|
||||||
|
|
||||||
|
let (event_tx, event_rx) = mpsc::channel::<UiEvent>();
|
||||||
|
let action_sender = std::rc::Rc::new(std::cell::RefCell::new(None::<Sender<WorkerAction>>));
|
||||||
|
let login_busy = std::rc::Rc::new(std::cell::Cell::new(false));
|
||||||
|
|
||||||
|
{
|
||||||
|
let tx = event_tx.clone();
|
||||||
|
let login_busy = login_busy.clone();
|
||||||
|
let login = login.clone();
|
||||||
|
let username = username.clone();
|
||||||
|
let response_entry = response_entry.clone();
|
||||||
|
let status = status.clone();
|
||||||
|
let session_picker = session_picker.clone();
|
||||||
|
let sessions = sessions.clone();
|
||||||
|
let action_sender = action_sender.clone();
|
||||||
|
let login_for_callback = login.clone();
|
||||||
|
login.connect_clicked(move |_| {
|
||||||
|
let Some(selected) = session_picker
|
||||||
|
.active_id()
|
||||||
|
.and_then(|id| id.parse::<usize>().ok())
|
||||||
|
.and_then(|i| sessions.get(i).cloned())
|
||||||
|
else {
|
||||||
|
status.set_text("No Wayland session was found in /usr/share/wayland-sessions.");
|
||||||
|
status.set_visible(true);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
if login_busy.get() {
|
||||||
|
let answer = response_entry.text().to_string();
|
||||||
|
response_entry.set_text("");
|
||||||
|
if let Some(sender) = action_sender.borrow().as_ref() {
|
||||||
|
let _ = sender.send(WorkerAction::Answer(Some(answer)));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let user = username.text().trim().to_string();
|
||||||
|
if user.is_empty() {
|
||||||
|
status.set_text("Username required");
|
||||||
|
status.set_visible(true);
|
||||||
|
username.grab_focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if selected.command.is_empty() {
|
||||||
|
status.set_text("Selected session has no launch command");
|
||||||
|
status.set_visible(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
login_busy.set(true);
|
||||||
|
login_for_callback.set_sensitive(false);
|
||||||
|
let (action_tx, action_rx) = mpsc::channel::<WorkerAction>();
|
||||||
|
*action_sender.borrow_mut() = Some(action_tx);
|
||||||
|
login_for_callback.set_label("Continue");
|
||||||
|
status.set_text("");
|
||||||
|
status.set_visible(false);
|
||||||
|
response_entry.set_text("");
|
||||||
|
response_entry.set_visible(false);
|
||||||
|
response_entry.set_sensitive(false);
|
||||||
|
let tx = tx.clone();
|
||||||
|
thread::spawn(move || authenticate(user, selected, tx, action_rx));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let login = login.clone();
|
||||||
|
response_entry.connect_activate(move |_| login.emit_clicked());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let login = login.clone();
|
||||||
|
username.connect_activate(move |_| {
|
||||||
|
login.emit_clicked();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let sessions_visible = std::rc::Rc::new(std::cell::Cell::new(false));
|
||||||
|
let session_toggle = gtk::EventControllerKey::new();
|
||||||
|
session_toggle.set_propagation_phase(gtk::PropagationPhase::Capture);
|
||||||
|
{
|
||||||
|
let sessions_visible = sessions_visible.clone();
|
||||||
|
let session_options = session_options.clone();
|
||||||
|
let session_picker = session_picker.clone();
|
||||||
|
let response_entry = response_entry.clone();
|
||||||
|
session_toggle.connect_key_pressed(move |_, key, _, _| {
|
||||||
|
if key == gtk::gdk::Key::F2 {
|
||||||
|
let visible = !sessions_visible.get();
|
||||||
|
sessions_visible.set(visible);
|
||||||
|
session_options.set_visible(visible);
|
||||||
|
if visible {
|
||||||
|
session_picker.grab_focus();
|
||||||
|
} else {
|
||||||
|
response_entry.grab_focus();
|
||||||
|
}
|
||||||
|
glib::Propagation::Stop
|
||||||
|
} else {
|
||||||
|
glib::Propagation::Proceed
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
window.add_controller(session_toggle);
|
||||||
|
let event_rx = std::rc::Rc::new(std::cell::RefCell::new(event_rx));
|
||||||
|
let login_busy_for_events = login_busy.clone();
|
||||||
|
let login_for_events = login.clone();
|
||||||
|
let username_for_events = username.clone();
|
||||||
|
let response_for_events = response_entry.clone();
|
||||||
|
let status_for_events = status.clone();
|
||||||
|
let app_for_events = app.clone();
|
||||||
|
let action_sender_for_events = action_sender.clone();
|
||||||
|
glib::timeout_add_local(Duration::from_millis(40), move || {
|
||||||
|
loop {
|
||||||
|
let event = event_rx.borrow().try_recv();
|
||||||
|
match event {
|
||||||
|
Ok(UiEvent::Prompt {
|
||||||
|
message,
|
||||||
|
secret,
|
||||||
|
input,
|
||||||
|
}) => {
|
||||||
|
login_for_events.set_sensitive(input);
|
||||||
|
response_for_events.set_visibility(!secret);
|
||||||
|
response_for_events.set_sensitive(input);
|
||||||
|
response_for_events.set_visible(input);
|
||||||
|
if input {
|
||||||
|
login_for_events.set_label("Continue");
|
||||||
|
response_for_events.set_placeholder_text(Some(&message));
|
||||||
|
status_for_events.set_text("");
|
||||||
|
status_for_events.set_visible(false);
|
||||||
|
response_for_events.grab_focus();
|
||||||
|
} else {
|
||||||
|
status_for_events.set_text(&message);
|
||||||
|
status_for_events.set_visible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(UiEvent::Status(message)) => {
|
||||||
|
login_for_events.set_sensitive(false);
|
||||||
|
status_for_events.set_text(&message);
|
||||||
|
status_for_events.set_visible(true);
|
||||||
|
}
|
||||||
|
Ok(UiEvent::Started) => {
|
||||||
|
app_for_events.quit();
|
||||||
|
return glib::ControlFlow::Break;
|
||||||
|
}
|
||||||
|
Ok(UiEvent::Failed(message)) => {
|
||||||
|
login_busy_for_events.set(false);
|
||||||
|
*action_sender_for_events.borrow_mut() = None;
|
||||||
|
login_for_events.set_label("Log in");
|
||||||
|
login_for_events.set_sensitive(true);
|
||||||
|
response_for_events.set_text("");
|
||||||
|
response_for_events.set_sensitive(true);
|
||||||
|
response_for_events.set_visibility(false);
|
||||||
|
response_for_events.set_visible(false);
|
||||||
|
response_for_events.set_placeholder_text(Some("Password"));
|
||||||
|
status_for_events.set_text(&message);
|
||||||
|
status_for_events.set_visible(true);
|
||||||
|
username_for_events.grab_focus();
|
||||||
|
}
|
||||||
|
Err(mpsc::TryRecvError::Empty) => break,
|
||||||
|
Err(mpsc::TryRecvError::Disconnected) => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glib::ControlFlow::Continue
|
||||||
|
});
|
||||||
|
|
||||||
|
window.present();
|
||||||
|
username.grab_focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authenticate(
|
||||||
|
username: String,
|
||||||
|
session: Session,
|
||||||
|
events: Sender<UiEvent>,
|
||||||
|
actions: Receiver<WorkerAction>,
|
||||||
|
) {
|
||||||
|
let result = (|| -> Result<(), String> {
|
||||||
|
let mut ipc = Ipc::connect()?;
|
||||||
|
let mut response = ipc.request(&Request::CreateSession { username })?;
|
||||||
|
loop {
|
||||||
|
match response {
|
||||||
|
Response::Success => break,
|
||||||
|
Response::AuthMessage {
|
||||||
|
auth_message_type,
|
||||||
|
auth_message,
|
||||||
|
} => {
|
||||||
|
let kind = auth_message_type.as_str();
|
||||||
|
let input = matches!(kind, "visible" | "secret");
|
||||||
|
events
|
||||||
|
.send(UiEvent::Prompt {
|
||||||
|
message: auth_message,
|
||||||
|
secret: kind == "secret",
|
||||||
|
input,
|
||||||
|
})
|
||||||
|
.map_err(|_| "Greeter UI closed during authentication")?;
|
||||||
|
let answer = if input {
|
||||||
|
match actions
|
||||||
|
.recv()
|
||||||
|
.map_err(|_| "Greeter UI closed during authentication")?
|
||||||
|
{
|
||||||
|
WorkerAction::Answer(value) => value,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
response =
|
||||||
|
ipc.request(&Request::PostAuthMessageResponse { response: answer })?;
|
||||||
|
}
|
||||||
|
Response::Error {
|
||||||
|
error_type,
|
||||||
|
description,
|
||||||
|
} => {
|
||||||
|
return Err(if error_type == "auth_error" {
|
||||||
|
format!("Authentication failed: {description}")
|
||||||
|
} else {
|
||||||
|
description
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events
|
||||||
|
.send(UiEvent::Status("Starting your session…".into()))
|
||||||
|
.ok();
|
||||||
|
let env = vec![
|
||||||
|
"XDG_SESSION_TYPE=wayland".into(),
|
||||||
|
format!("XDG_CURRENT_DESKTOP={}", session.name),
|
||||||
|
format!("XDG_SESSION_DESKTOP={}", session.name),
|
||||||
|
];
|
||||||
|
match ipc.request(&Request::StartSession {
|
||||||
|
cmd: session.command,
|
||||||
|
env,
|
||||||
|
})? {
|
||||||
|
Response::Success => {
|
||||||
|
let _ = events.send(UiEvent::Started);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Response::Error { description, .. } => {
|
||||||
|
Err(format!("Could not start session: {description}"))
|
||||||
|
}
|
||||||
|
Response::AuthMessage { .. } => {
|
||||||
|
Err("Unexpected authentication prompt while starting session".into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
if let Err(error) = result {
|
||||||
|
let _ = events.send(UiEvent::Failed(error));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field_label(text: &str) -> gtk::Label {
|
||||||
|
let label = gtk::Label::new(Some(text));
|
||||||
|
label.set_xalign(0.0);
|
||||||
|
label.add_css_class("field-label");
|
||||||
|
label
|
||||||
|
}
|
||||||
|
|
||||||
|
fn discover_sessions() -> Vec<Session> {
|
||||||
|
let mut sessions = Vec::new();
|
||||||
|
for dir in [
|
||||||
|
// Match the user's previous tuigreet configuration. Put local greetd
|
||||||
|
// entries first so a custom launcher can override a system desktop
|
||||||
|
// entry with the same session name.
|
||||||
|
"/etc/greetd/sessions",
|
||||||
|
"/usr/share/wayland-sessions",
|
||||||
|
"/usr/local/share/wayland-sessions",
|
||||||
|
] {
|
||||||
|
let Ok(entries) = fs::read_dir(dir) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
for entry in entries.flatten() {
|
||||||
|
let path = entry.path();
|
||||||
|
if path.extension().is_none_or(|ext| ext != "desktop") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(session) = parse_session(&path) {
|
||||||
|
sessions.push(session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sessions.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||||
|
sessions.dedup_by(|a, b| a.name == b.name);
|
||||||
|
if sessions.is_empty() {
|
||||||
|
sessions.push(Session {
|
||||||
|
name: "Hyprland".into(),
|
||||||
|
command: vec!["Hyprland".into()],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_session(path: &Path) -> Option<Session> {
|
||||||
|
let contents = fs::read_to_string(path).ok()?;
|
||||||
|
let mut in_desktop_entry = false;
|
||||||
|
let mut name = None;
|
||||||
|
let mut exec = None;
|
||||||
|
for line in contents.lines().map(str::trim) {
|
||||||
|
if line.starts_with('[') {
|
||||||
|
in_desktop_entry = line == "[Desktop Entry]";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if !in_desktop_entry || line.starts_with('#') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(value) = line.strip_prefix("Name=") {
|
||||||
|
name = Some(value.trim().to_string());
|
||||||
|
}
|
||||||
|
if let Some(value) = line.strip_prefix("Exec=") {
|
||||||
|
exec = Some(value.trim());
|
||||||
|
}
|
||||||
|
if line == "Hidden=true" || line == "NoDisplay=true" {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut command = split_exec(exec?);
|
||||||
|
command.retain(|part| !(part.starts_with('%') && part.len() == 2));
|
||||||
|
if command.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Session {
|
||||||
|
name: name.filter(|s| !s.is_empty()).unwrap_or_else(|| {
|
||||||
|
path.file_stem()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
|
}),
|
||||||
|
command,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Desktop Entry Exec is a command line, not a shell program. This tokenizer
|
||||||
|
// handles quoting and escaping while intentionally avoiding shell expansion.
|
||||||
|
fn split_exec(input: &str) -> Vec<String> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
let mut word = String::new();
|
||||||
|
let mut quote = None;
|
||||||
|
let mut escaped = false;
|
||||||
|
for ch in input.chars() {
|
||||||
|
if escaped {
|
||||||
|
word.push(ch);
|
||||||
|
escaped = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ch == '\\' && quote != Some('\'') {
|
||||||
|
escaped = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if let Some(q) = quote {
|
||||||
|
if ch == q {
|
||||||
|
quote = None;
|
||||||
|
} else {
|
||||||
|
word.push(ch);
|
||||||
|
}
|
||||||
|
} else if ch == '\'' || ch == '"' {
|
||||||
|
quote = Some(ch);
|
||||||
|
} else if ch.is_whitespace() {
|
||||||
|
if !word.is_empty() {
|
||||||
|
out.push(std::mem::take(&mut word));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
word.push(ch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if escaped {
|
||||||
|
word.push('\\');
|
||||||
|
}
|
||||||
|
if !word.is_empty() {
|
||||||
|
out.push(word);
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|
||||||
|
fn install_style() {
|
||||||
|
let css = r#"
|
||||||
|
.backdrop { background: #151515; color: #d0d0d0; font-family: "Maple Mono Normal"; font-size: 9pt; }
|
||||||
|
.card { background: #151515; border: 1px solid #505050; border-radius: 1px; padding: 18px; }
|
||||||
|
.field-label { color: #505050; font-size: 9pt; }
|
||||||
|
entry, combobox button { color: #d0d0d0; background: #151515; border: 1px solid #505050; border-radius: 1px; min-height: 36px; padding: 0 10px; caret-color: #f5f5f5; }
|
||||||
|
entry:focus, combobox button:focus { border-color: #6c99bb; }
|
||||||
|
entry placeholder { color: #505050; }
|
||||||
|
button.login-button { color: #d0d0d0; background: #151515; border: 1px solid #505050; border-radius: 1px; min-height: 36px; margin-top: 2px; }
|
||||||
|
button.login-button:hover { color: #f5f5f5; background: #547c99; }
|
||||||
|
button.login-button:focus { border-color: #6c99bb; }
|
||||||
|
button.login-button:disabled { color: #505050; background: #151515; border-color: #505050; }
|
||||||
|
.status { color: #d3a04d; font-size: 9pt; }
|
||||||
|
"#;
|
||||||
|
let provider = gtk::CssProvider::new();
|
||||||
|
provider.load_from_data(css);
|
||||||
|
if let Some(display) = gtk::gdk::Display::default() {
|
||||||
|
gtk::style_context_add_provider_for_display(
|
||||||
|
&display,
|
||||||
|
&provider,
|
||||||
|
gtk::STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user