Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e6da6282b1 | |||
| 80fb344128 | |||
| d3bdf46ecb | |||
| 41c032be54 | |||
| d33f9b3ede | |||
| 4a968e5ba5 |
246
Cargo.lock
generated
246
Cargo.lock
generated
@@ -4,9 +4,9 @@ version = 3
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstream"
|
name = "anstream"
|
||||||
version = "0.6.14"
|
version = "0.6.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b"
|
checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"anstyle-parse",
|
"anstyle-parse",
|
||||||
@@ -19,33 +19,33 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle"
|
name = "anstyle"
|
||||||
version = "1.0.7"
|
version = "1.0.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b"
|
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-parse"
|
name = "anstyle-parse"
|
||||||
version = "0.2.4"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4"
|
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-query"
|
name = "anstyle-query"
|
||||||
version = "1.0.3"
|
version = "1.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5"
|
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "3.0.3"
|
version = "3.0.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
@@ -53,9 +53,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.4"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -63,9 +63,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.2"
|
version = "4.5.20"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4"
|
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -75,9 +75,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.4"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64"
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -87,15 +87,27 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_lex"
|
name = "clap_lex"
|
||||||
version = "0.7.0"
|
version = "0.7.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
|
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "colorchoice"
|
name = "colorchoice"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.15.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@@ -104,34 +116,115 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_terminal_polyfill"
|
name = "i3"
|
||||||
version = "1.70.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800"
|
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.82"
|
version = "1.0.89"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b"
|
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.36"
|
version = "1.0.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "screencfg"
|
name = "screencfg"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
|
"i3",
|
||||||
|
"serde",
|
||||||
|
"toml",
|
||||||
|
"xrandr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.214"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.214"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.132"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -142,9 +235,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.61"
|
version = "2.0.87"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9"
|
checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -152,31 +245,65 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "toml"
|
||||||
version = "1.0.12"
|
version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8parse"
|
name = "utf8parse"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.52.0"
|
version = "0.59.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-targets",
|
"windows-targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
|
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows_aarch64_gnullvm",
|
"windows_aarch64_gnullvm",
|
||||||
"windows_aarch64_msvc",
|
"windows_aarch64_msvc",
|
||||||
@@ -190,48 +317,61 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_gnullvm"
|
name = "windows_aarch64_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
|
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_aarch64_msvc"
|
name = "windows_aarch64_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
|
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnu"
|
name = "windows_i686_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
|
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_gnullvm"
|
name = "windows_i686_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
|
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_i686_msvc"
|
name = "windows_i686_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
|
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnu"
|
name = "windows_x86_64_gnu"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
|
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_gnullvm"
|
name = "windows_x86_64_gnullvm"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
|
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows_x86_64_msvc"
|
name = "windows_x86_64_msvc"
|
||||||
version = "0.52.5"
|
version = "0.52.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.6.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xrandr"
|
||||||
|
version = "0.1.0"
|
||||||
|
|||||||
216
Cargo.toml
216
Cargo.toml
@@ -1,12 +1,222 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "screencfg"
|
name = "screencfg"
|
||||||
description = "Automatically configure your screen setup"
|
description = "Automatically configure your screen setup with i3"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
repository = "https://github.com/hakoerber/screencfg/"
|
||||||
|
authors = ["Hannes Körber <hannes@hkoerber.de>"]
|
||||||
|
rust-version = "1.74.1"
|
||||||
|
readme = "README.md"
|
||||||
|
license-file = "LICENSE"
|
||||||
|
keywords = ["i3", "xrandr"]
|
||||||
|
categories = ["command-line-utilities"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.4", default-features = false, features = ["std", "derive"] }
|
clap = { version = "4.*", default-features = false, features = [
|
||||||
|
"std",
|
||||||
|
"derive",
|
||||||
|
] }
|
||||||
|
toml = { version = "0.8.*", default-features = false, features = ["parse"] }
|
||||||
|
i3 = { path = "./i3" }
|
||||||
|
xrandr = { path = "./xrandr" }
|
||||||
|
serde = { version = "1.0.214", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
full = ["clap/help", "clap/color", "clap/suggestions", "clap/usage", "clap/error-context"]
|
full = [
|
||||||
|
"clap/help",
|
||||||
|
"clap/color",
|
||||||
|
"clap/suggestions",
|
||||||
|
"clap/usage",
|
||||||
|
"clap/error-context",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = ["i3", "xrandr"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
opt-level = 3
|
||||||
|
debug = false
|
||||||
|
strip = "symbols"
|
||||||
|
debug-assertions = false
|
||||||
|
overflow-checks = false
|
||||||
|
lto = "fat"
|
||||||
|
panic = "abort"
|
||||||
|
codegen-units = 1
|
||||||
|
rpath = false
|
||||||
|
|
||||||
|
[workspace.lints.rust]
|
||||||
|
absolute_paths_not_starting_with_crate = "deny"
|
||||||
|
elided_lifetimes_in_paths = "deny"
|
||||||
|
explicit_outlives_requirements = "deny"
|
||||||
|
keyword_idents = "deny"
|
||||||
|
let_underscore_drop = "deny"
|
||||||
|
non_ascii_idents = "deny"
|
||||||
|
non_local_definitions = "deny"
|
||||||
|
single_use_lifetimes = "deny"
|
||||||
|
unit_bindings = "deny"
|
||||||
|
unreachable_pub = "deny"
|
||||||
|
unsafe_code = { level = "forbid", priority = -1 }
|
||||||
|
unsafe_op_in_unsafe_fn = "deny"
|
||||||
|
unstable_features = { level = "forbid", priority = -1 }
|
||||||
|
unused_crate_dependencies = "deny"
|
||||||
|
unused_import_braces = "deny"
|
||||||
|
unused_lifetimes = "deny"
|
||||||
|
unused_macro_rules = "deny"
|
||||||
|
unused_qualifications = "deny"
|
||||||
|
unused_results = "deny"
|
||||||
|
variant_size_differences = "deny"
|
||||||
|
|
||||||
|
[workspace.lints.clippy]
|
||||||
|
# enabled groups
|
||||||
|
correctness = { level = "deny", priority = -1 }
|
||||||
|
suspicious = { level = "warn", priority = -1 }
|
||||||
|
style = { level = "warn", priority = -1 }
|
||||||
|
complexity = { level = "warn", priority = -1 }
|
||||||
|
perf = { level = "warn", priority = -1 }
|
||||||
|
cargo = { level = "warn", priority = -1 }
|
||||||
|
pedantic = { level = "warn", priority = -1 }
|
||||||
|
nursery = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
|
# pedantic overrides
|
||||||
|
too_many_lines = "allow"
|
||||||
|
must_use_candidate = "allow"
|
||||||
|
map_unwrap_or = "allow"
|
||||||
|
missing_errors_doc = "allow"
|
||||||
|
|
||||||
|
# nursery overrides
|
||||||
|
missing_const_for_fn = "allow"
|
||||||
|
option_if_let_else = "allow"
|
||||||
|
|
||||||
|
# complexity overrides
|
||||||
|
too_many_arguments = "allow"
|
||||||
|
|
||||||
|
# style overrides
|
||||||
|
new_without_default = "allow"
|
||||||
|
|
||||||
|
# cargo overrides
|
||||||
|
multiple_crate_versions = "allow"
|
||||||
|
cargo_common_metadata = "allow"
|
||||||
|
|
||||||
|
# selected restrictions
|
||||||
|
allow_attributes = "warn"
|
||||||
|
allow_attributes_without_reason = "warn"
|
||||||
|
arithmetic_side_effects = "warn"
|
||||||
|
as_conversions = "warn"
|
||||||
|
assertions_on_result_states = "warn"
|
||||||
|
cfg_not_test = "warn"
|
||||||
|
clone_on_ref_ptr = "warn"
|
||||||
|
create_dir = "warn"
|
||||||
|
dbg_macro = "warn"
|
||||||
|
decimal_literal_representation = "warn"
|
||||||
|
default_numeric_fallback = "warn"
|
||||||
|
deref_by_slicing = "warn"
|
||||||
|
disallowed_script_idents = "warn"
|
||||||
|
else_if_without_else = "warn"
|
||||||
|
empty_drop = "warn"
|
||||||
|
empty_enum_variants_with_brackets = "warn"
|
||||||
|
empty_structs_with_brackets = "warn"
|
||||||
|
exit = "warn"
|
||||||
|
filetype_is_file = "warn"
|
||||||
|
float_arithmetic = "warn"
|
||||||
|
float_cmp_const = "warn"
|
||||||
|
fn_to_numeric_cast_any = "warn"
|
||||||
|
format_push_string = "warn"
|
||||||
|
get_unwrap = "warn"
|
||||||
|
indexing_slicing = "warn"
|
||||||
|
infinite_loop = "warn"
|
||||||
|
inline_asm_x86_att_syntax = "warn"
|
||||||
|
inline_asm_x86_intel_syntax = "warn"
|
||||||
|
integer_division = "warn"
|
||||||
|
iter_over_hash_type = "warn"
|
||||||
|
large_include_file = "warn"
|
||||||
|
let_underscore_must_use = "warn"
|
||||||
|
let_underscore_untyped = "warn"
|
||||||
|
little_endian_bytes = "warn"
|
||||||
|
lossy_float_literal = "warn"
|
||||||
|
map_err_ignore = "warn"
|
||||||
|
mem_forget = "warn"
|
||||||
|
missing_assert_message = "warn"
|
||||||
|
missing_asserts_for_indexing = "warn"
|
||||||
|
mixed_read_write_in_expression = "warn"
|
||||||
|
modulo_arithmetic = "warn"
|
||||||
|
multiple_inherent_impl = "warn"
|
||||||
|
multiple_unsafe_ops_per_block = "warn"
|
||||||
|
mutex_atomic = "warn"
|
||||||
|
panic = "warn"
|
||||||
|
partial_pub_fields = "warn"
|
||||||
|
pattern_type_mismatch = "warn"
|
||||||
|
print_stderr = "warn"
|
||||||
|
print_stdout = "warn"
|
||||||
|
pub_without_shorthand = "warn"
|
||||||
|
rc_buffer = "warn"
|
||||||
|
rc_mutex = "warn"
|
||||||
|
redundant_type_annotations = "warn"
|
||||||
|
renamed_function_params = "warn"
|
||||||
|
rest_pat_in_fully_bound_structs = "warn"
|
||||||
|
same_name_method = "warn"
|
||||||
|
self_named_module_files = "warn"
|
||||||
|
semicolon_outside_block = "warn"
|
||||||
|
str_to_string = "warn"
|
||||||
|
string_add = "warn"
|
||||||
|
string_lit_chars_any = "warn"
|
||||||
|
string_slice = "warn"
|
||||||
|
string_to_string = "warn"
|
||||||
|
suspicious_xor_used_as_pow = "warn"
|
||||||
|
tests_outside_test_module = "warn"
|
||||||
|
todo = "warn"
|
||||||
|
try_err = "warn"
|
||||||
|
undocumented_unsafe_blocks = "warn"
|
||||||
|
unimplemented = "warn"
|
||||||
|
unnecessary_safety_comment = "warn"
|
||||||
|
unnecessary_safety_doc = "warn"
|
||||||
|
unnecessary_self_imports = "warn"
|
||||||
|
unneeded_field_pattern = "warn"
|
||||||
|
unseparated_literal_suffix = "warn"
|
||||||
|
unused_result_ok = "warn"
|
||||||
|
unwrap_used = "warn"
|
||||||
|
use_debug = "warn"
|
||||||
|
verbose_file_reads = "warn"
|
||||||
|
|
||||||
|
# restrictions explicit allows
|
||||||
|
absolute_paths = "allow"
|
||||||
|
alloc_instead_of_core = "allow"
|
||||||
|
as_underscore = "allow"
|
||||||
|
big_endian_bytes = "allow"
|
||||||
|
default_union_representation = "allow"
|
||||||
|
error_impl_error = "allow"
|
||||||
|
exhaustive_enums = "allow"
|
||||||
|
exhaustive_structs = "allow"
|
||||||
|
expect_used = "allow"
|
||||||
|
field_scoped_visibility_modifiers = "allow"
|
||||||
|
host_endian_bytes = "allow"
|
||||||
|
if_then_some_else_none = "allow"
|
||||||
|
impl_trait_in_params = "allow"
|
||||||
|
implicit_return = "allow"
|
||||||
|
integer_division_remainder_used = "allow"
|
||||||
|
min_ident_chars = "allow"
|
||||||
|
missing_docs_in_private_items = "allow"
|
||||||
|
missing_inline_in_public_items = "allow"
|
||||||
|
missing_trait_methods = "allow"
|
||||||
|
mod_module_files = "allow"
|
||||||
|
needless_raw_strings = "allow"
|
||||||
|
non_ascii_literal = "allow"
|
||||||
|
panic_in_result_fn = "allow"
|
||||||
|
pathbuf_init_then_push = "allow"
|
||||||
|
pub_use = "allow"
|
||||||
|
pub_with_shorthand = "allow"
|
||||||
|
question_mark_used = "allow"
|
||||||
|
ref_patterns = "allow"
|
||||||
|
semicolon_inside_block = "allow"
|
||||||
|
separated_literal_suffix = "allow"
|
||||||
|
shadow_reuse = "allow"
|
||||||
|
shadow_same = "allow"
|
||||||
|
shadow_unrelated = "allow"
|
||||||
|
single_call_fn = "allow"
|
||||||
|
single_char_lifetime_names = "allow"
|
||||||
|
std_instead_of_alloc = "allow"
|
||||||
|
std_instead_of_core = "allow"
|
||||||
|
unreachable = "allow"
|
||||||
|
unwrap_in_result = "allow"
|
||||||
|
wildcard_enum_match_arm = "allow"
|
||||||
|
|||||||
25
Makefile
Normal file
25
Makefile
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
.PHONY: check
|
||||||
|
check: | fmt lint test
|
||||||
|
|
||||||
|
.PHONY: docs
|
||||||
|
docs:
|
||||||
|
cargo watch -- cargo doc
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
cargo hack --feature-powerset --no-dev-deps check
|
||||||
|
cargo test --workspace --color=always
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint:
|
||||||
|
cargo clippy --workspace --tests --color=always
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt:
|
||||||
|
cargo fmt
|
||||||
|
find -name '*.md' | xargs prettier --print-width 80 --prose-wrap always --write
|
||||||
|
find -name '*.toml' | xargs taplo format
|
||||||
|
|
||||||
|
.PHONY: build-static
|
||||||
|
build-static:
|
||||||
|
cargo build --target x86_64-unknown-linux-musl --no-default-features --release --workspace
|
||||||
11
i3/Cargo.toml
Normal file
11
i3/Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "i3"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.*", default-features = false, features = ["derive"] }
|
||||||
|
serde_json = { version = "1.*", default-features = false, features = ["std"] }
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
67
i3/src/error.rs
Normal file
67
i3/src/error.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
use std::{fmt, io};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Msg {
|
||||||
|
Owned(String),
|
||||||
|
Static(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Msg {
|
||||||
|
fn from(value: &'static str) -> Self {
|
||||||
|
Self::Static(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Msg {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::Owned(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Msg {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match *self {
|
||||||
|
Self::Owned(ref s) => s.as_str(),
|
||||||
|
Self::Static(s) => s,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Connection(Msg),
|
||||||
|
Command(Msg),
|
||||||
|
Protocol(Msg),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match *self {
|
||||||
|
Self::Connection(ref msg) => format!("connection failed: {msg}"),
|
||||||
|
Self::Command(ref msg) => format!("command failed: {msg}"),
|
||||||
|
Self::Protocol(ref msg) => format!("overflow: {msg}"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::Command(Msg::Owned(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<serde_json::Error> for Error {
|
||||||
|
fn from(value: serde_json::Error) -> Self {
|
||||||
|
Self::Connection(Msg::Owned(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
619
i3/src/lib.rs
Normal file
619
i3/src/lib.rs
Normal file
@@ -0,0 +1,619 @@
|
|||||||
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
ffi::OsStr,
|
||||||
|
fmt,
|
||||||
|
io::{Read, Write},
|
||||||
|
ops::{Deref, DerefMut, Index},
|
||||||
|
os::unix::{ffi::OsStrExt as _, net},
|
||||||
|
path::PathBuf,
|
||||||
|
process,
|
||||||
|
time::Duration,
|
||||||
|
vec::IntoIter,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
pub use error::Error;
|
||||||
|
|
||||||
|
pub enum Command {
|
||||||
|
Nop,
|
||||||
|
MoveWorkspace { id: usize, output: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Command> for Cow<'static, str> {
|
||||||
|
fn from(value: &Command) -> Self {
|
||||||
|
match *value {
|
||||||
|
Command::Nop => Cow::from("nop"),
|
||||||
|
Command::MoveWorkspace { id, ref output } => Cow::from(format!(
|
||||||
|
"[workspace=\"{id}\"] move workspace to output {output}"
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Conn {
|
||||||
|
fn version(&mut self) -> Result<Version, Error>;
|
||||||
|
fn outputs(&mut self) -> Result<Outputs, Error>;
|
||||||
|
fn workspaces(&mut self) -> Result<Workspaces, Error>;
|
||||||
|
fn command(&mut self, command: Command) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum MockSetting {
|
||||||
|
LaptopOnly,
|
||||||
|
ExternalOnly(usize),
|
||||||
|
Mixed,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MockConnection {
|
||||||
|
pub fail: bool,
|
||||||
|
pub setting: MockSetting,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockConnection {
|
||||||
|
fn check_fail(&self) -> Result<(), Error> {
|
||||||
|
if self.fail {
|
||||||
|
Err(Error::Connection("fail".into()))
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Conn for MockConnection {
|
||||||
|
fn version(&mut self) -> Result<Version, Error> {
|
||||||
|
self.check_fail()?;
|
||||||
|
Ok(Version {
|
||||||
|
minor: 1,
|
||||||
|
patch: 2,
|
||||||
|
major: 3,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outputs(&mut self) -> Result<Outputs, Error> {
|
||||||
|
self.check_fail()?;
|
||||||
|
match self.setting {
|
||||||
|
MockSetting::LaptopOnly => Ok(Outputs(vec![Output {
|
||||||
|
name: "eDP-1".into(),
|
||||||
|
active: true,
|
||||||
|
primary: true,
|
||||||
|
}])),
|
||||||
|
MockSetting::ExternalOnly(num) => match num {
|
||||||
|
1 => Ok(Outputs(vec![Output {
|
||||||
|
name: "DP-1".into(),
|
||||||
|
active: true,
|
||||||
|
primary: false,
|
||||||
|
}])),
|
||||||
|
2 => Ok(Outputs(vec![
|
||||||
|
Output {
|
||||||
|
name: "DP-1".into(),
|
||||||
|
active: true,
|
||||||
|
primary: false,
|
||||||
|
},
|
||||||
|
Output {
|
||||||
|
name: "DP-2".into(),
|
||||||
|
active: false,
|
||||||
|
primary: false,
|
||||||
|
},
|
||||||
|
])),
|
||||||
|
#[expect(clippy::panic, reason = "just a mock")]
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
MockSetting::Mixed => Ok(Outputs(vec![
|
||||||
|
Output {
|
||||||
|
name: "eDP-1".into(),
|
||||||
|
active: true,
|
||||||
|
primary: true,
|
||||||
|
},
|
||||||
|
Output {
|
||||||
|
name: "HDMI-1".into(),
|
||||||
|
active: true,
|
||||||
|
primary: false,
|
||||||
|
},
|
||||||
|
Output {
|
||||||
|
name: "DP-1".into(),
|
||||||
|
active: true,
|
||||||
|
primary: false,
|
||||||
|
},
|
||||||
|
])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspaces(&mut self) -> Result<Workspaces, Error> {
|
||||||
|
self.check_fail()?;
|
||||||
|
match self.setting {
|
||||||
|
MockSetting::LaptopOnly => Ok(Workspaces(vec![
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "eDP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "eDP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "eDP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "eDP-1".into(),
|
||||||
|
},
|
||||||
|
])),
|
||||||
|
MockSetting::ExternalOnly(num) => match num {
|
||||||
|
1 => Ok(Workspaces(vec![
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-1".into(),
|
||||||
|
},
|
||||||
|
])),
|
||||||
|
2 => Ok(Workspaces(vec![
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-2".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-2".into(),
|
||||||
|
},
|
||||||
|
])),
|
||||||
|
#[expect(clippy::panic, reason = "just a mock")]
|
||||||
|
_ => panic!(),
|
||||||
|
},
|
||||||
|
MockSetting::Mixed => Ok(Workspaces(vec![
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "eDP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "eDP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "DP-1".into(),
|
||||||
|
},
|
||||||
|
Workspace {
|
||||||
|
num: 1,
|
||||||
|
name: "num1".into(),
|
||||||
|
output: "HDMI-1".into(),
|
||||||
|
},
|
||||||
|
])),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command(&mut self, _command: Command) -> Result<(), Error> {
|
||||||
|
self.check_fail()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Connection(net::UnixStream);
|
||||||
|
|
||||||
|
impl Conn for Connection {
|
||||||
|
fn version(&mut self) -> Result<Version, Error> {
|
||||||
|
Message::Version.send(self)?;
|
||||||
|
|
||||||
|
let response = Response::read(self)?;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Response::Version(version) => Ok(version.into()),
|
||||||
|
Response::Workspaces(_) | Response::Command(_) | Response::Outputs(_) => Err(
|
||||||
|
Error::Connection("received invalid response from i3".into()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outputs(&mut self) -> Result<Outputs, Error> {
|
||||||
|
Message::Outputs.send(self)?;
|
||||||
|
let response = Response::read(self)?;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Response::Outputs(outputs) => Ok(outputs.into()),
|
||||||
|
Response::Version(_) | Response::Workspaces(_) | Response::Command(_) => Err(
|
||||||
|
Error::Connection("received invalid response from i3".into()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn workspaces(&mut self) -> Result<Workspaces, Error> {
|
||||||
|
Message::Workspaces.send(self)?;
|
||||||
|
|
||||||
|
let response = Response::read(self)?;
|
||||||
|
|
||||||
|
match response {
|
||||||
|
Response::Workspaces(workspaces) => Ok(workspaces.into()),
|
||||||
|
Response::Version(_) | Response::Command(_) | Response::Outputs(_) => Err(
|
||||||
|
Error::Connection("received invalid response from i3".into()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command(&mut self, command: Command) -> Result<(), Error> {
|
||||||
|
Message::Command(command).send(self)?;
|
||||||
|
|
||||||
|
let response = Response::read(self)?;
|
||||||
|
match response {
|
||||||
|
Response::Command(commands) => {
|
||||||
|
for payload in commands {
|
||||||
|
if !payload.success {
|
||||||
|
return Err(Error::Command(match payload.error {
|
||||||
|
Some(err) => err.into(),
|
||||||
|
None => "unknown error".into(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Response::Version(_) | Response::Workspaces(_) | Response::Outputs(_) => Err(
|
||||||
|
Error::Connection("received invalid response from i3".into()),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_socketpath() -> Result<PathBuf, Error> {
|
||||||
|
let cmd = process::Command::new("i3")
|
||||||
|
.arg("--get-socketpath")
|
||||||
|
.output()?;
|
||||||
|
|
||||||
|
let bytes = cmd
|
||||||
|
.stdout
|
||||||
|
.into_iter()
|
||||||
|
.take_while(|c| *c != b'\n')
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
|
||||||
|
let string = OsStr::from_bytes(&bytes);
|
||||||
|
|
||||||
|
let path = PathBuf::from(string);
|
||||||
|
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn connect() -> Result<Connection, Error> {
|
||||||
|
let socketpath = get_socketpath()?;
|
||||||
|
|
||||||
|
let socket = net::SocketAddr::from_pathname(socketpath)?;
|
||||||
|
|
||||||
|
let stream = net::UnixStream::connect_addr(&socket)?;
|
||||||
|
stream.set_read_timeout(Some(Duration::from_millis(100)))?;
|
||||||
|
|
||||||
|
Ok(Connection(stream))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct OutputPayload {
|
||||||
|
name: String,
|
||||||
|
active: bool,
|
||||||
|
primary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Output {
|
||||||
|
pub name: String,
|
||||||
|
pub active: bool,
|
||||||
|
pub primary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Output {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.name)?;
|
||||||
|
if self.active {
|
||||||
|
write!(f, " [active]")?;
|
||||||
|
}
|
||||||
|
if self.primary {
|
||||||
|
write!(f, " [primary]")?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<OutputPayload> for Output {
|
||||||
|
fn from(value: OutputPayload) -> Self {
|
||||||
|
Self {
|
||||||
|
name: value.name,
|
||||||
|
active: value.active,
|
||||||
|
primary: value.primary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Workspaces(Vec<Workspace>);
|
||||||
|
|
||||||
|
impl From<Vec<WorkspacePayload>> for Workspaces {
|
||||||
|
fn from(value: Vec<WorkspacePayload>) -> Self {
|
||||||
|
Self(value.into_iter().map(Into::into).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Workspaces {
|
||||||
|
type Item = Workspace;
|
||||||
|
type IntoIter = IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Workspaces {
|
||||||
|
type Target = [Workspace];
|
||||||
|
|
||||||
|
fn deref(&self) -> &[Workspace] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for Workspaces {
|
||||||
|
fn deref_mut(&mut self) -> &mut [Workspace] {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Outputs(Vec<Output>);
|
||||||
|
|
||||||
|
impl From<Vec<OutputPayload>> for Outputs {
|
||||||
|
fn from(value: Vec<OutputPayload>) -> Self {
|
||||||
|
Self(
|
||||||
|
value
|
||||||
|
.into_iter()
|
||||||
|
.filter(|output| output.name != "xroot-0")
|
||||||
|
.map(Into::into)
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Outputs {
|
||||||
|
type Item = Output;
|
||||||
|
type IntoIter = <Vec<Output> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.0.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Outputs {
|
||||||
|
type Target = [Output];
|
||||||
|
|
||||||
|
fn deref(&self) -> &[Output] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for Outputs {
|
||||||
|
fn deref_mut(&mut self) -> &mut [Output] {
|
||||||
|
&mut self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<usize> for Outputs {
|
||||||
|
type Output = Output;
|
||||||
|
|
||||||
|
#[expect(
|
||||||
|
clippy::indexing_slicing,
|
||||||
|
reason = "transparent slicing, panicking is ok"
|
||||||
|
)]
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.0[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Message {
|
||||||
|
Command(Command),
|
||||||
|
Workspaces,
|
||||||
|
Outputs,
|
||||||
|
Version,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Message> for u32 {
|
||||||
|
fn from(value: Message) -> Self {
|
||||||
|
match value {
|
||||||
|
Message::Command(_) => 0,
|
||||||
|
Message::Workspaces => 1,
|
||||||
|
Message::Outputs => 3,
|
||||||
|
Message::Version => 7,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Message {
|
||||||
|
fn bytes(self) -> Result<Vec<u8>, Error> {
|
||||||
|
let payload: Option<Cow<'static, str>> = match self {
|
||||||
|
Self::Command(ref command) => Some(command.into()),
|
||||||
|
Self::Workspaces | Self::Outputs | Self::Version => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut message: Vec<u8> = vec![];
|
||||||
|
let command_number: u32 = self.into();
|
||||||
|
|
||||||
|
message.extend_from_slice(b"i3-ipc");
|
||||||
|
message.extend_from_slice(
|
||||||
|
&u32::try_from(payload.as_ref().map_or(0, |l| l.len()))
|
||||||
|
.map_err(|_err| Error::Command("payload length bigger than 4 bytes".into()))?
|
||||||
|
.to_ne_bytes(),
|
||||||
|
);
|
||||||
|
message.extend_from_slice(&(command_number.to_ne_bytes()));
|
||||||
|
if let Some(payload) = payload {
|
||||||
|
message.extend_from_slice(payload.as_bytes());
|
||||||
|
}
|
||||||
|
Ok(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(self, socket: &mut Connection) -> Result<(), Error> {
|
||||||
|
let message = self.bytes()?;
|
||||||
|
socket.0.write_all(&message)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
#[expect(dead_code, reason = "external data defintion")]
|
||||||
|
struct VersionPayload {
|
||||||
|
human_readable: String,
|
||||||
|
loaded_config_file_name: String,
|
||||||
|
major: usize,
|
||||||
|
minor: usize,
|
||||||
|
patch: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Version {
|
||||||
|
minor: usize,
|
||||||
|
patch: usize,
|
||||||
|
major: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Version {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VersionPayload> for Version {
|
||||||
|
fn from(value: VersionPayload) -> Self {
|
||||||
|
Self {
|
||||||
|
major: value.major,
|
||||||
|
minor: value.minor,
|
||||||
|
patch: value.patch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
#[expect(dead_code, reason = "external data defintion")]
|
||||||
|
struct WorkspacePayload {
|
||||||
|
id: usize,
|
||||||
|
num: usize,
|
||||||
|
name: String,
|
||||||
|
output: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Deserialize)]
|
||||||
|
struct CommandPayload {
|
||||||
|
success: bool,
|
||||||
|
error: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Workspace {
|
||||||
|
pub num: usize,
|
||||||
|
pub name: String,
|
||||||
|
pub output: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Workspace {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{} on {}", self.num, self.output)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<WorkspacePayload> for Workspace {
|
||||||
|
fn from(value: WorkspacePayload) -> Self {
|
||||||
|
Self {
|
||||||
|
num: value.num,
|
||||||
|
name: value.name,
|
||||||
|
output: value.output,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Response {
|
||||||
|
Version(VersionPayload),
|
||||||
|
Workspaces(Vec<WorkspacePayload>),
|
||||||
|
Command(Vec<CommandPayload>),
|
||||||
|
Outputs(Vec<OutputPayload>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Response {
|
||||||
|
fn read(stream: &mut Connection) -> Result<Self, Error> {
|
||||||
|
let mut response = vec![
|
||||||
|
0;
|
||||||
|
"i3-ipc".chars().count().checked_add(4 + 4).ok_or_else(|| {
|
||||||
|
Error::Protocol("payload length overflowed".into())
|
||||||
|
})?
|
||||||
|
];
|
||||||
|
|
||||||
|
stream.0.read_exact(&mut response)?;
|
||||||
|
|
||||||
|
if &response
|
||||||
|
.get(0..6)
|
||||||
|
.ok_or_else(|| Error::Protocol("response too short for even the magic string".into()))?
|
||||||
|
!= b"i3-ipc"
|
||||||
|
{
|
||||||
|
return Err(Error::Protocol("magic string not found".into()));
|
||||||
|
}
|
||||||
|
let response_length = {
|
||||||
|
let bytes = response
|
||||||
|
.get(6..10)
|
||||||
|
.ok_or_else(|| Error::Protocol("not enough bytes for response length".into()))?;
|
||||||
|
|
||||||
|
let bytes = bytes
|
||||||
|
.try_into()
|
||||||
|
.expect("slice of length 4 can always be converted into an array of size 4");
|
||||||
|
|
||||||
|
u32::from_ne_bytes(bytes)
|
||||||
|
};
|
||||||
|
|
||||||
|
let response_command = {
|
||||||
|
let bytes = response
|
||||||
|
.get(10..14)
|
||||||
|
.ok_or_else(|| Error::Protocol("not enough bytes for command".into()))?;
|
||||||
|
|
||||||
|
let bytes = bytes
|
||||||
|
.try_into()
|
||||||
|
.expect("slice of length 4 can always be converted into an array of size 4");
|
||||||
|
|
||||||
|
u32::from_ne_bytes(bytes)
|
||||||
|
};
|
||||||
|
|
||||||
|
response = vec![
|
||||||
|
0;
|
||||||
|
response_length
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_err| { Error::Protocol("u32 overflowed usize".into()) })?
|
||||||
|
];
|
||||||
|
|
||||||
|
stream.0.read_exact(&mut response)?;
|
||||||
|
|
||||||
|
match response_command {
|
||||||
|
0 => Ok(Self::Command(serde_json::from_slice(&response)?)),
|
||||||
|
1 => Ok(Self::Workspaces(serde_json::from_slice(&response)?)),
|
||||||
|
3 => Ok(Self::Outputs(serde_json::from_slice(&response)?)),
|
||||||
|
7 => Ok(Self::Version(serde_json::from_slice(&response)?)),
|
||||||
|
_ => Err(Error::Connection("unknown response type".into())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,12 @@
|
|||||||
pkgbase = screencfg-git
|
pkgbase = screencfg
|
||||||
pkgdesc = Automatically configure your screen setup
|
pkgdesc = Automatically configure your screen setup
|
||||||
pkgver = 0.1.r0.g1e8bf1d
|
pkgver = 0.1
|
||||||
pkgrel = 1
|
pkgrel = 1
|
||||||
url = https://github.com/hakoerber/screencfg
|
|
||||||
arch = x86_64
|
arch = x86_64
|
||||||
license = GPL-3.0-only
|
license = GPL-3.0-only
|
||||||
makedepends = cargo
|
makedepends = cargo
|
||||||
makedepends = git
|
makedepends = git
|
||||||
depends = glibc
|
depends = glibc
|
||||||
depends = gcc-libs
|
depends = gcc-libs
|
||||||
provides = screencfg
|
|
||||||
conflicts = screencfg
|
|
||||||
source = screencfg-git::git+https://github.com/hakoerber/screencfg#branch=master
|
|
||||||
sha256sums = SKIP
|
|
||||||
|
|
||||||
pkgname = screencfg-git
|
pkgname = screencfg
|
||||||
|
|||||||
13
pkg/arch/local/.SRCINFO
Normal file
13
pkg/arch/local/.SRCINFO
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
pkgbase = screencfg
|
||||||
|
pkgdesc = Automatically configure your screen setup
|
||||||
|
pkgver = 0.1.r0.g1e8bf1d
|
||||||
|
pkgrel = 1
|
||||||
|
url = https://github.com/hakoerber/screencfg
|
||||||
|
arch = x86_64
|
||||||
|
license = GPL-3.0-only
|
||||||
|
makedepends = cargo
|
||||||
|
makedepends = git
|
||||||
|
depends = glibc
|
||||||
|
depends = gcc-libs
|
||||||
|
|
||||||
|
pkgname = screencfg
|
||||||
4
pkg/arch/local/.gitignore
vendored
Normal file
4
pkg/arch/local/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/.gitignore
|
||||||
|
!/PKGBUILD
|
||||||
|
!/.SRCINFO
|
||||||
40
pkg/arch/local/PKGBUILD
Normal file
40
pkg/arch/local/PKGBUILD
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Maintainer: Hannes Körber <hannes@hkoerber.de>
|
||||||
|
pkgname='screencfg'
|
||||||
|
pkgver=0.1.r0.g1e8bf1d
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc='Automatically configure your screen setup'
|
||||||
|
arch=('x86_64')
|
||||||
|
url='https://github.com/hakoerber/screencfg'
|
||||||
|
license=('GPL-3.0-only')
|
||||||
|
depends=('glibc' 'gcc-libs')
|
||||||
|
makedepends=('cargo' 'git')
|
||||||
|
source=()
|
||||||
|
sha256sums=()
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
cd "../../../.."
|
||||||
|
pwd
|
||||||
|
export RUSTUP_TOOLCHAIN=stable
|
||||||
|
cargo fetch --locked --target "$(rustc -vV | sed -n 's/host: //p')"
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
cd "../../../.."
|
||||||
|
pwd
|
||||||
|
export RUSTUP_TOOLCHAIN=stable
|
||||||
|
export CARGO_TARGET_DIR=target
|
||||||
|
cargo build --frozen --release
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
cd "../../../.."
|
||||||
|
pwd
|
||||||
|
export RUSTUP_TOOLCHAIN=stable
|
||||||
|
cargo test --frozen
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
pwd
|
||||||
|
cd "../../../.."
|
||||||
|
install -Dm0755 -t "$pkgdir/usr/bin/" "target/release/${pkgname/-git}"
|
||||||
|
}
|
||||||
17
pkg/arch/screencfg-git/.SRCINFO
Normal file
17
pkg/arch/screencfg-git/.SRCINFO
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
pkgbase = screencfg-git
|
||||||
|
pkgdesc = Automatically configure your screen setup
|
||||||
|
pkgver = 0.1.r2.gd33f9b3
|
||||||
|
pkgrel = 1
|
||||||
|
url = https://github.com/hakoerber/screencfg
|
||||||
|
arch = x86_64
|
||||||
|
license = GPL-3.0-only
|
||||||
|
makedepends = cargo
|
||||||
|
makedepends = git
|
||||||
|
depends = glibc
|
||||||
|
depends = gcc-libs
|
||||||
|
provides = screencfg
|
||||||
|
conflicts = screencfg
|
||||||
|
source = screencfg-git::git+https://github.com/hakoerber/screencfg#branch=develop
|
||||||
|
sha256sums = SKIP
|
||||||
|
|
||||||
|
pkgname = screencfg-git
|
||||||
4
pkg/arch/screencfg-git/.gitignore
vendored
Normal file
4
pkg/arch/screencfg-git/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*
|
||||||
|
!/.gitignore
|
||||||
|
!/PKGBUILD
|
||||||
|
!/.SRCINFO
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Maintainer: Hannes Körber <hannes@hkoerber.de>
|
# Maintainer: Hannes Körber <hannes@hkoerber.de>
|
||||||
pkgname='screencfg-git'
|
pkgname='screencfg-git'
|
||||||
pkgver=0.1.r0.g1e8bf1d
|
pkgver=0.1.r2.gd33f9b3
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc='Automatically configure your screen setup'
|
pkgdesc='Automatically configure your screen setup'
|
||||||
arch=('x86_64')
|
arch=('x86_64')
|
||||||
@@ -10,7 +10,7 @@ depends=('glibc' 'gcc-libs')
|
|||||||
makedepends=('cargo' 'git')
|
makedepends=('cargo' 'git')
|
||||||
provides=('screencfg')
|
provides=('screencfg')
|
||||||
conflicts=('screencfg')
|
conflicts=('screencfg')
|
||||||
source=("${pkgname}::git+https://github.com/hakoerber/screencfg#branch=master")
|
source=("${pkgname}::git+https://github.com/hakoerber/screencfg#branch=develop")
|
||||||
sha256sums=('SKIP')
|
sha256sums=('SKIP')
|
||||||
|
|
||||||
pkgver() {
|
pkgver() {
|
||||||
23
src/config.rs
Normal file
23
src/config.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::Error;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub(crate) struct Config {
|
||||||
|
pub post_commands: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_path(path: &Path) -> Result<Option<Config>, Error> {
|
||||||
|
let content = match std::fs::read_to_string(path) {
|
||||||
|
Ok(p) => p,
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => return Ok(None),
|
||||||
|
_ => return Err(Error::ConfigFileOpen(e)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Some(toml::from_str(&content)?))
|
||||||
|
}
|
||||||
104
src/error.rs
Normal file
104
src/error.rs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
use std::{fmt, io, path::PathBuf, string};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum Msg {
|
||||||
|
Owned(String),
|
||||||
|
Static(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Msg {
|
||||||
|
fn from(value: &'static str) -> Self {
|
||||||
|
Self::Static(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Msg {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::Owned(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Msg {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match self {
|
||||||
|
Msg::Owned(ref s) => s.as_str(),
|
||||||
|
Msg::Static(s) => s,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum Error {
|
||||||
|
Generic(Msg),
|
||||||
|
Command(Msg),
|
||||||
|
Classify(Msg),
|
||||||
|
Workstation(Msg),
|
||||||
|
Plan(Msg),
|
||||||
|
Apply(Msg),
|
||||||
|
I3(i3::Error),
|
||||||
|
Xrandr(xrandr::Error),
|
||||||
|
InvalidSetup(Msg),
|
||||||
|
InvalidConfig(Msg),
|
||||||
|
ConfigFileOpen(io::Error),
|
||||||
|
ConfigNotFound { path: PathBuf },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match *self {
|
||||||
|
Self::Generic(ref msg) => format!("error: {msg}"),
|
||||||
|
Self::Command(ref msg) => format!("command failed: {msg}"),
|
||||||
|
Self::Classify(ref msg) => format!("classification failed: {msg}"),
|
||||||
|
Self::Workstation(ref msg) => format!("workstation failed: {msg}"),
|
||||||
|
Self::Plan(ref msg) => format!("plan failed: {msg}"),
|
||||||
|
Self::Apply(ref msg) => format!("apply failed: {msg}"),
|
||||||
|
Self::I3(ref e) => format!("i3: {e}"),
|
||||||
|
Self::Xrandr(ref e) => format!("xrandr: {e}"),
|
||||||
|
Self::InvalidSetup(ref msg) => format!("invalid setup: {msg}"),
|
||||||
|
Self::InvalidConfig(ref msg) => format!("invalid config: {msg}"),
|
||||||
|
Self::ConfigFileOpen(ref err) => format!("could not open config: {err}"),
|
||||||
|
Self::ConfigNotFound { ref path } =>
|
||||||
|
format!("could not find config file at {}", path.display()),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<fmt::Error> for Error {
|
||||||
|
fn from(value: fmt::Error) -> Self {
|
||||||
|
Self::Generic(Msg::Owned(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i3::Error> for Error {
|
||||||
|
fn from(value: i3::Error) -> Self {
|
||||||
|
Self::I3(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<xrandr::Error> for Error {
|
||||||
|
fn from(value: xrandr::Error) -> Self {
|
||||||
|
Self::Xrandr(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<string::FromUtf8Error> for Error {
|
||||||
|
fn from(value: string::FromUtf8Error) -> Self {
|
||||||
|
Self::Command(Msg::Owned(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<toml::de::Error> for Error {
|
||||||
|
fn from(value: toml::de::Error) -> Self {
|
||||||
|
Self::InvalidConfig(Msg::Owned(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
1274
src/main.rs
1274
src/main.rs
File diff suppressed because it is too large
Load Diff
9
update-pkgbuild.sh
Executable file
9
update-pkgbuild.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if ! git remote | grep -q ^aur$ ; then
|
||||||
|
git remote add aur ssh://aur@aur.archlinux.org/screencfg-git.git
|
||||||
|
fi
|
||||||
|
|
||||||
|
git subtree push --prefix pkg/arch/screencfg-git aur master
|
||||||
|
|
||||||
|
git remote rm aur
|
||||||
9
xrandr/Cargo.toml
Normal file
9
xrandr/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "xrandr"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
65
xrandr/src/error.rs
Normal file
65
xrandr/src/error.rs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
use std::{fmt, io, string};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Msg {
|
||||||
|
Owned(String),
|
||||||
|
Static(&'static str),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static str> for Msg {
|
||||||
|
fn from(value: &'static str) -> Self {
|
||||||
|
Self::Static(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for Msg {
|
||||||
|
fn from(value: String) -> Self {
|
||||||
|
Self::Owned(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Msg {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match *self {
|
||||||
|
Self::Owned(ref s) => s.as_str(),
|
||||||
|
Self::Static(s) => s,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
Command(Msg),
|
||||||
|
Parse(Msg),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for Error {
|
||||||
|
fn from(value: io::Error) -> Self {
|
||||||
|
Self::Command(Msg::Owned(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<string::FromUtf8Error> for Error {
|
||||||
|
fn from(value: string::FromUtf8Error) -> Self {
|
||||||
|
Self::Parse(Msg::Owned(value.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
match *self {
|
||||||
|
Self::Command(ref msg) => format!("command failed: {msg}"),
|
||||||
|
Self::Parse(ref msg) => format!("parsing command output failed: {msg}"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
||||||
57
xrandr/src/lib.rs
Normal file
57
xrandr/src/lib.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use std::{process, string::String};
|
||||||
|
|
||||||
|
mod error;
|
||||||
|
|
||||||
|
pub use error::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum OutputState {
|
||||||
|
Connected,
|
||||||
|
Disconnected,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for OutputState {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
"connected" => Ok(Self::Connected),
|
||||||
|
"disconnected" => Ok(Self::Disconnected),
|
||||||
|
_ => Err(Error::Parse(
|
||||||
|
format!("unknown xrandr output state: {value}").into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Output {
|
||||||
|
pub name: String,
|
||||||
|
pub state: OutputState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Output {
|
||||||
|
pub fn findall() -> Result<Vec<Self>, Error> {
|
||||||
|
String::from_utf8(
|
||||||
|
process::Command::new("xrandr")
|
||||||
|
.arg("--query")
|
||||||
|
.output()?
|
||||||
|
.stdout,
|
||||||
|
)?
|
||||||
|
.lines()
|
||||||
|
.skip(1) // skip header
|
||||||
|
.filter(|line| line.chars().next().map_or(false, char::is_alphanumeric))
|
||||||
|
.map(|line| {
|
||||||
|
let mut parts = line.split_whitespace();
|
||||||
|
match (parts.next(), parts.next()) {
|
||||||
|
(Some(part_1), Some(part_2)) => Ok(Self {
|
||||||
|
name: part_1.to_owned(),
|
||||||
|
state: part_2.try_into()?,
|
||||||
|
}),
|
||||||
|
_ => Err(Error::Command(
|
||||||
|
format!("not enough output information in line: {line}").into(),
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user