Compare commits

...

2 commits

Author SHA1 Message Date
chayleaf aac6c64779
more porting 2024-08-14 20:01:37 +07:00
chayleaf 30e7205ebf
convert commands into strings 2024-08-14 11:28:19 +07:00
4 changed files with 779 additions and 260 deletions

329
Cargo.lock generated
View file

@ -47,7 +47,7 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
dependencies = [
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -57,9 +57,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
dependencies = [
"anstyle",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "arrayvec"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "autocfg"
version = "1.3.0"
@ -72,6 +78,19 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "brush-parser"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98bbbd29d06a11b0819fd28f076633bdb6e9943777fe0ed61201b283e7523f20"
dependencies = [
"indenter",
"peg",
"thiserror",
"tracing",
"utf8-chars",
]
[[package]]
name = "build-env"
version = "0.3.1"
@ -90,12 +109,39 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "colorchoice"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.48.0",
]
[[package]]
name = "dlib"
version = "0.5.2"
@ -111,6 +157,12 @@ version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
[[package]]
name = "either"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "env_filter"
version = "0.1.2"
@ -141,7 +193,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
@ -150,12 +213,27 @@ version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "indenter"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itertools"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.155"
@ -169,7 +247,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4"
dependencies = [
"cfg-if",
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags",
"libc",
]
[[package]]
@ -210,6 +298,63 @@ dependencies = [
"autocfg",
]
[[package]]
name = "nix"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
dependencies = [
"bitflags",
"cfg-if",
"cfg_aliases",
"libc",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "peg"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "295283b02df346d1ef66052a757869b2876ac29a6bb0ac3f5f7cd44aebe40e8f"
dependencies = [
"peg-macros",
"peg-runtime",
]
[[package]]
name = "peg-macros"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdad6a1d9cf116a059582ce415d5f5566aabcd4008646779dab7fdc2a9a9d426"
dependencies = [
"peg-runtime",
"proc-macro2",
"quote",
]
[[package]]
name = "peg-runtime"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3aeb8f54c078314c2065ee649a7241f46b9d8e418e1a9581ba0546657d7aa3a"
[[package]]
name = "pin-project-lite"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02"
[[package]]
name = "pkg-config"
version = "0.3.30"
@ -243,6 +388,17 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_users"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]]
name = "regex"
version = "1.10.6"
@ -282,7 +438,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.52.0",
]
[[package]]
@ -301,26 +457,107 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
name = "swayidle-rs"
version = "0.1.0"
dependencies = [
"brush-parser",
"dirs",
"env_logger",
"itertools",
"libc",
"libsystemd-sys",
"log",
"nix",
"wayland-client",
"wayland-sys",
]
[[package]]
name = "syn"
version = "2.0.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing"
version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8-chars"
version = "3.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb8b618e8c34f77c00710bbe6adaff80f3c856296be49241071e1aa08df45cec"
dependencies = [
"arrayvec",
]
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wayland-backend"
version = "0.3.6"
@ -372,13 +609,37 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
@ -387,28 +648,46 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -421,24 +700,48 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

View file

@ -6,9 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
brush-parser = "0.2.5"
dirs = "5.0.1"
env_logger = "0.11.5"
itertools = "0.13.0"
libc = "0.2.155"
libsystemd-sys = "0.9.3"
log = "0.4.22"
nix = { version = "0.29.0", features = ["fs"] }
wayland-client = { version = "0.31.5", features = ["log"] }
wayland-sys = { version = "0.31.4", features = ["client", "server"] }

228
src/expand.rs Normal file
View file

@ -0,0 +1,228 @@
use brush_parser::word::WordPiece;
use itertools::Itertools;
fn expand_backslash_escapes(s: &str) -> Result<(Vec<u8>, bool), ()> {
let mut result: Vec<u8> = vec![];
let mut it = s.chars();
while let Some(c) = it.next() {
if c != '\\' {
// Not a backslash, add and move on.
result.append(c.to_string().into_bytes().as_mut());
continue;
}
match it.next() {
Some('a') => result.push(b'\x07'),
Some('b') => result.push(b'\x08'),
Some('c') => {
if let Some(_next_next) = it.next() {
// "control character in ANSI C quotes"
return Err(());
} else {
result.push(b'\\');
result.push(b'c');
}
}
Some('e' | 'E') => result.push(b'\x1b'),
Some('f') => result.push(b'\x0c'),
Some('n') => result.push(b'\n'),
Some('r') => result.push(b'\r'),
Some('t') => result.push(b'\t'),
Some('v') => result.push(b'\x0b'),
Some('\\') => result.push(b'\\'),
Some('\'') => result.push(b'\''),
Some('\"') => result.push(b'\"'),
Some('?') => result.push(b'?'),
Some('0') => {
// Consume 0-3 valid octal chars
let mut taken_so_far = 0;
let mut octal_chars: String = it
.take_while_ref(|c| {
if taken_so_far < 3 && matches!(*c, '0'..='7') {
taken_so_far += 1;
true
} else {
false
}
})
.collect();
if octal_chars.is_empty() {
octal_chars.push('0');
}
let value = u8::from_str_radix(octal_chars.as_str(), 8).map_err(|_| ())?;
result.push(value);
}
Some('x') => {
// Consume 1-2 valid hex chars
let mut taken_so_far = 0;
let hex_chars: String = it
.take_while_ref(|c| {
if taken_so_far < 2 && c.is_ascii_hexdigit() {
taken_so_far += 1;
true
} else {
false
}
})
.collect();
if hex_chars.is_empty() {
result.push(b'\\');
result.append(c.to_string().into_bytes().as_mut());
} else {
let value = u8::from_str_radix(hex_chars.as_str(), 16).map_err(|_| ())?;
result.push(value);
}
}
Some('u') => {
// Consume 1-4 hex digits
let mut taken_so_far = 0;
let hex_chars: String = it
.take_while_ref(|c| {
if taken_so_far < 4 && c.is_ascii_hexdigit() {
taken_so_far += 1;
true
} else {
false
}
})
.collect();
if hex_chars.is_empty() {
result.push(b'\\');
result.append(c.to_string().into_bytes().as_mut());
} else {
let value = u16::from_str_radix(hex_chars.as_str(), 16).map_err(|_| ())?;
if let Some(decoded) = char::from_u32(u32::from(value)) {
result.append(decoded.to_string().into_bytes().as_mut());
} else {
result.push(b'\\');
result.append(c.to_string().into_bytes().as_mut());
}
}
}
Some('U') => {
// Consume 1-8 hex digits
let mut taken_so_far = 0;
let hex_chars: String = it
.take_while_ref(|c| {
if taken_so_far < 8 && c.is_ascii_hexdigit() {
taken_so_far += 1;
true
} else {
false
}
})
.collect();
if hex_chars.is_empty() {
result.push(b'\\');
result.append(c.to_string().into_bytes().as_mut());
} else {
let value = u32::from_str_radix(hex_chars.as_str(), 16).map_err(|_| ())?;
if let Some(decoded) = char::from_u32(value) {
result.append(decoded.to_string().into_bytes().as_mut());
} else {
result.push(b'\\');
result.append(c.to_string().into_bytes().as_mut());
}
}
}
Some(c) => {
// Not a valid escape sequence.
result.push(b'\\');
result.append(c.to_string().into_bytes().as_mut());
}
None => {
// Trailing backslash.
result.push(b'\\');
}
}
}
Ok((result, true))
}
fn expand_tilde_expression(prefix: &str) -> Result<String, ()> {
if !prefix.is_empty() {
// "expansion: complex tilde expression"
return Err(());
}
if let Some(home_dir) = dirs::home_dir() {
Ok(home_dir.to_string_lossy().to_string())
} else {
Err(())
}
}
fn expand_word_piece(word_piece: WordPiece) -> Result<(Vec<String>, bool), ()> {
let mut cat = true;
let expansion: Vec<String> = match word_piece {
brush_parser::word::WordPiece::Text(s) => vec![s],
brush_parser::word::WordPiece::SingleQuotedText(s) => vec![s],
brush_parser::word::WordPiece::AnsiCQuotedText(s) => {
let (expanded, _) = expand_backslash_escapes(s.as_str())?;
vec![String::from_utf8_lossy(expanded.as_slice()).into_owned()]
}
brush_parser::word::WordPiece::DoubleQuotedSequence(pieces) => {
let mut fields: Vec<String> = vec![];
let pieces_is_empty = pieces.is_empty();
for piece in pieces {
let (this_fields, concatenate) = expand_word_piece(piece)?;
if concatenate {
fields.push(this_fields.join(" "));
} else {
fields.extend(this_fields);
};
}
// If there were no pieces, then make sure we yield a single field containing an
// empty, unsplittable string.
if pieces_is_empty {
fields.push(String::new());
}
cat = false;
fields
}
brush_parser::word::WordPiece::TildePrefix(prefix) => {
vec![expand_tilde_expression(prefix.as_str())?]
}
brush_parser::word::WordPiece::ParameterExpansion(_) => return Err(()),
brush_parser::word::WordPiece::CommandSubstitution(_) => return Err(()),
brush_parser::word::WordPiece::EscapeSequence(s) => {
let expanded = s.strip_prefix('\\').unwrap();
vec![expanded.to_owned()]
}
brush_parser::word::WordPiece::ArithmeticExpression(_) => return Err(()),
};
Ok((expansion, cat))
}
pub fn wordexp(cmd: &str) -> Result<Vec<String>, ()> {
let frags = brush_parser::word::parse(
cmd,
&brush_parser::ParserOptions {
enable_extended_globbing: false,
posix_mode: false,
sh_mode: false,
tilde_expansion: true,
},
)
.map_err(|_| ())?;
Ok(frags
.into_iter()
.map(|x| Ok(expand_word_piece(x)?.0))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect())
}

View file

@ -4,15 +4,24 @@
non_upper_case_globals,
clippy::missing_safety_doc
)]
use std::{borrow::Cow, env, ffi::CStr, mem, mem::MaybeUninit, os::raw::c_void, process, ptr};
use std::{
borrow::Cow,
env,
ffi::{CStr, CString},
fs::File,
io::{BufRead, BufReader},
mem::{self, MaybeUninit},
os::raw::c_void,
path::Path,
process, ptr,
};
use ext_idle_notify_v1_protocol::{
ext_idle_notification_v1_interface, ext_idle_notifier_v1_interface,
};
use libc::{
__errno_location, abort, access, calloc, close, execvp, exit, fclose, fopen, fork, free,
getenv, getline, getopt, getpid, memset, printf, sigemptyset, signal, sigprocmask, sigset_t,
size_t, sprintf, strcmp, strdup, strlen, strncmp, strstr, strtoul, waitpid,
__errno_location, abort, calloc, close, execvp, exit, fork, getenv, getopt, getpid,
sigemptyset, signal, sigprocmask, sigset_t, size_t, sprintf, strcmp, strdup, strstr, waitpid,
};
use libsystemd_sys::bus::{
sd_bus, sd_bus_call_method, sd_bus_default_system, sd_bus_error, sd_bus_error_free,
@ -21,6 +30,7 @@ use libsystemd_sys::bus::{
sd_bus_message_read, sd_bus_message_read_basic, sd_bus_message_skip, sd_bus_message_unref,
sd_bus_process, sd_bus_slot,
};
use nix::unistd::AccessFlags;
use wayland_client::protocol::__interfaces::{wl_registry_interface, wl_seat_interface};
use wayland_sys::{
client::{
@ -36,6 +46,7 @@ use wayland_sys::{
},
};
mod expand;
mod ext_idle_notify_v1_protocol;
#[repr(C)]
@ -68,12 +79,6 @@ extern "C" {
flags: u32,
_: ...
) -> *mut wl_proxy;
fn wordexp(
__words: *const libc::c_char,
__pwordexp: *mut wordexp_t,
__flags: libc::c_int,
) -> libc::c_int;
fn wordfree(__wordexp: *mut wordexp_t);
fn sd_bus_match_signal(
bus_0: *mut sd_bus,
ret: *mut *mut sd_bus_slot,
@ -111,13 +116,6 @@ const WL_EVENT_WRITABLE: u32 = 2;
const WL_EVENT_READABLE: u32 = 1;
#[derive(Copy, Clone)]
#[repr(C)]
struct wordexp_t {
we_wordc: size_t,
we_wordv: *mut *mut libc::c_char,
we_offs: size_t,
}
#[derive(Copy, Clone)]
#[repr(C)]
struct ext_idle_notification_v1_listener {
idled: Option<unsafe extern "C" fn(*mut libc::c_void, *mut ext_idle_notification_v1) -> ()>,
resumed: Option<unsafe extern "C" fn(*mut libc::c_void, *mut ext_idle_notification_v1) -> ()>,
@ -134,11 +132,11 @@ struct swayidle_state {
event_loop: *mut wl_event_loop,
timeout_cmds: wl_list,
seats: wl_list,
seat_name: *mut libc::c_char,
before_sleep_cmd: *mut libc::c_char,
after_resume_cmd: *mut libc::c_char,
logind_lock_cmd: *mut libc::c_char,
logind_unlock_cmd: *mut libc::c_char,
seat_name: Option<String>,
before_sleep_cmd: Option<String>,
after_resume_cmd: Option<String>,
logind_lock_cmd: Option<String>,
logind_unlock_cmd: Option<String>,
logind_idlehint: bool,
timeouts_enabled: bool,
wait: bool,
@ -149,8 +147,8 @@ struct swayidle_timeout_cmd {
timeout: libc::c_int,
registered_timeout: libc::c_int,
idle_notification: *mut ext_idle_notification_v1,
idle_cmd: *mut libc::c_char,
resume_cmd: *mut libc::c_char,
idle_cmd: Option<String>,
resume_cmd: Option<String>,
idlehint: bool,
resume_pending: bool,
}
@ -162,7 +160,7 @@ struct seat {
capabilities: u32,
}
#[inline]
unsafe extern "C" fn wl_display_get_registry(wl_display: *mut wl_display) -> *mut wl_registry {
unsafe fn wl_display_get_registry(wl_display: *mut wl_display) -> *mut wl_registry {
wl_proxy_marshal_flags(
wl_display.cast(),
1,
@ -174,7 +172,7 @@ unsafe extern "C" fn wl_display_get_registry(wl_display: *mut wl_display) -> *mu
.cast()
}
#[inline]
unsafe extern "C" fn wl_registry_add_listener(
unsafe fn wl_registry_add_listener(
wl_registry: *mut wl_registry,
listener: *mut wl_registry_listener,
data: *mut libc::c_void,
@ -182,7 +180,7 @@ unsafe extern "C" fn wl_registry_add_listener(
wl_proxy_add_listener(wl_registry.cast(), listener.cast(), data)
}
#[inline]
unsafe extern "C" fn wl_registry_bind(
unsafe fn wl_registry_bind(
wl_registry: *mut wl_registry,
name: u32,
interface: *const wl_interface,
@ -202,7 +200,7 @@ unsafe extern "C" fn wl_registry_bind(
.cast()
}
#[inline]
unsafe extern "C" fn wl_seat_add_listener(
unsafe fn wl_seat_add_listener(
wl_seat: *mut wl_seat,
listener: *const wl_seat_listener,
data: *mut libc::c_void,
@ -210,7 +208,7 @@ unsafe extern "C" fn wl_seat_add_listener(
wl_proxy_add_listener(wl_seat.cast(), listener.cast_mut().cast(), data)
}
#[inline]
unsafe extern "C" fn ext_idle_notifier_v1_get_idle_notification(
unsafe fn ext_idle_notifier_v1_get_idle_notification(
ext_idle_notifier_v1: *mut ext_idle_notifier_v1,
timeout: u32,
seat_0: *mut wl_seat,
@ -228,7 +226,7 @@ unsafe extern "C" fn ext_idle_notifier_v1_get_idle_notification(
.cast()
}
#[inline]
unsafe extern "C" fn ext_idle_notification_v1_add_listener(
unsafe fn ext_idle_notification_v1_add_listener(
ext_idle_notification_v1: *mut ext_idle_notification_v1,
listener: *const ext_idle_notification_v1_listener,
data: *mut libc::c_void,
@ -240,7 +238,7 @@ unsafe extern "C" fn ext_idle_notification_v1_add_listener(
)
}
#[inline]
unsafe extern "C" fn ext_idle_notification_v1_destroy(
unsafe fn ext_idle_notification_v1_destroy(
ext_idle_notification_v1: *mut ext_idle_notification_v1,
) {
wl_proxy_marshal_flags(
@ -253,7 +251,6 @@ unsafe extern "C" fn ext_idle_notification_v1_destroy(
}
static mut idle_notifier: *mut ext_idle_notifier_v1 = ptr::null_mut();
static mut seat: *mut wl_seat = ptr::null_mut();
#[no_mangle]
static mut state: swayidle_state = swayidle_state {
display: ptr::null_mut(),
event_loop: ptr::null_mut(),
@ -265,17 +262,16 @@ static mut state: swayidle_state = swayidle_state {
prev: ptr::null_mut(),
next: ptr::null_mut(),
},
seat_name: ptr::null_mut(),
before_sleep_cmd: ptr::null_mut(),
after_resume_cmd: ptr::null_mut(),
logind_lock_cmd: ptr::null_mut(),
logind_unlock_cmd: ptr::null_mut(),
seat_name: None,
before_sleep_cmd: None,
after_resume_cmd: None,
logind_lock_cmd: None,
logind_unlock_cmd: None,
logind_idlehint: false,
timeouts_enabled: false,
wait: false,
};
#[no_mangle]
unsafe extern "C" fn swayidle_log_init(verbosity: log_importance) {
unsafe fn swayidle_log_init(verbosity: log_importance) {
env_logger::builder()
.filter(
None,
@ -290,7 +286,6 @@ unsafe extern "C" fn swayidle_log_init(verbosity: log_importance) {
}
impl swayidle_state {
unsafe fn init(&mut self) {
memset(ptr::addr_of_mut!(*self).cast(), 0, mem::size_of::<Self>());
wl_list_init(&mut self.timeout_cmds);
wl_list_init(&mut self.seats);
}
@ -299,22 +294,18 @@ impl swayidle_state {
let mut tmp: *mut swayidle_timeout_cmd = (*cmd).link.next.cast();
while ptr::addr_of_mut!((*cmd).link) != ptr::addr_of_mut!(self.timeout_cmds) {
wl_list_remove(&mut (*cmd).link);
free((*cmd).idle_cmd.cast());
free((*cmd).resume_cmd.cast());
free(cmd.cast());
drop(Box::from_raw(cmd));
cmd = tmp;
tmp = (*cmd).link.next.cast();
}
free(self.after_resume_cmd.cast());
free(self.before_sleep_cmd.cast());
}
unsafe fn sway_terminate(&self, exit_code: libc::c_int) -> ! {
wl_display_disconnect(self.display);
wl_event_loop_destroy(self.event_loop);
exit(exit_code);
}
unsafe fn cmd_exec(&self, param: *mut libc::c_char) {
log::debug!("Cmd exec {}", read_str(param));
unsafe fn cmd_exec(&self, param: &str) {
log::debug!("Cmd exec {param}");
let mut pid = fork();
match pid {
0 => {
@ -330,10 +321,11 @@ impl swayidle_state {
signal(2, 0);
signal(15, 0);
signal(10, 0);
let param = CString::new(param).unwrap();
let cmd: [*const libc::c_char; 4] = [
b"sh\0".as_ptr().cast(),
b"-c\0".as_ptr().cast(),
param,
param.as_ptr(),
ptr::null(),
];
execvp(cmd[0], cmd.as_ptr());
@ -352,7 +344,7 @@ impl swayidle_state {
log::error!("fork failed: {}", strerror(*__errno_location()),);
}
_ => {
log::debug!("Spawned process {}", read_str(param));
log::debug!("Spawned process {param}");
if self.wait {
log::debug!("Blocking until process exits");
}
@ -364,7 +356,7 @@ impl swayidle_state {
}
};
}
unsafe extern "C" fn connect_to_bus(&self) {
unsafe fn connect_to_bus(&self) {
let ret = sd_bus_default_system(ptr::addr_of_mut!(bus));
if ret < 0 {
*__errno_location() = -ret;
@ -440,7 +432,7 @@ impl swayidle_state {
&mut self,
argc: usize,
argv: *mut *mut libc::c_char,
config_path: *mut *mut libc::c_char,
config_path: &mut Option<String>,
) -> libc::c_int {
let mut c: libc::c_int;
loop {
@ -448,55 +440,65 @@ impl swayidle_state {
if c == -1 {
break;
}
match c {
67 => {
free((*config_path).cast());
*config_path = strdup(optarg);
match c as u8 {
b'C' => {
*config_path = Some(read_str(optarg).into());
}
100 => {
b'd' => {
swayidle_log_init(LOG_DEBUG);
}
119 => {
b'w' => {
self.wait = 1 != 0;
}
83 => {
self.seat_name = strdup(optarg);
b'S' => {
self.seat_name = read_str2(optarg);
}
104 | 63 => {
printf(b"Usage: %s [OPTIONS]\n\0".as_ptr().cast(), *argv);
printf(b" -h\tthis help menu\n\0".as_ptr().cast());
printf(b" -C\tpath to config file\n\0".as_ptr().cast());
printf(b" -d\tdebug\n\0".as_ptr().cast());
printf(b" -w\twait for command to finish\n\0".as_ptr().cast());
printf(b" -S\tpick the seat to work with\n\0".as_ptr().cast());
b'h' | b'?' => {
println!("Usage: {} [OPTIONS]", read_str(*argv));
println!(" -h\tthis help menu");
println!(" -C\tpath to config file");
println!(" -d\tdebug");
println!(" -w\twait for command to finish");
println!(" -S\tpick the seat to work with");
return 1;
}
_ => return 1,
}
}
let mut i = optind as usize;
while i < argc {
if strcmp(b"timeout\0".as_ptr().cast(), *argv.add(i)) == 0 {
log::debug!("Got timeout");
i += self.parse_timeout(argc - i, &mut *argv.add(i));
} else if strcmp(b"before-sleep\0".as_ptr().cast(), *argv.add(i)) == 0 {
log::debug!("Got before-sleep");
i += self.parse_sleep(argc - i, &mut *argv.add(i));
} else if strcmp(b"after-resume\0".as_ptr().cast(), *argv.add(i)) == 0 {
log::debug!("Got after-resume");
i += self.parse_resume(argc - i, &mut *argv.add(i));
} else if strcmp(b"lock\0".as_ptr().cast(), *argv.add(i)) == 0 {
log::debug!("Got lock");
i += self.parse_lock(argc - i, &mut *argv.add(i));
} else if strcmp(b"unlock\0".as_ptr().cast(), *argv.add(i)) == 0 {
log::debug!("Got unlock");
i += self.parse_unlock(argc - i, &mut *argv.add(i));
} else if strcmp(b"idlehint\0".as_ptr().cast(), *argv.add(i)) == 0 {
log::debug!("Got idlehint");
i += self.parse_idlehint(argc - i, &mut *argv.add(i));
} else {
log::error!("Unsupported command '{}'", read_str(*argv.add(i)));
return 1;
let args = (optind as usize..argc)
.map(|i| read_str(*argv.add(i)).into_owned())
.collect::<Vec<_>>();
let mut i = 0;
while i < args.len() {
match args[i].as_str() {
"timeout" => {
log::debug!("Got timeout");
i += self.parse_timeout(&args[i..]);
}
"before-sleep" => {
log::debug!("Got before-sleep");
i += self.parse_sleep(&args[i..]);
}
"after-resume" => {
log::debug!("Got after-resume");
i += self.parse_resume(&args[i..]);
}
"lock" => {
log::debug!("Got lock");
i += self.parse_lock(&args[i..]);
}
"unlock" => {
log::debug!("Got unlock");
i += self.parse_unlock(&args[i..]);
}
"idlehint" => {
log::debug!("Got idlehint");
i += self.parse_idlehint(&args[i..]);
}
_ => {
log::error!("Unsupported command '{}'", read_str(*argv.add(i)));
return 1;
}
}
}
0
@ -518,16 +520,16 @@ impl swayidle_state {
b"delay\0".as_ptr().cast(),
ptr::addr_of_mut!(sleep_lock_fd),
);
if !self.after_resume_cmd.is_null() {
self.cmd_exec(self.after_resume_cmd);
if let Some(after_resume_cmd) = &self.after_resume_cmd {
self.cmd_exec(after_resume_cmd);
}
if self.logind_idlehint {
set_idle_hint(false);
}
return 0;
}
if !self.before_sleep_cmd.is_null() {
self.cmd_exec(self.before_sleep_cmd);
if let Some(before_sleep_cmd) = &self.before_sleep_cmd {
self.cmd_exec(before_sleep_cmd);
}
log::debug!("Prepare for sleep done");
release_inhibitor_lock(sleep_lock_fd);
@ -535,8 +537,8 @@ impl swayidle_state {
}
unsafe fn handle_lock(&self) -> libc::c_int {
log::debug!("Lock signal received");
if !self.logind_lock_cmd.is_null() {
self.cmd_exec(self.logind_lock_cmd);
if let Some(logind_lock_cmd) = &self.logind_lock_cmd {
self.cmd_exec(logind_lock_cmd);
}
log::debug!("Lock command done");
0
@ -546,8 +548,8 @@ impl swayidle_state {
if self.logind_idlehint {
set_idle_hint(false);
}
if !self.logind_unlock_cmd.is_null() {
self.cmd_exec(self.logind_unlock_cmd);
if let Some(logind_unlock_cmd) = &self.logind_unlock_cmd {
self.cmd_exec(logind_unlock_cmd);
}
log::debug!("Unlock command done");
0
@ -590,7 +592,7 @@ impl swayidle_state {
cmd = (*cmd).link.next.cast();
}
}
unsafe extern "C" fn disable_timeouts(&mut self) {
unsafe fn disable_timeouts(&mut self) {
if !self.timeouts_enabled {
return;
}
@ -606,20 +608,20 @@ impl swayidle_state {
set_idle_hint(false);
}
}
unsafe fn parse_timeout(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize {
if argc < 3 {
unsafe fn parse_timeout(&mut self, args: &[String]) -> usize {
if args.len() < 3 {
log::error!(
"Too few parameters to timeout command. Usage: timeout <seconds> <command>"
);
exit(-1);
}
let cmd = build_timeout_cmd(argv);
let cmd = build_timeout_cmd(args);
log::debug!("Register idle timeout at {} ms", (*cmd).timeout,);
log::debug!("Setup idle");
(*cmd).idle_cmd = parse_command(argc - 2, &mut *argv.offset(2));
let result = if argc >= 5 && strcmp(b"resume\0".as_ptr().cast(), *argv.offset(3)) == 0 {
(*cmd).idle_cmd = parse_command(&args[2..]);
let result = if args.len() >= 5 && args[3] == "resume" {
log::debug!("Setup resume");
(*cmd).resume_cmd = parse_command(argc - 4, &mut *argv.offset(4));
(*cmd).resume_cmd = parse_command(&args[4..]);
5
} else {
3
@ -627,123 +629,111 @@ impl swayidle_state {
wl_list_insert(&mut self.timeout_cmds, &mut (*cmd).link);
result
}
unsafe fn parse_sleep(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize {
if argc < 2 {
unsafe fn parse_sleep(&mut self, args: &[String]) -> usize {
if args.len() < 2 {
log::error!(
"Too few parameters to before-sleep command. Usage: before-sleep <command>"
);
exit(-1);
}
self.before_sleep_cmd = parse_command(argc - 1, &mut *argv.offset(1));
if !self.before_sleep_cmd.is_null() {
log::debug!("Setup sleep lock: {}", read_str(self.before_sleep_cmd));
self.before_sleep_cmd = parse_command(&args[1..]);
if let Some(before_sleep_cmd) = &self.before_sleep_cmd {
log::debug!("Setup sleep lock: {before_sleep_cmd}");
}
2
}
unsafe fn parse_resume(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize {
if argc < 2 {
unsafe fn parse_resume(&mut self, args: &[String]) -> usize {
if args.len() < 2 {
log::error!(
"Too few parameters to after-resume command. Usage: after-resume <command>"
);
exit(-1);
}
self.after_resume_cmd = parse_command(argc - 1, &mut *argv.offset(1));
if !self.after_resume_cmd.is_null() {
log::debug!("Setup resume hook: {}", read_str(self.after_resume_cmd));
self.after_resume_cmd = parse_command(&args[1..]);
if let Some(after_resume_cmd) = &self.after_resume_cmd {
log::debug!("Setup resume hook: {after_resume_cmd}");
}
2
}
unsafe fn parse_lock(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize {
if argc < 2 {
unsafe fn parse_lock(&mut self, args: &[String]) -> usize {
if args.len() < 2 {
log::error!("Too few parameters to lock command. Usage: lock <command>");
exit(-1);
}
self.logind_lock_cmd = parse_command(argc - 1, &mut *argv.offset(1));
if !self.logind_lock_cmd.is_null() {
log::debug!("Setup lock hook: {}", read_str(self.logind_lock_cmd));
self.logind_lock_cmd = parse_command(&args[1..]);
if let Some(logind_lock_cmd) = &self.logind_lock_cmd {
log::debug!("Setup lock hook: {logind_lock_cmd}");
}
2
}
unsafe fn parse_unlock(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize {
if argc < 2 {
unsafe fn parse_unlock(&mut self, args: &[String]) -> usize {
if args.len() < 2 {
log::error!("Too few parameters to unlock command. Usage: unlock <command>");
exit(-1);
}
self.logind_unlock_cmd = parse_command(argc - 1, &mut *argv.offset(1));
if !self.logind_unlock_cmd.is_null() {
log::debug!("Setup unlock hook: {}", read_str(self.logind_unlock_cmd));
self.logind_unlock_cmd = parse_command(&args[1..]);
if let Some(logind_unlock_cmd) = &self.logind_unlock_cmd {
log::debug!("Setup unlock hook: {logind_unlock_cmd}");
}
2
}
unsafe fn parse_idlehint(&mut self, argc: usize, argv: *mut *mut libc::c_char) -> usize {
unsafe fn parse_idlehint(&mut self, args: &[String]) -> usize {
if self.logind_idlehint {
log::error!("Cannot add multiple idlehint events");
exit(-1);
}
if argc < 2 {
if args.len() < 2 {
log::error!("Too few parameters to idlehint command. Usage: idlehint <seconds>");
exit(-1);
}
let cmd: *mut swayidle_timeout_cmd = build_timeout_cmd(argv);
let cmd: *mut swayidle_timeout_cmd = build_timeout_cmd(args);
(*cmd).idlehint = true;
log::debug!("Register idlehint timeout at {} ms", (*cmd).timeout,);
wl_list_insert(&mut self.timeout_cmds, &mut (*cmd).link);
self.logind_idlehint = true;
2
}
unsafe extern "C" fn load_config(&mut self, config_path: *const libc::c_char) -> i32 {
let f = fopen(config_path, b"r\0".as_ptr().cast());
if f.is_null() {
unsafe fn load_config(&mut self, config_path: &Path) -> i32 {
let Ok(file) = File::open(config_path) else {
return -2;
}
};
let mut file = BufReader::new(file);
let mut lineno: size_t = 0;
let mut line = ptr::null_mut();
let mut n = 0;
let mut nread;
let mut line = String::new();
loop {
nread = getline(&mut line, &mut n, f);
if nread == -1 {
line.clear();
let Ok(nread) = file.read_line(&mut line) else {
break;
};
if nread == 0 {
break;
}
lineno = lineno.wrapping_add(1);
if *line.offset(nread - 1) as u8 == b'\n' {
*line.offset(nread - 1) = 0;
}
if strlen(line) == 0 || *line as u8 == b'#' {
let line = line.strip_suffix('\n').unwrap_or(&line);
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut i = 0;
while *line.add(i) as u8 != b'\0' && *line.add(i) as u8 != b' ' {
i = i.wrapping_add(1);
}
let mut p = wordexp_t {
we_wordc: 0,
we_wordv: ptr::null_mut(),
we_offs: 0,
};
wordexp(line, &mut p, 0);
if strncmp(b"timeout\0".as_ptr().cast(), line, i) == 0 {
self.parse_timeout(p.we_wordc, p.we_wordv);
} else if strncmp(b"before-sleep\0".as_ptr().cast(), line, i) == 0 {
self.parse_sleep(p.we_wordc, p.we_wordv);
} else if strncmp(b"after-resume\0".as_ptr().cast(), line, i) == 0 {
self.parse_resume(p.we_wordc, p.we_wordv);
} else if strncmp(b"lock\0".as_ptr().cast(), line, i) == 0 {
self.parse_lock(p.we_wordc, p.we_wordv);
} else if strncmp(b"unlock\0".as_ptr().cast(), line, i) == 0 {
self.parse_unlock(p.we_wordc, p.we_wordv);
} else if strncmp(b"idlehint\0".as_ptr().cast(), line, i) == 0 {
self.parse_idlehint(p.we_wordc, p.we_wordv);
let (cmd, args) = if let Some((a, b)) = line.split_once(' ') {
(a, Some(b))
} else {
*line.add(i) = 0;
log::error!("Unexpected keyword {} in line {lineno}", read_str(line));
free(line.cast::<libc::c_void>());
return -22;
}
wordfree(&mut p);
(line, None)
};
let args = args
.map(|args| expand::wordexp(args).unwrap())
.unwrap_or_default();
match cmd {
"timeout" => self.parse_timeout(&args),
"before-sleep" => self.parse_sleep(&args),
"after-resume" => self.parse_resume(&args),
"lock" => self.parse_lock(&args),
"unlock" => self.parse_unlock(&args),
"idlehint" => self.parse_idlehint(&args),
_ => {
log::error!("Unexpected keyword {cmd} in line {lineno}");
return -22;
}
};
}
free(line.cast());
fclose(f);
0
}
}
@ -752,6 +742,13 @@ impl Drop for swayidle_state {
unsafe { self.finish() }
}
}
unsafe fn read_str2(ptr: *const libc::c_char) -> Option<String> {
if ptr.is_null() {
None
} else {
Some(CStr::from_ptr(ptr).to_string_lossy().into())
}
}
unsafe fn read_str(ptr: *const libc::c_char) -> Cow<'static, str> {
if ptr.is_null() {
"".into()
@ -762,7 +759,7 @@ unsafe fn read_str(ptr: *const libc::c_char) -> Cow<'static, str> {
static mut sleep_lock_fd: libc::c_int = -1;
static mut bus: *mut sd_bus = ptr::null_mut();
static mut session_name: *mut libc::c_char = ptr::null_mut();
unsafe extern "C" fn acquire_inhibitor_lock(
unsafe fn acquire_inhibitor_lock(
type_0: *const libc::c_char,
mode: *const libc::c_char,
fd: *mut libc::c_int,
@ -824,13 +821,13 @@ unsafe extern "C" fn acquire_inhibitor_lock(
sd_bus_error_free(&mut error);
sd_bus_message_unref(msg);
}
unsafe extern "C" fn release_inhibitor_lock(fd: libc::c_int) {
unsafe fn release_inhibitor_lock(fd: libc::c_int) {
if fd >= 0 {
log::debug!("Releasing inhibitor lock {}", fd);
close(fd);
}
}
unsafe extern "C" fn set_idle_hint(hint: bool) {
unsafe fn set_idle_hint(hint: bool) {
log::debug!("SetIdleHint {}", i32::from(hint));
let mut msg = ptr::null_mut();
let mut error = sd_bus_error {
@ -858,7 +855,7 @@ unsafe extern "C" fn set_idle_hint(hint: bool) {
sd_bus_error_free(&mut error);
sd_bus_message_unref(msg);
}
unsafe extern "C" fn get_logind_idle_inhibit() -> bool {
unsafe fn get_logind_idle_inhibit() -> bool {
let mut locks: *const libc::c_char = ptr::null();
let res: bool;
let mut reply = ptr::null_mut();
@ -1021,7 +1018,7 @@ unsafe extern "C" fn dbus_event(
}
count
}
unsafe extern "C" fn set_session() {
unsafe fn set_session() {
let mut is_error = false;
let mut msg = ptr::null_mut();
let mut error = sd_bus_error {
@ -1078,7 +1075,7 @@ unsafe extern "C" fn set_session() {
sd_bus_error_free(&mut error);
sd_bus_message_unref(msg);
}
unsafe extern "C" fn setup_sleep_listener() {
unsafe fn setup_sleep_listener() {
let ret = sd_bus_match_signal(
bus,
ptr::null_mut(),
@ -1103,7 +1100,7 @@ unsafe extern "C" fn setup_sleep_listener() {
ptr::addr_of_mut!(sleep_lock_fd),
);
}
unsafe extern "C" fn setup_lock_listener() {
unsafe fn setup_lock_listener() {
let ret = sd_bus_match_signal(
bus,
ptr::null_mut(),
@ -1122,7 +1119,7 @@ unsafe extern "C" fn setup_lock_listener() {
);
}
}
unsafe extern "C" fn setup_unlock_listener() {
unsafe fn setup_unlock_listener() {
let ret = sd_bus_match_signal(
bus,
ptr::null_mut(),
@ -1141,7 +1138,7 @@ unsafe extern "C" fn setup_unlock_listener() {
);
}
}
unsafe extern "C" fn setup_property_changed_listener() {
unsafe fn setup_property_changed_listener() {
let ret = sd_bus_match_signal(
bus,
ptr::null_mut(),
@ -1199,13 +1196,13 @@ static mut registry_listener: wl_registry_listener = wl_registry_listener {
global: Some(handle_global),
global_remove: Some(handle_global_remove),
};
unsafe extern "C" fn destroy_cmd_timer(cmd: *mut swayidle_timeout_cmd) {
unsafe fn destroy_cmd_timer(cmd: *mut swayidle_timeout_cmd) {
if !(*cmd).idle_notification.is_null() {
ext_idle_notification_v1_destroy((*cmd).idle_notification);
(*cmd).idle_notification = ptr::null_mut();
}
}
unsafe extern "C" fn register_timeout(cmd: *mut swayidle_timeout_cmd, timeout: libc::c_int) {
unsafe fn register_timeout(cmd: *mut swayidle_timeout_cmd, timeout: libc::c_int) {
destroy_cmd_timer(cmd);
if timeout < 0 {
log::debug!("Not registering idle timeout");
@ -1227,8 +1224,8 @@ unsafe extern "C" fn handle_idled(data: *mut libc::c_void, _notif: *mut ext_idle
log::debug!("idle state");
if (*cmd).idlehint {
set_idle_hint(true);
} else if !(*cmd).idle_cmd.is_null() {
state.cmd_exec((*cmd).idle_cmd);
} else if let Some(idle_cmd) = &(*cmd).idle_cmd {
state.cmd_exec(idle_cmd);
}
}
unsafe extern "C" fn handle_resumed(
@ -1243,8 +1240,8 @@ unsafe extern "C" fn handle_resumed(
}
if (*cmd).idlehint {
set_idle_hint(false);
} else if !(*cmd).resume_cmd.is_null() {
state.cmd_exec((*cmd).resume_cmd);
} else if let Some(resume_cmd) = &(*cmd).resume_cmd {
state.cmd_exec(resume_cmd);
}
}
static mut idle_notification_listener: ext_idle_notification_v1_listener =
@ -1252,35 +1249,37 @@ static mut idle_notification_listener: ext_idle_notification_v1_listener =
idled: Some(handle_idled),
resumed: Some(handle_resumed),
};
unsafe extern "C" fn parse_command(argc: usize, argv: *mut *mut libc::c_char) -> *mut libc::c_char {
if argc < 1 {
unsafe fn parse_command(args: &[String]) -> Option<String> {
let Some(ret) = args.first() else {
log::error!("Missing command");
return ptr::null_mut();
}
log::debug!("Command: {}", read_str(*argv));
strdup(*argv)
return None;
};
log::debug!("Command: {ret}");
Some(ret.clone())
}
unsafe extern "C" fn build_timeout_cmd(argv: *mut *mut libc::c_char) -> *mut swayidle_timeout_cmd {
unsafe fn build_timeout_cmd(args: &[String]) -> *mut swayidle_timeout_cmd {
*__errno_location() = 0;
let mut endptr = ptr::null_mut();
let seconds = strtoul(*argv.offset(1), &mut endptr, 10) as libc::c_int;
if *__errno_location() != 0 || *endptr as u8 != b'\0' {
let Ok(seconds) = args[1].parse::<i32>() else {
log::error!(
"Invalid {} parameter '{}', it should be a numeric value representing seconds",
read_str(*argv),
read_str(*argv.offset(1)),
args[0],
args[1],
);
exit(-1);
}
let cmd: *mut swayidle_timeout_cmd = calloc(1, mem::size_of::<swayidle_timeout_cmd>()).cast();
(*cmd).idlehint = false;
(*cmd).resume_pending = false;
if seconds > 0 {
(*cmd).timeout = seconds * 1000;
} else {
(*cmd).timeout = -1;
}
cmd
};
Box::into_raw(Box::new(swayidle_timeout_cmd {
idlehint: false,
resume_pending: false,
timeout: if seconds > 0 { seconds * 1000 } else { -1 },
idle_cmd: None,
idle_notification: ptr::null_mut(),
link: wl_list {
prev: ptr::null_mut(),
next: ptr::null_mut(),
},
registered_timeout: 0,
resume_cmd: None,
}))
}
unsafe extern "C" fn handle_signal(sig: libc::c_int, _data: *mut libc::c_void) -> libc::c_int {
state.handle_signal(sig)
@ -1292,69 +1291,54 @@ unsafe extern "C" fn display_event(
) -> libc::c_int {
state.display_event(mask)
}
unsafe extern "C" fn get_config_path() -> *mut libc::c_char {
static mut config_paths: [*const libc::c_char; 3] = [
b"$XDG_CONFIG_HOME/swayidle/config\0"
.as_ptr()
.cast::<libc::c_char>(),
b"$HOME/.swayidle/config\0".as_ptr().cast::<libc::c_char>(),
b"/usr/local/etc/swayidle/config\0"
.as_ptr()
.cast::<libc::c_char>(),
unsafe fn get_config_path() -> Option<String> {
static mut config_paths: [&str; 3] = [
"$XDG_CONFIG_HOME/swayidle/config",
"$HOME/.swayidle/config",
"/usr/local/etc/swayidle/config",
];
let config_home = getenv(b"XDG_CONFIG_HOME\0".as_ptr().cast::<libc::c_char>());
if config_home.is_null() || *config_home as u8 == b'\n' {
config_paths[0] = b"$HOME/.config/swayidle/config\0"
.as_ptr()
.cast::<libc::c_char>();
config_paths[0] = "$HOME/.config/swayidle/config";
}
let mut p = wordexp_t {
we_wordc: 0,
we_wordv: ptr::null_mut(),
we_offs: 0,
};
let mut path;
let mut i = 0;
while i
< (mem::size_of::<[*mut libc::c_char; 3]>())
.wrapping_div(mem::size_of::<*mut libc::c_char>())
{
if wordexp(config_paths[i], &mut p, 0) == 0 {
path = strdup(*p.we_wordv);
wordfree(&mut p);
if !path.is_null() && access(path, 4) == 0 {
return path;
if let Ok(res) = expand::wordexp(config_paths[i]) {
path = &res[0];
if !path.is_empty() && nix::unistd::access(path.as_str(), AccessFlags::R_OK).is_ok() {
return Some(path.clone());
}
free(path.cast());
}
i = i.wrapping_add(1);
}
ptr::null_mut()
None
}
unsafe fn main_0(argc: usize, argv: *mut *mut libc::c_char) -> libc::c_int {
state.init();
let mut config_path = ptr::null_mut();
let mut config_path = None;
if state.parse_args(argc, argv, &mut config_path) != 0 {
free(config_path.cast());
return -1;
}
if config_path.is_null() {
if config_path.is_none() {
config_path = get_config_path();
}
let config_load = if config_path.is_null() {
-2
let config_load = if let Some(ref config_path) = config_path {
state.load_config(Path::new(&config_path))
} else {
state.load_config(config_path)
-2
};
if config_load == -2 {
log::debug!("No config file found.");
} else if config_load == -22 {
log::error!("Config file {} has errors, exiting.", read_str(config_path));
log::error!("Config file {} has errors, exiting.", config_path.unwrap());
exit(-1);
} else {
log::debug!("Loaded config at {}", read_str(config_path));
log::debug!("Loaded config at {}", config_path.unwrap());
}
free(config_path.cast());
state.event_loop = wl_event_loop_create();
wl_event_loop_add_signal(state.event_loop, 2, handle_signal, ptr::null_mut());
wl_event_loop_add_signal(state.event_loop, 15, handle_signal, ptr::null_mut());
@ -1375,7 +1359,7 @@ unsafe fn main_0(argc: usize, argv: *mut *mut libc::c_char) -> libc::c_int {
let mut seat_i: *mut seat;
seat_i = state.seats.next.cast();
while ptr::addr_of_mut!((*seat_i).link) != ptr::addr_of_mut!(state.seats) {
if (state.seat_name).is_null() || strcmp((*seat_i).name, state.seat_name) == 0 {
if !matches!(&state.seat_name, Some(seat_name) if read_str((*seat_i).name) != *seat_name) {
seat = (*seat_i).proxy;
}
seat_i = (*seat_i).link.next.cast();
@ -1385,25 +1369,25 @@ unsafe fn main_0(argc: usize, argv: *mut *mut libc::c_char) -> libc::c_int {
return -4;
}
if seat.is_null() {
if state.seat_name.is_null() {
log::error!("No seat found");
if let Some(seat_name) = &state.seat_name {
log::error!("Seat {seat_name} not found");
} else {
log::error!("Seat {} not found", read_str(state.seat_name));
log::error!("No seat found");
}
return -5;
}
let mut should_run = wl_list_empty(&state.timeout_cmds) == 0;
state.connect_to_bus();
setup_property_changed_listener();
if !state.before_sleep_cmd.is_null() || !state.after_resume_cmd.is_null() {
if state.before_sleep_cmd.is_some() || state.after_resume_cmd.is_some() {
should_run = true;
setup_sleep_listener();
}
if !state.logind_lock_cmd.is_null() {
if state.logind_lock_cmd.is_some() {
should_run = true;
setup_lock_listener();
}
if !state.logind_unlock_cmd.is_null() {
if state.logind_unlock_cmd.is_some() {
should_run = true;
setup_unlock_listener();
}