diff options
| author | 2026-02-23 14:51:45 -0500 | |
|---|---|---|
| committer | 2026-02-23 14:51:45 -0500 | |
| commit | c9dfb848ed27480c62d7444af47c0dd17926d146 (patch) | |
| tree | 19eec85b16497e211f93daa10def229fca01e2b8 | |
initial commit
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 316 | ||||
| -rw-r--r-- | Cargo.toml | 9 | ||||
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | src/main.rs | 152 |
5 files changed, 481 insertions, 0 deletions
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..7bde6ec --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,316 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags 2.11.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.117", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "lockrs" +version = "0.1.0" +dependencies = [ + "pam", + "xcb", + "xkbcommon", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "pam" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab553c52103edb295d8f7d6a3b593dc22a30b1fb99643c777a8f36915e285ba" +dependencies = [ + "libc", + "memchr", + "pam-macros", + "pam-sys", + "users", +] + +[[package]] +name = "pam-macros" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c94f3b9b97df3c6d4e51a14916639b24e02c7d15d1dba686ce9b1118277cb811" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "pam-sys" +version = "1.0.0-alpha5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce9484729b3e52c0bacdc5191cb6a6a5f31ef4c09c5e4ab1209d3340ad9e997b" +dependencies = [ + "bindgen", + "libc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "users" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4227e95324a443c9fcb06e03d4d85e91aabe9a5a02aa818688b6918b6af486" +dependencies = [ + "libc", + "log", +] + +[[package]] +name = "xcb" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4c580d8205abb0a5cf4eb7e927bd664e425b6c3263f9c5310583da96970cf6" +dependencies = [ + "bitflags 1.3.2", + "libc", + "quick-xml", +] + +[[package]] +name = "xkbcommon" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" +dependencies = [ + "libc", + "memmap2", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..5ac819c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "lockrs" +version = "0.1.0" +edition = "2024" + +[dependencies] +pam = "0.8.0" +xcb = "1.7.0" +xkbcommon = "0.9.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..5743b48 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# lockrs + +A minimal X11 lockscreen make with xcb and rust. diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..bde0457 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,152 @@ +use pam::Client; +use xcb::Xid; +use xcb::x; +use xkbcommon::xkb; + +fn get_username() -> String { + std::env::var("USER") + .or_else(|_| std::env::var("LOGNAME")) + .unwrap_or_else(|_| "root".to_string()) +} + +fn authenticate(username: &str, password: &str) -> bool { + let mut client = match Client::with_password("login") { + Ok(c) => c, + Err(_) => return false, + }; + client + .conversation_mut() + .set_credentials(username, password); + client.authenticate().is_ok() && client.open_session().is_ok() +} + +fn main() -> xcb::Result<()> { + let username = get_username(); + let mut password_buf = String::new(); + + // make keymap + let context = xkb::Context::new(xkb::CONTEXT_NO_FLAGS); + let keymap = xkb::Keymap::new_from_names( + &context, + "", // rules + "", // model + "", // layout + "", // variant + None, // options + xkb::KEYMAP_COMPILE_NO_FLAGS, + ) + .expect("Failed to create keymap"); + let mut state = xkb::State::new(&keymap); + + let (conn, screen_num) = xcb::Connection::connect(None)?; + + let setup = conn.get_setup(); + let screen = setup.roots().nth(screen_num as usize).unwrap(); + + let window: x::Window = conn.generate_id(); + + let cookie = conn.send_request_checked(&x::CreateWindow { + depth: x::COPY_FROM_PARENT as u8, + wid: window, + parent: screen.root(), + x: 0, + y: 0, + width: screen.width_in_pixels(), + height: screen.height_in_pixels(), + border_width: 0, + class: x::WindowClass::InputOutput, + visual: screen.root_visual(), + value_list: &[ + x::Cw::BackPixel(screen.black_pixel()), + x::Cw::OverrideRedirect(true), + x::Cw::EventMask( + x::EventMask::EXPOSURE + | x::EventMask::KEY_PRESS + | x::EventMask::BUTTON_PRESS + | x::EventMask::POINTER_MOTION, + ), + ], + }); + conn.check_request(cookie)?; + + conn.send_request(&x::MapWindow { window }); + conn.flush()?; + + let grab_keyboard = conn.send_request(&x::GrabKeyboard { + owner_events: false, + grab_window: window, + time: x::CURRENT_TIME, + pointer_mode: x::GrabMode::Async, + keyboard_mode: x::GrabMode::Async, + }); + + let reply = conn.wait_for_reply(grab_keyboard)?; + if reply.status() != x::GrabStatus::Success { + eprintln!("Failed to grab keyboard"); + return Ok(()); + } + + let grab_pointer = conn.send_request(&x::GrabPointer { + owner_events: false, + grab_window: window, + event_mask: x::EventMask::BUTTON_PRESS | x::EventMask::POINTER_MOTION, + pointer_mode: x::GrabMode::Async, + keyboard_mode: x::GrabMode::Async, + confine_to: x::Window::none(), + cursor: x::Cursor::none(), + time: x::CURRENT_TIME, + }); + + let reply = conn.wait_for_reply(grab_pointer)?; + if reply.status() != x::GrabStatus::Success { + eprintln!("Failed to grab pointer"); + return Ok(()); + } + + // We enter the main event loop + loop { + match conn.wait_for_event()? { + xcb::Event::X(x::Event::KeyPress(ev)) => { + let keycode = ev.detail(); + state.update_key(keycode.into(), xkb::KeyDirection::Down); + + let keysym = state.key_get_one_sym(keycode.into()); + let c = xkb::keysym_to_utf8(keysym); + match keycode { + 36 => { + if authenticate(&username, &password_buf) { + conn.send_request(&x::UngrabKeyboard { + time: x::CURRENT_TIME, + }); + conn.send_request(&x::UngrabPointer { + time: x::CURRENT_TIME, + }); + conn.flush()?; + break Ok(()); + } else { + eprintln!("Authentication failed"); + password_buf.clear(); + } + } + // Backspace (22) + 22 => { + password_buf.pop(); + } + // Escape (9) — clear buffer + 9 => { + password_buf.clear(); + } + + _ => { + password_buf.push_str(&c); + } + } + } + xcb::Event::X(x::Event::KeyRelease(ev)) => { + let keycode = ev.detail(); + state.update_key(keycode.into(), xkb::KeyDirection::Up); + } + _ => {} + } + } +} |
