Dynamic widget configuration and layout
1. Implement new layout API, replacing previous hard-coded layout. 2. Make the widgets (previously modules) configurable. 3. Split some widgets into smaller modules. 4. Configuration file (reluctantly JSON at time of writing). 5. More correct damage handling, copying damage to next buffer.
This commit is contained in:
Generated
+80
@@ -142,6 +142,11 @@ dependencies = [
|
|||||||
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -375,6 +380,14 @@ dependencies = [
|
|||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "0.6.13"
|
version = "0.6.13"
|
||||||
@@ -383,6 +396,14 @@ dependencies = [
|
|||||||
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rcalc_lib"
|
name = "rcalc_lib"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@@ -419,6 +440,11 @@ dependencies = [
|
|||||||
"stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.5"
|
version = "1.0.5"
|
||||||
@@ -427,6 +453,34 @@ dependencies = [
|
|||||||
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.99"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha-1"
|
name = "sha-1"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@@ -476,6 +530,16 @@ dependencies = [
|
|||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.1.42"
|
version = "0.1.42"
|
||||||
@@ -501,6 +565,11 @@ name = "unicode-xid"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "void"
|
name = "void"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
@@ -613,6 +682,8 @@ dependencies = [
|
|||||||
"rcalc_lib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rcalc_lib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -650,6 +721,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||||
"checksum fuzzy-matcher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2db8c952765d7250f5fa509db7f6510ceef4be8672affc9bdbeffa2695923f67"
|
"checksum fuzzy-matcher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2db8c952765d7250f5fa509db7f6510ceef4be8672affc9bdbeffa2695923f67"
|
||||||
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
|
||||||
|
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
|
||||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
|
||||||
"checksum libdbus-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69382cb3be797cfe62ba89ac5e594a0e3022914ff44867ffbcfc03daa24eebd7"
|
"checksum libdbus-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "69382cb3be797cfe62ba89ac5e594a0e3022914ff44867ffbcfc03daa24eebd7"
|
||||||
@@ -678,21 +750,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum pest_meta 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f249ea6de7c7b7aba92b4ff4376a994c6dbd98fd2166c89d5c4947397ecb574d"
|
"checksum pest_meta 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f249ea6de7c7b7aba92b4ff4376a994c6dbd98fd2166c89d5c4947397ecb574d"
|
||||||
"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af"
|
"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af"
|
||||||
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
|
||||||
|
"checksum proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95"
|
||||||
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
|
||||||
|
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
|
||||||
"checksum rcalc_lib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2497fc2d7578e75282922918d96de8d4790714312900d95b6d5f577168f2656e"
|
"checksum rcalc_lib 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2497fc2d7578e75282922918d96de8d4790714312900d95b6d5f577168f2656e"
|
||||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||||
"checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
|
"checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2"
|
||||||
"checksum rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "654103d61a05074b268a107cf6581ce120f0fc0115f2610ed9dfea363bb81139"
|
"checksum rusttype 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)" = "654103d61a05074b268a107cf6581ce120f0fc0115f2610ed9dfea363bb81139"
|
||||||
|
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
|
||||||
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
|
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
|
||||||
|
"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f"
|
||||||
|
"checksum serde_derive 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "cb4dc18c61206b08dc98216c98faa0232f4337e1e1b8574551d5bad29ea1b425"
|
||||||
|
"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704"
|
||||||
"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
|
"checksum sha-1 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "23962131a91661d643c98940b20fcaffe62d776a823247be80a48fcb8b6fce68"
|
||||||
"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
"checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
|
||||||
"checksum smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "93960e8975909fcb14cc755de93af2149d8b8f4eb368315537d40cfd0f324054"
|
"checksum smithay-client-toolkit 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "93960e8975909fcb14cc755de93af2149d8b8f4eb368315537d40cfd0f324054"
|
||||||
"checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba"
|
"checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba"
|
||||||
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
|
||||||
|
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
|
||||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||||
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||||
"checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2"
|
"checksum ucd-trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8f00ed7be0c1ff1e24f46c3d2af4859f7e863672ba3a6e92e7cff702bf9f06c2"
|
||||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
|
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
|
||||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
|
"checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e"
|
||||||
"checksum wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "80d909ba6616dd772686f421e111f039402a13acecf395bcb4fc665277a504e0"
|
"checksum wayland-client 0.23.5 (registry+https://github.com/rust-lang/crates.io-index)" = "80d909ba6616dd772686f421e111f039402a13acecf395bcb4fc665277a504e0"
|
||||||
|
|||||||
+4
-1
@@ -12,6 +12,7 @@ default = [ "rcalc" ]
|
|||||||
ivy = [ "libivy" ]
|
ivy = [ "libivy" ]
|
||||||
bc = []
|
bc = []
|
||||||
rcalc = [ "rcalc_lib" ]
|
rcalc = [ "rcalc_lib" ]
|
||||||
|
damage_debug = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
smithay-client-toolkit = "0.6.4"
|
smithay-client-toolkit = "0.6.4"
|
||||||
@@ -30,4 +31,6 @@ lazy_static = "1.4"
|
|||||||
libivy = { git = "https://github.com/kennylevinsen/libivy.git", optional = true }
|
libivy = { git = "https://github.com/kennylevinsen/libivy.git", optional = true }
|
||||||
rcalc_lib = { version = "0.9", optional = true }
|
rcalc_lib = { version = "0.9", optional = true }
|
||||||
rust-ini = "0.13"
|
rust-ini = "0.13"
|
||||||
shlex = "0.1"
|
shlex = "0.1"
|
||||||
|
serde_json = "1.0"
|
||||||
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"widget": {
|
||||||
|
"margin": {
|
||||||
|
"margins": [20,20,20,20],
|
||||||
|
"widget": {
|
||||||
|
"verticalLayout": [{
|
||||||
|
"horizontalLayout": [{
|
||||||
|
"margin": {
|
||||||
|
"margins": [0,88,0,32],
|
||||||
|
"widget": {
|
||||||
|
"verticalLayout": [
|
||||||
|
{"date": {"font_size": 64.0}},
|
||||||
|
{"clock": {"font_size": 256.0}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"verticalLayout": [{
|
||||||
|
"margin": {
|
||||||
|
"margins": [0,0,0,8],
|
||||||
|
"widget": {"battery": {"font_size": 24.0,"length": 600}}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"margin": {
|
||||||
|
"margins": [0,0,0,8],
|
||||||
|
"widget": {"backlight": {"font_size": 24.0,"length": 600}}
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
"margin": {
|
||||||
|
"margins": [0,0,0,8],
|
||||||
|
"widget": {"pulseAudio": {"font_size": 24.0,"length": 600}}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]},
|
||||||
|
{"calendar": {"font_size": 16.0,"sections": 3}},
|
||||||
|
{"launcher": {"font_size": 32.0,"length": 1200}}
|
||||||
|
]}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"outputMode": "active",
|
||||||
|
"scale": 1
|
||||||
|
}
|
||||||
+104
-149
@@ -1,12 +1,12 @@
|
|||||||
use std::cmp::max;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::mpsc::{channel, Sender};
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
|
|
||||||
use smithay_client_toolkit::keyboard::{keysyms, map_keyboard_auto, Event as KbEvent, KeyState};
|
use smithay_client_toolkit::keyboard::{
|
||||||
use smithay_client_toolkit::utils::DoubleMemPool;
|
keysyms, map_keyboard_auto, Event as KbEvent, KeyState, ModifiersState,
|
||||||
|
};
|
||||||
|
|
||||||
use wayland_client::protocol::{wl_compositor, wl_output, wl_pointer, wl_shm, wl_surface};
|
use wayland_client::protocol::{wl_compositor, wl_output, wl_pointer, wl_shm, wl_surface};
|
||||||
use wayland_client::{Display, EventQueue, GlobalEvent, GlobalManager, NewProxy};
|
use wayland_client::{Display, EventQueue, GlobalEvent, GlobalManager, NewProxy};
|
||||||
@@ -16,16 +16,10 @@ use wayland_protocols::wlr::unstable::layer_shell::v1::client::{
|
|||||||
|
|
||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
|
use crate::widget::{DrawContext, Widget};
|
||||||
|
|
||||||
use crate::cmd::Cmd;
|
use crate::cmd::Cmd;
|
||||||
|
use crate::doublemempool::DoubleMemPool;
|
||||||
use crate::modules::backlight::Backlight;
|
|
||||||
use crate::modules::battery::UpowerBattery;
|
|
||||||
use crate::modules::calendar::Calendar;
|
|
||||||
use crate::modules::clock::Clock;
|
|
||||||
use crate::modules::launcher::Launcher;
|
|
||||||
use crate::modules::module::{Input, Module};
|
|
||||||
use crate::modules::sound::PulseAudio;
|
|
||||||
|
|
||||||
pub enum OutputMode {
|
pub enum OutputMode {
|
||||||
Active,
|
Active,
|
||||||
@@ -39,7 +33,6 @@ struct AppInner {
|
|||||||
configured_surfaces: Arc<Mutex<usize>>,
|
configured_surfaces: Arc<Mutex<usize>>,
|
||||||
outputs: Vec<(u32, wl_output::WlOutput)>,
|
outputs: Vec<(u32, wl_output::WlOutput)>,
|
||||||
shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
|
shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
|
||||||
dimensions: (u32, u32),
|
|
||||||
draw_tx: Sender<Cmd>,
|
draw_tx: Sender<Cmd>,
|
||||||
output_mode: OutputMode,
|
output_mode: OutputMode,
|
||||||
visible: bool,
|
visible: bool,
|
||||||
@@ -55,7 +48,6 @@ impl AppInner {
|
|||||||
configured_surfaces: Arc::new(Mutex::new(0)),
|
configured_surfaces: Arc::new(Mutex::new(0)),
|
||||||
outputs: Vec::new(),
|
outputs: Vec::new(),
|
||||||
shell: None,
|
shell: None,
|
||||||
dimensions: (0, 0),
|
|
||||||
draw_tx: tx,
|
draw_tx: tx,
|
||||||
output_mode: output_mode,
|
output_mode: output_mode,
|
||||||
visible: true,
|
visible: true,
|
||||||
@@ -66,7 +58,6 @@ impl AppInner {
|
|||||||
fn add_shell_surface(
|
fn add_shell_surface(
|
||||||
compositor: &wl_compositor::WlCompositor,
|
compositor: &wl_compositor::WlCompositor,
|
||||||
shell: &zwlr_layer_shell_v1::ZwlrLayerShellV1,
|
shell: &zwlr_layer_shell_v1::ZwlrLayerShellV1,
|
||||||
dimensions: (u32, u32),
|
|
||||||
scale: u32,
|
scale: u32,
|
||||||
configured_surfaces: Arc<Mutex<usize>>,
|
configured_surfaces: Arc<Mutex<usize>>,
|
||||||
tx: Sender<Cmd>,
|
tx: Sender<Cmd>,
|
||||||
@@ -79,6 +70,8 @@ impl AppInner {
|
|||||||
.create_surface(NewProxy::implement_dummy)
|
.create_surface(NewProxy::implement_dummy)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let this_is_stupid = Arc::new(Mutex::new(false));
|
||||||
|
|
||||||
let shell_surface = shell
|
let shell_surface = shell
|
||||||
.get_layer_surface(
|
.get_layer_surface(
|
||||||
&surface,
|
&surface,
|
||||||
@@ -89,9 +82,13 @@ impl AppInner {
|
|||||||
layer.implement_closure(
|
layer.implement_closure(
|
||||||
move |evt, layer| match evt {
|
move |evt, layer| match evt {
|
||||||
zwlr_layer_surface_v1::Event::Configure { serial, .. } => {
|
zwlr_layer_surface_v1::Event::Configure { serial, .. } => {
|
||||||
*(configured_surfaces.lock().unwrap()) += 1;
|
let mut x = this_is_stupid.lock().unwrap();
|
||||||
layer.ack_configure(serial);
|
if !*x {
|
||||||
tx.send(Cmd::ForceDraw).unwrap();
|
*x = true;
|
||||||
|
*(configured_surfaces.lock().unwrap()) += 1;
|
||||||
|
layer.ack_configure(serial);
|
||||||
|
tx.send(Cmd::ForceDraw).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
@@ -102,7 +99,6 @@ impl AppInner {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
shell_surface.set_keyboard_interactivity(1);
|
shell_surface.set_keyboard_interactivity(1);
|
||||||
shell_surface.set_size(dimensions.0/scale, dimensions.1/scale);
|
|
||||||
surface.set_buffer_scale(scale as i32);
|
surface.set_buffer_scale(scale as i32);
|
||||||
surface.commit();
|
surface.commit();
|
||||||
(surface, shell_surface)
|
(surface, shell_surface)
|
||||||
@@ -136,7 +132,6 @@ impl AppInner {
|
|||||||
let (surface, shell_surface) = AppInner::add_shell_surface(
|
let (surface, shell_surface) = AppInner::add_shell_surface(
|
||||||
&compositor,
|
&compositor,
|
||||||
&shell,
|
&shell,
|
||||||
self.dimensions,
|
|
||||||
self.scale,
|
self.scale,
|
||||||
self.configured_surfaces.clone(),
|
self.configured_surfaces.clone(),
|
||||||
self.draw_tx.clone(),
|
self.draw_tx.clone(),
|
||||||
@@ -152,7 +147,6 @@ impl AppInner {
|
|||||||
let (surface, shell_surface) = AppInner::add_shell_surface(
|
let (surface, shell_surface) = AppInner::add_shell_surface(
|
||||||
&compositor,
|
&compositor,
|
||||||
&shell,
|
&shell,
|
||||||
self.dimensions,
|
|
||||||
self.scale,
|
self.scale,
|
||||||
self.configured_surfaces.clone(),
|
self.configured_surfaces.clone(),
|
||||||
self.draw_tx.clone(),
|
self.draw_tx.clone(),
|
||||||
@@ -201,10 +195,6 @@ impl AppInner {
|
|||||||
fn set_shell(&mut self, shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>) {
|
fn set_shell(&mut self, shell: Option<zwlr_layer_shell_v1::ZwlrLayerShellV1>) {
|
||||||
self.shell = shell
|
self.shell = shell
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_dimensions(&mut self, dimensions: (u32, u32)) {
|
|
||||||
self.dimensions = dimensions
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
@@ -212,12 +202,19 @@ pub struct App {
|
|||||||
display: Display,
|
display: Display,
|
||||||
event_queue: EventQueue,
|
event_queue: EventQueue,
|
||||||
cmd_queue: Arc<Mutex<VecDeque<Cmd>>>,
|
cmd_queue: Arc<Mutex<VecDeque<Cmd>>>,
|
||||||
modules: Vec<Module>,
|
widget: Option<Box<dyn Widget + Send>>,
|
||||||
inner: Arc<Mutex<AppInner>>,
|
inner: Arc<Mutex<AppInner>>,
|
||||||
|
last_damage: Option<Vec<(i32, i32, i32, i32)>>,
|
||||||
|
last_dim: (u32, u32),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn redraw(&mut self, force: bool) -> Result<(), ::std::io::Error> {
|
pub fn redraw(&mut self, mut force: bool) -> Result<(), ::std::io::Error> {
|
||||||
|
let widget = match self.widget {
|
||||||
|
Some(ref mut widget) => widget,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
let inner = self.inner.lock().unwrap();
|
let inner = self.inner.lock().unwrap();
|
||||||
let time = Local::now();
|
let time = Local::now();
|
||||||
|
|
||||||
@@ -226,41 +223,56 @@ impl App {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let pool = match self.pools.pool() {
|
let (last, pool) = match self.pools.pool() {
|
||||||
Some(pool) => pool,
|
Some((last, pool)) => (last, pool),
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (buf_x, buf_y) = inner.dimensions;
|
let size = widget.size();
|
||||||
|
let size_changed = self.last_dim != size;
|
||||||
let bg = Color::new(0.0, 0.0, 0.0, 0.9);
|
|
||||||
|
|
||||||
// resize the pool if relevant
|
// resize the pool if relevant
|
||||||
|
pool.resize((4 * size.0 * size.1) as usize)
|
||||||
pool.resize((4 * buf_x * buf_y) as usize)
|
|
||||||
.expect("Failed to resize the memory pool.");
|
.expect("Failed to resize the memory pool.");
|
||||||
|
|
||||||
let mmap = pool.mmap();
|
let mmap = pool.mmap();
|
||||||
let mut buf = Buffer::new(mmap, inner.dimensions);
|
let mut buf = Buffer::new(mmap, size);
|
||||||
|
|
||||||
|
// Copy old damage
|
||||||
|
if let Some(d) = &self.last_damage {
|
||||||
|
if !size_changed {
|
||||||
|
let lastmmap = last.mmap();
|
||||||
|
let last = Buffer::new(lastmmap, size);
|
||||||
|
|
||||||
|
if cfg!(feature = "damage_debug") {
|
||||||
|
buf.memset(&Color::new(0.5, 0.75, 0.75, 1.0));
|
||||||
|
}
|
||||||
|
for d in d {
|
||||||
|
last.copy_to(&mut buf, d.clone());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
force = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
force = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bg = Color::new(0.0, 0.0, 0.0, 0.9);
|
||||||
if force {
|
if force {
|
||||||
buf.memset(&bg);
|
buf.memset(&bg);
|
||||||
}
|
}
|
||||||
let mut damage = vec![];
|
let report = widget.draw(
|
||||||
|
&mut DrawContext {
|
||||||
{
|
buf: &mut buf,
|
||||||
let mut margin_buf = buf.subdimensions((20, 20, buf_x - 40, buf_y - 40))?;
|
bg: &bg,
|
||||||
for module in self.modules.iter() {
|
time: &time,
|
||||||
if module.update(&time, force)? {
|
force,
|
||||||
let mut b = &mut margin_buf.subdimensions(module.get_bounds())?;
|
},
|
||||||
let mut d = module.draw(&mut b, &bg, &time)?;
|
(0, 0),
|
||||||
damage.append(&mut d);
|
)?;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mmap.flush().unwrap();
|
mmap.flush().unwrap();
|
||||||
|
|
||||||
if damage.len() == 0 {
|
if !size_changed && !report.full_damage && report.damage.len() == 0 {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@@ -268,22 +280,33 @@ impl App {
|
|||||||
// get a buffer and attach it
|
// get a buffer and attach it
|
||||||
let new_buffer = pool.buffer(
|
let new_buffer = pool.buffer(
|
||||||
0,
|
0,
|
||||||
buf_x as i32,
|
report.width as i32,
|
||||||
buf_y as i32,
|
report.height as i32,
|
||||||
4 * buf_x as i32,
|
4 * size.0 as i32,
|
||||||
wl_shm::Format::Argb8888,
|
wl_shm::Format::Argb8888,
|
||||||
);
|
);
|
||||||
|
if size_changed {
|
||||||
|
for shell_surface in inner.shell_surfaces.iter() {
|
||||||
|
shell_surface.set_size(size.0 / inner.scale, size.1 / inner.scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
for surface in inner.surfaces.iter() {
|
for surface in inner.surfaces.iter() {
|
||||||
surface.attach(Some(&new_buffer), 0, 0);
|
surface.attach(Some(&new_buffer), 0, 0);
|
||||||
if force {
|
if cfg!(feature = "damage_debug") || force || report.full_damage {
|
||||||
surface.damage_buffer(0, 0, buf_x as i32, buf_y as i32);
|
surface.damage_buffer(0, 0, size.0 as i32, size.1 as i32);
|
||||||
} else {
|
} else {
|
||||||
for d in damage.iter() {
|
for d in report.damage.iter() {
|
||||||
surface.damage_buffer(d.0, d.1, d.2, d.3);
|
surface.damage_buffer(d.0, d.1, d.2, d.3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
surface.commit();
|
surface.commit();
|
||||||
}
|
}
|
||||||
|
self.last_damage = if force || report.full_damage {
|
||||||
|
Some(vec![(0, 0, size.0 as i32, size.1 as i32)])
|
||||||
|
} else {
|
||||||
|
Some(report.damage)
|
||||||
|
};
|
||||||
|
self.last_dim = size;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,36 +328,13 @@ impl App {
|
|||||||
&mut self.event_queue
|
&mut self.event_queue
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wipe(&mut self) {
|
pub fn get_widget(&mut self) -> &mut Box<dyn Widget + Send> {
|
||||||
let inner = self.inner.lock().unwrap();
|
self.widget.as_mut().unwrap()
|
||||||
let pool = match self.pools.pool() {
|
|
||||||
Some(pool) => pool,
|
|
||||||
None => return,
|
|
||||||
};
|
|
||||||
pool.resize((4 * inner.dimensions.0 * inner.dimensions.1) as usize)
|
|
||||||
.expect("Failed to resize the memory pool.");
|
|
||||||
let mmap = pool.mmap();
|
|
||||||
let mut buf = Buffer::new(mmap, inner.dimensions);
|
|
||||||
let bg = Color::new(0.0, 0.0, 0.0, 0.9);
|
|
||||||
buf.memset(&bg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_modules<F>(&self, f: F)
|
pub fn set_widget(&mut self, w: Box<dyn Widget + Send>) -> Result<(), ::std::io::Error> {
|
||||||
where
|
self.widget = Some(w);
|
||||||
F: Fn(&Module),
|
self.redraw(true)
|
||||||
{
|
|
||||||
for m in self.modules.iter() {
|
|
||||||
f(m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_module(&self, pos: (u32, u32)) -> Option<&Module> {
|
|
||||||
for m in self.modules.iter() {
|
|
||||||
if m.intersect(pos) {
|
|
||||||
return Some(&m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(tx: Sender<Cmd>, output_mode: OutputMode, scale: u32) -> App {
|
pub fn new(tx: Sender<Cmd>, output_mode: OutputMode, scale: u32) -> App {
|
||||||
@@ -343,32 +343,6 @@ impl App {
|
|||||||
//
|
//
|
||||||
// Set up modules
|
// Set up modules
|
||||||
//
|
//
|
||||||
let (mod_tx, mod_rx) = channel();
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let mut modules = vec![
|
|
||||||
Module::new(Box::new(Clock::new(tx.clone())), (0, 0, 536, 320)),
|
|
||||||
Module::new(Box::new(Calendar::new()), (0, 368, 1232, 344)),
|
|
||||||
];
|
|
||||||
|
|
||||||
if let Ok(m) = Launcher::new(tx.clone()) {
|
|
||||||
modules.push(Module::new(Box::new(m), (0, 728, 1232, 32)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vert_off = 0;
|
|
||||||
if let Ok(m) = UpowerBattery::new(tx.clone()) {
|
|
||||||
modules.push(Module::new(Box::new(m), (640, vert_off, 592, 32)));
|
|
||||||
vert_off += 32;
|
|
||||||
}
|
|
||||||
if let Ok(m) = Backlight::new() {
|
|
||||||
modules.push(Module::new(Box::new(m), (640, vert_off, 592, 32)));
|
|
||||||
vert_off += 32;
|
|
||||||
}
|
|
||||||
if let Ok(m) = PulseAudio::new(tx.clone()) {
|
|
||||||
modules.push(Module::new(Box::new(m), (640, vert_off, 592, 32)));
|
|
||||||
}
|
|
||||||
|
|
||||||
mod_tx.send(modules).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let cmd_queue = Arc::new(Mutex::new(VecDeque::new()));
|
let cmd_queue = Arc::new(Mutex::new(VecDeque::new()));
|
||||||
|
|
||||||
@@ -435,7 +409,7 @@ impl App {
|
|||||||
})
|
})
|
||||||
.expect("server didn't advertise `wl_shm`");
|
.expect("server didn't advertise `wl_shm`");
|
||||||
|
|
||||||
let pools = DoubleMemPool::new(&shm, || {}).expect("Failed to create a memory pool !");
|
let pools = DoubleMemPool::new(&shm).expect("Failed to create a memory pool !");
|
||||||
|
|
||||||
//
|
//
|
||||||
// Get our seat
|
// Get our seat
|
||||||
@@ -457,11 +431,18 @@ impl App {
|
|||||||
} => match state {
|
} => match state {
|
||||||
KeyState::Pressed => match keysym {
|
KeyState::Pressed => match keysym {
|
||||||
keysyms::XKB_KEY_Escape => kbd_clone.lock().unwrap().push_back(Cmd::Exit),
|
keysyms::XKB_KEY_Escape => kbd_clone.lock().unwrap().push_back(Cmd::Exit),
|
||||||
v => kbd_clone.lock().unwrap().push_back(Cmd::KeyboardInput {
|
v => kbd_clone.lock().unwrap().push_back(Cmd::Keyboard {
|
||||||
input: Input::Keypress {
|
key: v,
|
||||||
key: v,
|
key_state: state,
|
||||||
interpreted: utf8,
|
modifiers_state: ModifiersState {
|
||||||
|
ctrl: false,
|
||||||
|
alt: false,
|
||||||
|
shift: false,
|
||||||
|
caps_lock: false,
|
||||||
|
logo: false,
|
||||||
|
num_lock: false,
|
||||||
},
|
},
|
||||||
|
interpreted: utf8,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
@@ -486,22 +467,6 @@ impl App {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
//
|
|
||||||
// Calculate window dimensions
|
|
||||||
//
|
|
||||||
let modules = mod_rx.recv().unwrap();
|
|
||||||
|
|
||||||
let mut dimensions = (0, 0);
|
|
||||||
for m in modules.iter() {
|
|
||||||
let b = m.get_bounds();
|
|
||||||
dimensions = (max(dimensions.0, b.0 + b.2), max(dimensions.1, b.1 + b.3));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add padding
|
|
||||||
inner
|
|
||||||
.lock()
|
|
||||||
.unwrap()
|
|
||||||
.set_dimensions((dimensions.0 + 40, dimensions.1 + 40));
|
|
||||||
inner.lock().unwrap().outputs_changed();
|
inner.lock().unwrap().outputs_changed();
|
||||||
event_queue.sync_roundtrip().unwrap();
|
event_queue.sync_roundtrip().unwrap();
|
||||||
|
|
||||||
@@ -547,31 +512,19 @@ impl App {
|
|||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
wl_pointer::Event::Frame => {
|
wl_pointer::Event::Frame => {
|
||||||
if pos.0 < 20 || pos.1 < 20 {
|
|
||||||
// Ignore stuff outside our margins
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let pos = (pos.0 - 20, pos.1 - 20);
|
|
||||||
if vert_scroll != 0.0 || horiz_scroll != 0.0 {
|
if vert_scroll != 0.0 || horiz_scroll != 0.0 {
|
||||||
pointer_clone.lock().unwrap().push_back(Cmd::MouseInput {
|
pointer_clone.lock().unwrap().push_back(Cmd::MouseScroll {
|
||||||
|
scroll: (horiz_scroll, vert_scroll),
|
||||||
pos: pos,
|
pos: pos,
|
||||||
input: Input::Scroll {
|
|
||||||
pos: pos,
|
|
||||||
x: horiz_scroll,
|
|
||||||
y: vert_scroll,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
vert_scroll = 0.0;
|
vert_scroll = 0.0;
|
||||||
horiz_scroll = 0.0;
|
horiz_scroll = 0.0;
|
||||||
}
|
}
|
||||||
if btn_clicked {
|
if btn_clicked {
|
||||||
pointer_clone.lock().unwrap().push_back(Cmd::MouseInput {
|
pointer_clone
|
||||||
pos: pos,
|
.lock()
|
||||||
input: Input::Click {
|
.unwrap()
|
||||||
pos: pos,
|
.push_back(Cmd::MouseClick { btn: btn, pos: pos });
|
||||||
button: btn,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
btn_clicked = false;
|
btn_clicked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -589,8 +542,10 @@ impl App {
|
|||||||
event_queue: event_queue,
|
event_queue: event_queue,
|
||||||
cmd_queue: cmd_queue,
|
cmd_queue: cmd_queue,
|
||||||
pools: pools,
|
pools: pools,
|
||||||
modules: modules,
|
widget: None,
|
||||||
inner: inner,
|
inner: inner,
|
||||||
|
last_damage: None,
|
||||||
|
last_dim: (0, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-10
@@ -38,13 +38,43 @@ impl<'a> Buffer<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn copy_to(&self, other: &mut Buffer, (x, y, width, height): (i32, i32, i32, i32)) {
|
||||||
|
debug_assert!(self.dimensions == other.dimensions);
|
||||||
|
debug_assert!(self.subdimensions.is_none() && other.subdimensions.is_none());
|
||||||
|
|
||||||
|
if x == 0 && width as u32 == self.dimensions.0 {
|
||||||
|
// Full-width copy
|
||||||
|
let offset = y as isize * self.dimensions.0 as isize;
|
||||||
|
let n = height as usize * self.dimensions.0 as usize;
|
||||||
|
unsafe {
|
||||||
|
std::ptr::copy(
|
||||||
|
(self.buf.as_ptr() as *const u32).offset(offset),
|
||||||
|
(other.buf.as_mut_ptr() as *mut u32).offset(offset),
|
||||||
|
n,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Row-by-row copy
|
||||||
|
for cur_y in y as isize..(y + height) as isize {
|
||||||
|
let offset = x as isize + cur_y as isize * self.dimensions.0 as isize;
|
||||||
|
unsafe {
|
||||||
|
std::ptr::copy(
|
||||||
|
(self.buf.as_ptr() as *const u32).offset(offset),
|
||||||
|
(other.buf.as_mut_ptr() as *mut u32).offset(offset),
|
||||||
|
width as usize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn subdimensions(
|
pub fn subdimensions(
|
||||||
&mut self,
|
&mut self,
|
||||||
subdimensions: (u32, u32, u32, u32),
|
subdimensions: (u32, u32, u32, u32),
|
||||||
) -> Result<Buffer, ::std::io::Error> {
|
) -> Result<Buffer, ::std::io::Error> {
|
||||||
let bounds = self.get_bounds();
|
let bounds = self.get_bounds();
|
||||||
if subdimensions.0 + subdimensions.2 > bounds.2
|
if subdimensions.0 + subdimensions.2 >= bounds.2
|
||||||
|| subdimensions.1 + subdimensions.3 > bounds.3
|
|| subdimensions.1 + subdimensions.3 >= bounds.3
|
||||||
{
|
{
|
||||||
return Err(::std::io::Error::new(
|
return Err(::std::io::Error::new(
|
||||||
::std::io::ErrorKind::Other,
|
::std::io::ErrorKind::Other,
|
||||||
@@ -67,14 +97,9 @@ impl<'a> Buffer<'a> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(
|
pub fn offset(&mut self, offset: (u32, u32)) -> Result<Buffer, ::std::io::Error> {
|
||||||
&mut self,
|
|
||||||
offset: (u32, u32),
|
|
||||||
) -> Result<Buffer, ::std::io::Error> {
|
|
||||||
let bounds = self.get_bounds();
|
let bounds = self.get_bounds();
|
||||||
if offset.0 > bounds.2
|
if offset.0 > bounds.2 || offset.1 > bounds.3 {
|
||||||
|| offset.1 > bounds.3
|
|
||||||
{
|
|
||||||
return Err(::std::io::Error::new(
|
return Err(::std::io::Error::new(
|
||||||
::std::io::ErrorKind::Other,
|
::std::io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
@@ -119,7 +144,7 @@ impl<'a> Buffer<'a> {
|
|||||||
|
|
||||||
pub fn put(&mut self, pos: (u32, u32), c: &Color) -> Result<(), ::std::io::Error> {
|
pub fn put(&mut self, pos: (u32, u32), c: &Color) -> Result<(), ::std::io::Error> {
|
||||||
let true_pos = if let Some(subdim) = self.subdimensions {
|
let true_pos = if let Some(subdim) = self.subdimensions {
|
||||||
if pos.0 > subdim.2 || pos.1 > subdim.3 {
|
if pos.0 >= subdim.2 || pos.1 >= subdim.3 {
|
||||||
return Err(::std::io::Error::new(
|
return Err(::std::io::Error::new(
|
||||||
::std::io::ErrorKind::Other,
|
::std::io::ErrorKind::Other,
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
+15
-3
@@ -1,10 +1,22 @@
|
|||||||
use crate::modules::module::Input;
|
use smithay_client_toolkit::keyboard::{KeyState, ModifiersState};
|
||||||
|
|
||||||
pub enum Cmd {
|
pub enum Cmd {
|
||||||
Exit,
|
Exit,
|
||||||
Draw,
|
Draw,
|
||||||
ForceDraw,
|
ForceDraw,
|
||||||
ToggleVisible,
|
ToggleVisible,
|
||||||
MouseInput { pos: (u32, u32), input: Input },
|
MouseClick {
|
||||||
KeyboardInput { input: Input },
|
btn: u32,
|
||||||
|
pos: (u32, u32),
|
||||||
|
},
|
||||||
|
MouseScroll {
|
||||||
|
scroll: (f64, f64),
|
||||||
|
pos: (u32, u32),
|
||||||
|
},
|
||||||
|
Keyboard {
|
||||||
|
key: u32,
|
||||||
|
key_state: KeyState,
|
||||||
|
modifiers_state: ModifiersState,
|
||||||
|
interpreted: Option<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
+202
@@ -0,0 +1,202 @@
|
|||||||
|
use crate::cmd::Cmd;
|
||||||
|
use crate::widget;
|
||||||
|
use crate::widgets;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::default::Default;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum Widget {
|
||||||
|
Margin {
|
||||||
|
margins: (u32, u32, u32, u32),
|
||||||
|
widget: Box<Widget>,
|
||||||
|
},
|
||||||
|
Fixed {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
widget: Box<Widget>,
|
||||||
|
},
|
||||||
|
HorizontalLayout(Vec<Box<Widget>>),
|
||||||
|
VerticalLayout(Vec<Box<Widget>>),
|
||||||
|
Clock {
|
||||||
|
font_size: f32,
|
||||||
|
},
|
||||||
|
Date {
|
||||||
|
font_size: f32,
|
||||||
|
},
|
||||||
|
Calendar {
|
||||||
|
font_size: f32,
|
||||||
|
sections: u32,
|
||||||
|
},
|
||||||
|
Launcher {
|
||||||
|
font_size: f32,
|
||||||
|
length: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
app_opener: String,
|
||||||
|
#[serde(default)]
|
||||||
|
term_opener: String,
|
||||||
|
#[serde(default)]
|
||||||
|
url_opener: String,
|
||||||
|
},
|
||||||
|
Battery {
|
||||||
|
font_size: f32,
|
||||||
|
length: u32,
|
||||||
|
},
|
||||||
|
Backlight {
|
||||||
|
font_size: f32,
|
||||||
|
length: u32,
|
||||||
|
},
|
||||||
|
PulseAudio {
|
||||||
|
font_size: f32,
|
||||||
|
length: u32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget {
|
||||||
|
pub fn construct(self, tx: Sender<Cmd>) -> Option<Box<dyn widget::Widget + Send>> {
|
||||||
|
match self {
|
||||||
|
Widget::Margin {
|
||||||
|
margins,
|
||||||
|
widget,
|
||||||
|
} => match widget.construct(tx.clone()) {
|
||||||
|
Some(w) => Some(widget::Margin::new(margins, w)),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
Widget::Fixed {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
widget,
|
||||||
|
} => match widget.construct(tx.clone()) {
|
||||||
|
Some(w) => Some(widget::Fixed::new((width, height), w)),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
Widget::HorizontalLayout(widgets) => Some(widget::HorizontalLayout::new(
|
||||||
|
widgets
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.construct(tx.clone()))
|
||||||
|
.filter(|x| x.is_some())
|
||||||
|
.map(|x| x.unwrap())
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
Widget::VerticalLayout(widgets) => Some(widget::VerticalLayout::new(
|
||||||
|
widgets
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| x.construct(tx.clone()))
|
||||||
|
.filter(|x| x.is_some())
|
||||||
|
.map(|x| x.unwrap())
|
||||||
|
.collect(),
|
||||||
|
)),
|
||||||
|
Widget::Clock { font_size } => Some(widgets::clock::Clock::new(font_size, tx.clone())),
|
||||||
|
Widget::Date { font_size } => Some(widgets::date::Date::new(font_size)),
|
||||||
|
Widget::Calendar {
|
||||||
|
font_size,
|
||||||
|
sections,
|
||||||
|
} => Some(widgets::calendar::Calendar::new(font_size, sections)),
|
||||||
|
Widget::Launcher { font_size, length, app_opener, term_opener, url_opener } => Some(widgets::launcher::Launcher::new(
|
||||||
|
font_size,
|
||||||
|
length,
|
||||||
|
tx.clone(),
|
||||||
|
app_opener,
|
||||||
|
term_opener,
|
||||||
|
if url_opener.len() == 0 { "xdg_open ".to_string() } else { url_opener },
|
||||||
|
)),
|
||||||
|
Widget::Battery { font_size, length } => {
|
||||||
|
match widgets::battery::UpowerBattery::new(font_size, length, tx.clone()) {
|
||||||
|
Ok(w) => Some(w),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Widget::Backlight { font_size, length } => {
|
||||||
|
match widgets::backlight::Backlight::new(font_size, length) {
|
||||||
|
Ok(w) => Some(w),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Widget::PulseAudio { font_size, length } => {
|
||||||
|
match widgets::sound::PulseAudio::new(font_size, length, tx.clone()) {
|
||||||
|
Ok(w) => Some(w),
|
||||||
|
Err(_) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum OutputMode {
|
||||||
|
All,
|
||||||
|
Active,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OutputMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
OutputMode::Active
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct Config {
|
||||||
|
pub widget: Widget,
|
||||||
|
pub output_mode: OutputMode,
|
||||||
|
pub scale: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Config{
|
||||||
|
widget: Widget::Margin {
|
||||||
|
margins: (20, 20, 20, 20),
|
||||||
|
widget: Box::new(Widget::VerticalLayout(vec![
|
||||||
|
Box::new(Widget::HorizontalLayout(vec![
|
||||||
|
Box::new(Widget::Margin {
|
||||||
|
margins: (0, 88, 0, 32),
|
||||||
|
widget: Box::new(Widget::VerticalLayout(vec![
|
||||||
|
Box::new(Widget::Date { font_size: 64.0 }),
|
||||||
|
Box::new(Widget::Clock { font_size: 256.0 }),
|
||||||
|
])),
|
||||||
|
}),
|
||||||
|
Box::new(Widget::VerticalLayout(vec![
|
||||||
|
Box::new(Widget::Margin {
|
||||||
|
margins: (0, 0, 0, 8),
|
||||||
|
widget: Box::new(Widget::Battery {
|
||||||
|
font_size: 24.0,
|
||||||
|
length: 600,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Box::new(Widget::Margin {
|
||||||
|
margins: (0, 0, 0, 8),
|
||||||
|
widget: Box::new(Widget::Backlight {
|
||||||
|
font_size: 24.0,
|
||||||
|
length: 600,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
Box::new(Widget::Margin {
|
||||||
|
margins: (0, 0, 0, 8),
|
||||||
|
widget: Box::new(Widget::PulseAudio {
|
||||||
|
font_size: 24.0,
|
||||||
|
length: 600,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
])),
|
||||||
|
])),
|
||||||
|
Box::new(Widget::Calendar {
|
||||||
|
font_size: 16.0,
|
||||||
|
sections: 3,
|
||||||
|
}),
|
||||||
|
Box::new(Widget::Launcher {
|
||||||
|
font_size: 32.0,
|
||||||
|
length: 1200,
|
||||||
|
app_opener: "".to_string(),
|
||||||
|
term_opener: "".to_string(),
|
||||||
|
url_opener: "".to_string(),
|
||||||
|
}),
|
||||||
|
])),
|
||||||
|
},
|
||||||
|
output_mode: Default::default(),
|
||||||
|
scale: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
-1
@@ -20,7 +20,13 @@ pub struct Desktop {
|
|||||||
|
|
||||||
impl Desktop {
|
impl Desktop {
|
||||||
fn parse(f: &str) -> Result<Desktop, Box<dyn Error>> {
|
fn parse(f: &str) -> Result<Desktop, Box<dyn Error>> {
|
||||||
let file = Ini::load_from_file_opt(f, ParseOption{ enabled_quote: false, enabled_escape: false })?;
|
let file = Ini::load_from_file_opt(
|
||||||
|
f,
|
||||||
|
ParseOption {
|
||||||
|
enabled_quote: false,
|
||||||
|
enabled_escape: false,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
match file.section(Some("Desktop Entry").to_owned()) {
|
match file.section(Some("Desktop Entry").to_owned()) {
|
||||||
Some(desktop) => Ok(Desktop {
|
Some(desktop) => Ok(Desktop {
|
||||||
entry_type: desktop.get("Type").unwrap_or(&"".to_string()).to_string(),
|
entry_type: desktop.get("Type").unwrap_or(&"".to_string()).to_string(),
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use smithay_client_toolkit::utils::MemPool;
|
||||||
|
use wayland_client::protocol::wl_shm;
|
||||||
|
|
||||||
|
pub struct DoubleMemPool {
|
||||||
|
pool1: MemPool,
|
||||||
|
pool2: MemPool,
|
||||||
|
switch: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DoubleMemPool {
|
||||||
|
pub fn new(shm: &wl_shm::WlShm) -> ::std::io::Result<DoubleMemPool> {
|
||||||
|
Ok(DoubleMemPool {
|
||||||
|
pool1: MemPool::new(shm, move || {})?,
|
||||||
|
pool2: MemPool::new(shm, move || {})?,
|
||||||
|
switch: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pool(&mut self) -> Option<(&mut MemPool, &mut MemPool)> {
|
||||||
|
let switch = self.switch;
|
||||||
|
self.switch = !self.switch;
|
||||||
|
let (last, cur) = if switch {
|
||||||
|
(&mut self.pool2, &mut self.pool1)
|
||||||
|
} else {
|
||||||
|
(&mut self.pool1, &mut self.pool2)
|
||||||
|
};
|
||||||
|
|
||||||
|
if cur.is_used() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some((last, cur))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
-27
@@ -3,6 +3,8 @@ use std::io::{BufRead, BufReader, Read, Write};
|
|||||||
use std::os::unix::io::AsRawFd;
|
use std::os::unix::io::AsRawFd;
|
||||||
use std::os::unix::net::{UnixListener, UnixStream};
|
use std::os::unix::net::{UnixListener, UnixStream};
|
||||||
use std::sync::mpsc::channel;
|
use std::sync::mpsc::channel;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::default::Default;
|
||||||
|
|
||||||
use nix::poll::{poll, PollFd, PollFlags};
|
use nix::poll::{poll, PollFd, PollFlags};
|
||||||
use os_pipe::pipe;
|
use os_pipe::pipe;
|
||||||
@@ -11,9 +13,12 @@ mod app;
|
|||||||
mod buffer;
|
mod buffer;
|
||||||
mod cmd;
|
mod cmd;
|
||||||
mod color;
|
mod color;
|
||||||
mod draw;
|
mod config;
|
||||||
mod modules;
|
|
||||||
mod desktop;
|
mod desktop;
|
||||||
|
mod doublemempool;
|
||||||
|
mod draw;
|
||||||
|
mod widget;
|
||||||
|
mod widgets;
|
||||||
|
|
||||||
use app::{App, OutputMode};
|
use app::{App, OutputMode};
|
||||||
use cmd::Cmd;
|
use cmd::Cmd;
|
||||||
@@ -22,6 +27,7 @@ enum Mode {
|
|||||||
Start,
|
Start,
|
||||||
StartOrKill,
|
StartOrKill,
|
||||||
ToggleVisible,
|
ToggleVisible,
|
||||||
|
PrintConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -29,11 +35,24 @@ fn main() {
|
|||||||
Ok(dir) => dir + "/wldash",
|
Ok(dir) => dir + "/wldash",
|
||||||
Err(_) => "/tmp/wldash".to_string(),
|
Err(_) => "/tmp/wldash".to_string(),
|
||||||
};
|
};
|
||||||
let scale = match env::var("WLDASH_SCALE") {
|
let config_home = match env::var("XDG_CONFIG_HOME") {
|
||||||
Ok(s) => s.parse().unwrap(),
|
Ok(dir) => dir + "/wldash",
|
||||||
Err(_) => 1,
|
Err(_) => match env::var("HOME") {
|
||||||
|
Ok(home) => home + "/.config/wldash",
|
||||||
|
Err(_) => panic!("unable to find user folder"),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let config: config::Config = match File::open(config_home + "/config.json") {
|
||||||
|
Ok(f) => {
|
||||||
|
let reader = BufReader::new(f);
|
||||||
|
serde_json::from_reader(reader).unwrap()
|
||||||
|
},
|
||||||
|
Err(_) => Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let scale = config.scale;
|
||||||
|
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let mode = match args.len() {
|
let mode = match args.len() {
|
||||||
1 => Mode::Start,
|
1 => Mode::Start,
|
||||||
@@ -41,6 +60,7 @@ fn main() {
|
|||||||
"start" => Mode::Start,
|
"start" => Mode::Start,
|
||||||
"start-or-kill" => Mode::StartOrKill,
|
"start-or-kill" => Mode::StartOrKill,
|
||||||
"toggle-visible" => Mode::ToggleVisible,
|
"toggle-visible" => Mode::ToggleVisible,
|
||||||
|
"print-config" => Mode::PrintConfig,
|
||||||
s => {
|
s => {
|
||||||
eprintln!("unsupported sub-command {}", s);
|
eprintln!("unsupported sub-command {}", s);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
@@ -73,23 +93,37 @@ fn main() {
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Mode::PrintConfig => {
|
||||||
|
println!("{}", serde_json::to_string_pretty(&config).unwrap());
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = std::fs::remove_file(socket_path.clone());
|
let _ = std::fs::remove_file(socket_path.clone());
|
||||||
|
|
||||||
let listener = UnixListener::bind(socket_path.clone()).unwrap();
|
let listener = UnixListener::bind(socket_path.clone()).unwrap();
|
||||||
|
|
||||||
|
let output_mode = match config.output_mode {
|
||||||
|
config::OutputMode::All => OutputMode::All,
|
||||||
|
config::OutputMode::Active => OutputMode::Active,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (tx_draw, rx_draw) = channel();
|
||||||
|
let tx_draw_mod = tx_draw.clone();
|
||||||
|
let (mod_tx, mod_rx) = channel();
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
// Print, write to a file, or send to an HTTP server.
|
||||||
|
match config.widget.construct(tx_draw_mod) {
|
||||||
|
Some(w) => mod_tx.send(w).unwrap(),
|
||||||
|
None => panic!("no widget configured"),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut app = App::new(tx_draw, output_mode, scale);
|
||||||
|
let widget = mod_rx.recv().unwrap();
|
||||||
|
app.set_widget(widget).unwrap();
|
||||||
|
|
||||||
let (mut rx_pipe, mut tx_pipe) = pipe().unwrap();
|
let (mut rx_pipe, mut tx_pipe) = pipe().unwrap();
|
||||||
let ipc_pipe = tx_pipe.try_clone().unwrap();
|
let ipc_pipe = tx_pipe.try_clone().unwrap();
|
||||||
let (tx_draw, rx_draw) = channel();
|
|
||||||
|
|
||||||
let output_mode = match env::var("WLDASH_ALL_OUTPUTS") {
|
|
||||||
Ok(_) => OutputMode::All,
|
|
||||||
Err(_) => OutputMode::Active,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut app = App::new(tx_draw, output_mode, scale);
|
|
||||||
app.wipe();
|
|
||||||
|
|
||||||
let worker_queue = app.cmd_queue();
|
let worker_queue = app.cmd_queue();
|
||||||
let _ = std::thread::Builder::new()
|
let _ = std::thread::Builder::new()
|
||||||
@@ -156,18 +190,22 @@ fn main() {
|
|||||||
app.redraw(true).expect("Failed to draw");
|
app.redraw(true).expect("Failed to draw");
|
||||||
app.flush_display();
|
app.flush_display();
|
||||||
}
|
}
|
||||||
Cmd::MouseInput { pos, input } => {
|
Cmd::MouseClick { btn, pos } => {
|
||||||
if let Some(m) = app.get_module(pos) {
|
app.get_widget().mouse_click(btn, pos);
|
||||||
let bounds = m.get_bounds();
|
q.lock().unwrap().push_back(Cmd::Draw);
|
||||||
let input = input.offset((bounds.0, bounds.1));
|
|
||||||
m.input(input);
|
|
||||||
q.lock().unwrap().push_back(Cmd::Draw);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Cmd::KeyboardInput { input } => {
|
Cmd::MouseScroll { scroll, pos } => {
|
||||||
app.with_modules(|m| {
|
app.get_widget().mouse_scroll(scroll, pos);
|
||||||
m.input(input.clone());
|
q.lock().unwrap().push_back(Cmd::Draw);
|
||||||
});
|
}
|
||||||
|
Cmd::Keyboard {
|
||||||
|
key,
|
||||||
|
key_state,
|
||||||
|
modifiers_state,
|
||||||
|
interpreted,
|
||||||
|
} => {
|
||||||
|
app.get_widget()
|
||||||
|
.keyboard_input(key, modifiers_state, key_state, interpreted);
|
||||||
q.lock().unwrap().push_back(Cmd::Draw);
|
q.lock().unwrap().push_back(Cmd::Draw);
|
||||||
}
|
}
|
||||||
Cmd::ToggleVisible => {
|
Cmd::ToggleVisible => {
|
||||||
@@ -201,7 +239,7 @@ fn main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if fds[1].revents().unwrap().contains(PollFlags::POLLIN) {
|
if fds[1].revents().unwrap().contains(PollFlags::POLLIN) {
|
||||||
let mut v: Vec<u8> = vec![0x00];
|
let mut v = [0x00];
|
||||||
rx_pipe.read_exact(&mut v).unwrap();
|
rx_pipe.read_exact(&mut v).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
use crate::buffer::Buffer;
|
|
||||||
use crate::cmd::Cmd;
|
|
||||||
use crate::color::Color;
|
|
||||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
|
||||||
use crate::modules::module::{Input, ModuleImpl};
|
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
|
||||||
|
|
||||||
use chrono::{DateTime, Datelike, Duration, Local, Timelike};
|
|
||||||
|
|
||||||
pub struct Clock {
|
|
||||||
cur_time: DateTime<Local>,
|
|
||||||
first_draw: bool,
|
|
||||||
clock_cache: Font,
|
|
||||||
date_cache: Font,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clock {
|
|
||||||
pub fn new(ch: Sender<Cmd>) -> Clock {
|
|
||||||
let _ = std::thread::Builder::new()
|
|
||||||
.name("clock_ticker".to_string())
|
|
||||||
.spawn(move || loop {
|
|
||||||
let n = Local::now();
|
|
||||||
let target = (n + Duration::seconds(60))
|
|
||||||
.with_second(0)
|
|
||||||
.unwrap()
|
|
||||||
.with_nanosecond(0)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let d = target - n;
|
|
||||||
|
|
||||||
std::thread::sleep(d.to_std().unwrap());
|
|
||||||
ch.send(Cmd::Draw).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut date_cache = Font::new(&ROBOTO_REGULAR, 64.0);
|
|
||||||
date_cache
|
|
||||||
.add_str_to_cache("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/ ");
|
|
||||||
let mut clock_cache = Font::new(&ROBOTO_REGULAR, 256.0);
|
|
||||||
clock_cache.add_str_to_cache("0123456789:");
|
|
||||||
|
|
||||||
Clock {
|
|
||||||
cur_time: Local::now(),
|
|
||||||
first_draw: true,
|
|
||||||
date_cache: date_cache,
|
|
||||||
clock_cache: clock_cache,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleImpl for Clock {
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
time: &DateTime<Local>,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
|
||||||
buf.memset(bg);
|
|
||||||
|
|
||||||
self.date_cache.draw_text(
|
|
||||||
buf,
|
|
||||||
bg,
|
|
||||||
&Color::new(1.0, 1.0, 1.0, 1.0),
|
|
||||||
&format!(
|
|
||||||
"{:?}, {:02}/{:02}/{:4}",
|
|
||||||
time.weekday(),
|
|
||||||
time.day(),
|
|
||||||
time.month(),
|
|
||||||
time.year()
|
|
||||||
),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.clock_cache.draw_text_fixed_width(
|
|
||||||
&mut buf.offset((0, 64))?,
|
|
||||||
bg,
|
|
||||||
&Color::new(1.0, 1.0, 1.0, 1.0),
|
|
||||||
&[120, 120, 56, 120, 120],
|
|
||||||
&format!("{:02}:{:02}", time.hour(), time.minute()),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
|
|
||||||
if time.date() != self.cur_time.date()
|
|
||||||
|| time.hour() != self.cur_time.hour()
|
|
||||||
|| time.minute() != self.cur_time.minute()
|
|
||||||
|| force
|
|
||||||
|| self.first_draw
|
|
||||||
{
|
|
||||||
self.cur_time = time.clone();
|
|
||||||
self.first_draw = false;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input(&mut self, _input: Input) {}
|
|
||||||
}
|
|
||||||
@@ -1,427 +0,0 @@
|
|||||||
use crate::buffer::Buffer;
|
|
||||||
use crate::cmd::Cmd;
|
|
||||||
use crate::color::Color;
|
|
||||||
use crate::draw::{Font, ROBOTO_REGULAR};
|
|
||||||
use crate::modules::module::{Input, ModuleImpl};
|
|
||||||
use crate::desktop::{Desktop, load_desktop_files};
|
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
use std::sync::mpsc::Sender;
|
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
use fuzzy_matcher::skim::{fuzzy_indices, fuzzy_match};
|
|
||||||
use smithay_client_toolkit::keyboard::keysyms;
|
|
||||||
|
|
||||||
pub struct Launcher {
|
|
||||||
options: Vec<Desktop>,
|
|
||||||
term_opener: String,
|
|
||||||
app_opener: String,
|
|
||||||
url_opener: String,
|
|
||||||
matches: Vec<Desktop>,
|
|
||||||
input: String,
|
|
||||||
result: Option<String>,
|
|
||||||
offset: usize,
|
|
||||||
font: RefCell<Font>,
|
|
||||||
dirty: bool,
|
|
||||||
tx: Sender<Cmd>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Launcher {
|
|
||||||
pub fn new(listener: Sender<Cmd>) -> Result<Launcher, ::std::io::Error> {
|
|
||||||
let term = match env::var_os("WLDASH_TERM_OPENER") {
|
|
||||||
Some(s) => format!("{:} ", s.into_string().unwrap()).to_string(),
|
|
||||||
None => "".to_string(),
|
|
||||||
};
|
|
||||||
let app = match env::var_os("WLDASH_APP_OPENER") {
|
|
||||||
Some(s) => format!("{:} ", s.into_string().unwrap()).to_string(),
|
|
||||||
None => "".to_string(),
|
|
||||||
};
|
|
||||||
let url = match env::var_os("WLDASH_URL_OPENER") {
|
|
||||||
Some(s) => format!("{:} ", s.into_string().unwrap()).to_string(),
|
|
||||||
None => "xdg-open ".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Launcher {
|
|
||||||
options: load_desktop_files(),
|
|
||||||
term_opener: term,
|
|
||||||
app_opener: app,
|
|
||||||
url_opener: url,
|
|
||||||
matches: vec![],
|
|
||||||
input: "".to_string(),
|
|
||||||
result: None,
|
|
||||||
offset: 0,
|
|
||||||
font: RefCell::new(Font::new(&ROBOTO_REGULAR, 32.0)),
|
|
||||||
dirty: true,
|
|
||||||
tx: listener,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_launcher(
|
|
||||||
&self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
|
||||||
buf.memset(bg);
|
|
||||||
|
|
||||||
let mut x_off = if self.input.len() > 0 {
|
|
||||||
let c = if self.matches.len() == 0 {
|
|
||||||
Color::new(1.0, 0.5, 0.5, 1.0)
|
|
||||||
} else {
|
|
||||||
Color::new(1.0, 1.0, 1.0, 1.0)
|
|
||||||
};
|
|
||||||
let dim = self.font.borrow_mut().auto_draw_text(
|
|
||||||
buf,
|
|
||||||
bg,
|
|
||||||
&c,
|
|
||||||
&self.input,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
dim.0 + 8
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut width_remaining: i32 = 1232 - x_off as i32;
|
|
||||||
for (idx, m) in self.matches.iter().enumerate() {
|
|
||||||
let mut b = match buf.offset((x_off, 0)) {
|
|
||||||
Ok(b) => b,
|
|
||||||
Err(_) => break,
|
|
||||||
};
|
|
||||||
let size = if idx == self.offset && self.input.len() > 0 {
|
|
||||||
let (_, indices) =
|
|
||||||
fuzzy_indices(&m.name.to_lowercase(), &self.input.to_lowercase()).unwrap();
|
|
||||||
let mut colors = Vec::with_capacity(m.name.len());
|
|
||||||
for pos in 0..m.name.len() {
|
|
||||||
if indices.contains(&pos) {
|
|
||||||
colors.push(Color::new(1.0, 1.0, 1.0, 1.0));
|
|
||||||
} else {
|
|
||||||
colors.push(Color::new(0.75, 0.75, 0.75, 1.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.font
|
|
||||||
.borrow_mut()
|
|
||||||
.auto_draw_text_individual_colors(&mut b, bg, &colors, &m.name)?
|
|
||||||
} else {
|
|
||||||
self.font.borrow_mut().auto_draw_text(
|
|
||||||
&mut b,
|
|
||||||
bg,
|
|
||||||
&Color::new(0.5, 0.5, 0.5, 1.0),
|
|
||||||
&m.name,
|
|
||||||
)?
|
|
||||||
};
|
|
||||||
|
|
||||||
x_off += size.0 + 8;
|
|
||||||
width_remaining -= (size.0 + 8) as i32;
|
|
||||||
|
|
||||||
if width_remaining < 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_calc(
|
|
||||||
&self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
|
||||||
buf.memset(bg);
|
|
||||||
|
|
||||||
let x_off = self
|
|
||||||
.font
|
|
||||||
.borrow_mut()
|
|
||||||
.auto_draw_text(
|
|
||||||
buf,
|
|
||||||
bg,
|
|
||||||
&Color::new(1.0, 1.0, 0.0, 1.0),
|
|
||||||
"=",
|
|
||||||
)?
|
|
||||||
.0
|
|
||||||
+ 8;
|
|
||||||
|
|
||||||
let x_off = if self.input.len() > 0 {
|
|
||||||
let dim = self.font.borrow_mut().auto_draw_text(
|
|
||||||
&mut buf.offset((x_off, 0))?,
|
|
||||||
bg,
|
|
||||||
&Color::new(1.0, 1.0, 1.0, 1.0),
|
|
||||||
&self.input[1..],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
x_off + dim.0 + 8
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(result) = &self.result {
|
|
||||||
self.font.borrow_mut().auto_draw_text(
|
|
||||||
&mut buf.offset((x_off, 0))?,
|
|
||||||
bg,
|
|
||||||
&Color::new(0.75, 0.75, 0.75, 1.0),
|
|
||||||
&format!(" = {:}", result),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_shell(
|
|
||||||
&self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
|
||||||
buf.memset(bg);
|
|
||||||
|
|
||||||
let x_off = self
|
|
||||||
.font
|
|
||||||
.borrow_mut()
|
|
||||||
.auto_draw_text(
|
|
||||||
&mut buf.offset((0, 0))?,
|
|
||||||
bg,
|
|
||||||
&Color::new(1.0, 1.0, 0.0, 1.0),
|
|
||||||
"!",
|
|
||||||
)?
|
|
||||||
.0
|
|
||||||
+ 8;
|
|
||||||
|
|
||||||
if self.input.len() > 0 {
|
|
||||||
self.font.borrow_mut().auto_draw_text(
|
|
||||||
&mut buf.offset((x_off, 0))?,
|
|
||||||
bg,
|
|
||||||
&Color::new(1.0, 1.0, 1.0, 1.0),
|
|
||||||
&self.input[1..],
|
|
||||||
)?;
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "ivy")]
|
|
||||||
fn calc(s: &str) -> Result<String, String> {
|
|
||||||
libivy::eval(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "rcalc")]
|
|
||||||
fn calc(s: &str) -> Result<String, String> {
|
|
||||||
rcalc_lib::parse::eval(s, &mut rcalc_lib::parse::CalcState::new())
|
|
||||||
.map(|x| format!("{}", x).to_string())
|
|
||||||
.map_err(|x| format!("{}", x).to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "bc")]
|
|
||||||
fn calc(s: &str) -> Result<String, String> {
|
|
||||||
use std::io::Write;
|
|
||||||
let mut child = std::process::Command::new("bc")
|
|
||||||
.stdin(std::process::Stdio::piped())
|
|
||||||
.stdout(std::process::Stdio::piped())
|
|
||||||
.stderr(std::process::Stdio::piped())
|
|
||||||
.arg("--mathlib")
|
|
||||||
.spawn()
|
|
||||||
.map_err(|_| "bc not available".to_string())?;
|
|
||||||
{
|
|
||||||
let stdin = child.stdin.as_mut().unwrap();
|
|
||||||
stdin
|
|
||||||
.write_all(s.as_bytes())
|
|
||||||
.map_err(|_| "unable to write to stdin".to_string())?;
|
|
||||||
stdin
|
|
||||||
.write_all("\n".as_bytes())
|
|
||||||
.map_err(|_| "unable to write to stdin".to_string())?;
|
|
||||||
}
|
|
||||||
let output = child
|
|
||||||
.wait_with_output()
|
|
||||||
.map_err(|_| "unable to run bc".to_string())?;
|
|
||||||
Ok(std::str::from_utf8(&output.stdout)
|
|
||||||
.map_err(|_| "unable to read from bc")?
|
|
||||||
.trim()
|
|
||||||
.to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(any(feature = "ivy", feature = "bc", feature = "rcalc")))]
|
|
||||||
fn calc(_s: &str) -> Result<String, String> {
|
|
||||||
Err("no calculator implementation available".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wlcopy(s: &str) -> Result<(), String> {
|
|
||||||
let mut child = std::process::Command::new("wl-copy")
|
|
||||||
.stdin(std::process::Stdio::null())
|
|
||||||
.stdout(std::process::Stdio::null())
|
|
||||||
.stderr(std::process::Stdio::null())
|
|
||||||
.arg(s)
|
|
||||||
.spawn()
|
|
||||||
.map_err(|_| "wl-copy not available".to_string())?;
|
|
||||||
child
|
|
||||||
.wait()
|
|
||||||
.map_err(|_| "unable to run wl-copy".to_string())?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModuleImpl for Launcher {
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
_time: &DateTime<Local>,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
|
||||||
buf.memset(bg);
|
|
||||||
|
|
||||||
match self.input.chars().next() {
|
|
||||||
Some('=') => self.draw_calc(buf, bg),
|
|
||||||
Some('!') => self.draw_shell(buf, bg),
|
|
||||||
_ => self.draw_launcher(buf, bg),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, _time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
|
|
||||||
if self.dirty || force {
|
|
||||||
match self.input.chars().next() {
|
|
||||||
Some('=') => {
|
|
||||||
if self.input.len() > 1 {
|
|
||||||
match calc(&self.input.chars().skip(1).collect::<String>()) {
|
|
||||||
Ok(v) => self.result = Some(v),
|
|
||||||
Err(_) => self.result = None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some('!') => (),
|
|
||||||
_ => {
|
|
||||||
let mut m = self
|
|
||||||
.options
|
|
||||||
.iter()
|
|
||||||
.map(|x| {
|
|
||||||
(
|
|
||||||
fuzzy_match(&x.name.to_lowercase(), &self.input.to_lowercase()),
|
|
||||||
x,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.filter(|(x, _)| x.is_some())
|
|
||||||
.map(|(x, y)| (x.unwrap(), y.clone()))
|
|
||||||
.collect::<Vec<(i64, Desktop)>>();
|
|
||||||
|
|
||||||
m.sort_by(|(x1, y1), (x2, y2)| {
|
|
||||||
if x1 > x2 {
|
|
||||||
Ordering::Less
|
|
||||||
} else if x1 < x2 {
|
|
||||||
Ordering::Greater
|
|
||||||
} else if y1.name.len() < y2.name.len() {
|
|
||||||
Ordering::Less
|
|
||||||
} else if y1.name.len() > y2.name.len() {
|
|
||||||
Ordering::Greater
|
|
||||||
} else {
|
|
||||||
y1.cmp(y2)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
self.matches = m.into_iter().map(|(_, x)| x).collect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
self.dirty = false;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input(&mut self, input: Input) {
|
|
||||||
match input {
|
|
||||||
Input::Keypress { key, interpreted } => match key {
|
|
||||||
keysyms::XKB_KEY_BackSpace => {
|
|
||||||
if self.input.len() > 0 {
|
|
||||||
self.input = self.input[..self.input.len() - 1].to_string();
|
|
||||||
self.offset = 0;
|
|
||||||
self.result = None;
|
|
||||||
self.dirty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keysyms::XKB_KEY_Return => {
|
|
||||||
match self.input.chars().next() {
|
|
||||||
Some('=') => match self.result {
|
|
||||||
Some(ref v) => {
|
|
||||||
let _ = wlcopy(&v);
|
|
||||||
}
|
|
||||||
None => (),
|
|
||||||
},
|
|
||||||
Some('!') => {
|
|
||||||
let _ = Command::new("sh")
|
|
||||||
.arg("-c")
|
|
||||||
.arg(self.input.chars().skip(1).collect::<String>())
|
|
||||||
.spawn();
|
|
||||||
self.tx.send(Cmd::Exit).unwrap();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
if self.matches.len() > self.offset {
|
|
||||||
let d = &self.matches[self.offset];
|
|
||||||
if let Some(exec) = &d.exec {
|
|
||||||
let exec = exec
|
|
||||||
.replace("%f", "")
|
|
||||||
.replace("%F", "")
|
|
||||||
.replace("%u", "")
|
|
||||||
.replace("%U", "");
|
|
||||||
let prefix = if d.term {
|
|
||||||
&self.term_opener
|
|
||||||
} else {
|
|
||||||
&self.app_opener
|
|
||||||
};
|
|
||||||
|
|
||||||
let lexed = if prefix.len() > 0 {
|
|
||||||
let mut prefix = shlex::split(prefix).unwrap();
|
|
||||||
prefix.push(exec);
|
|
||||||
prefix
|
|
||||||
} else {
|
|
||||||
shlex::split(&exec).unwrap()
|
|
||||||
};
|
|
||||||
if lexed.len() > 0 {
|
|
||||||
let _ = Command::new(lexed[0].clone())
|
|
||||||
.args(&lexed[1..])
|
|
||||||
.spawn();
|
|
||||||
self.tx.send(Cmd::Exit).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(url) = &d.url {
|
|
||||||
if self.url_opener.len() > 0 {
|
|
||||||
let mut lexed = shlex::split(&self.url_opener).unwrap();
|
|
||||||
lexed.push(url.to_string());
|
|
||||||
if lexed.len() > 0 {
|
|
||||||
let _ = Command::new(lexed[0].clone())
|
|
||||||
.args(&lexed[1..])
|
|
||||||
.spawn();
|
|
||||||
self.tx.send(Cmd::Exit).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
keysyms::XKB_KEY_Right => {
|
|
||||||
if self.matches.len() > 0 && self.offset < self.matches.len() - 1 {
|
|
||||||
self.offset += 1;
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
keysyms::XKB_KEY_Left => {
|
|
||||||
if self.matches.len() > 0 && self.offset > 0 {
|
|
||||||
self.offset -= 1;
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => match interpreted {
|
|
||||||
Some(v) => {
|
|
||||||
self.input += &v;
|
|
||||||
self.offset = 0;
|
|
||||||
self.result = None;
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
use crate::buffer::Buffer;
|
|
||||||
use crate::color::Color;
|
|
||||||
|
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum Input {
|
|
||||||
Click {
|
|
||||||
pos: (u32, u32),
|
|
||||||
button: u32,
|
|
||||||
},
|
|
||||||
Scroll {
|
|
||||||
pos: (u32, u32),
|
|
||||||
x: f64,
|
|
||||||
y: f64,
|
|
||||||
},
|
|
||||||
Keypress {
|
|
||||||
key: u32,
|
|
||||||
interpreted: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Input {
|
|
||||||
pub fn offset(&self, offset: (u32, u32)) -> Input {
|
|
||||||
match self {
|
|
||||||
Input::Click { pos, button } => Input::Click {
|
|
||||||
pos: (pos.0 - offset.0, pos.1 - offset.1),
|
|
||||||
button: *button,
|
|
||||||
},
|
|
||||||
Input::Scroll { pos, x, y } => Input::Scroll {
|
|
||||||
pos: (pos.0 - offset.0, pos.1 - offset.1),
|
|
||||||
x: *x,
|
|
||||||
y: *y,
|
|
||||||
},
|
|
||||||
Input::Keypress { key, interpreted } => Input::Keypress {
|
|
||||||
key: *key,
|
|
||||||
interpreted: interpreted.clone(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ModuleImpl {
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
time: &DateTime<Local>,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error>;
|
|
||||||
fn update(&mut self, time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error>;
|
|
||||||
fn input(&mut self, input: Input);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Module {
|
|
||||||
m: Arc<Mutex<Box<dyn ModuleImpl + Send>>>,
|
|
||||||
pos: (u32, u32, u32, u32),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Module {
|
|
||||||
pub fn new(m: Box<dyn ModuleImpl + Send>, pos: (u32, u32, u32, u32)) -> Module {
|
|
||||||
Module {
|
|
||||||
m: Arc::new(Mutex::new(m)),
|
|
||||||
pos: pos,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_bounds(&self) -> (u32, u32, u32, u32) {
|
|
||||||
self.pos
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update(&self, time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
|
|
||||||
self.m.lock().unwrap().update(time, force)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw(
|
|
||||||
&self,
|
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
time: &DateTime<Local>,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
|
||||||
self.m.lock().unwrap().draw(buf, bg, time)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intersect(&self, pos: (u32, u32)) -> bool {
|
|
||||||
pos.0 >= self.pos.0
|
|
||||||
&& pos.0 < (self.pos.0 + self.pos.2)
|
|
||||||
&& pos.1 >= self.pos.1
|
|
||||||
&& pos.1 < (self.pos.1 + self.pos.3)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn input(&self, input: Input) {
|
|
||||||
self.m.lock().unwrap().input(input)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+365
@@ -0,0 +1,365 @@
|
|||||||
|
use crate::buffer::Buffer;
|
||||||
|
use crate::color::Color;
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
pub use smithay_client_toolkit::keyboard::{KeyState, ModifiersState};
|
||||||
|
|
||||||
|
pub struct DrawContext<'a> {
|
||||||
|
pub buf: &'a mut Buffer<'a>,
|
||||||
|
pub bg: &'a Color,
|
||||||
|
pub time: &'a DateTime<Local>,
|
||||||
|
pub force: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DrawReport {
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub damage: Vec<(i32, i32, i32, i32)>,
|
||||||
|
pub full_damage: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrawReport {
|
||||||
|
pub fn empty(width: u32, height: u32) -> DrawReport {
|
||||||
|
DrawReport {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
damage: Vec::new(),
|
||||||
|
full_damage: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Widget {
|
||||||
|
fn size(&self) -> (u32, u32);
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error>;
|
||||||
|
|
||||||
|
fn keyboard_input(
|
||||||
|
&mut self,
|
||||||
|
keysym: u32,
|
||||||
|
modifier_state: ModifiersState,
|
||||||
|
key_state: KeyState,
|
||||||
|
interpreted: Option<String>,
|
||||||
|
);
|
||||||
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32));
|
||||||
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VerticalLayout {
|
||||||
|
pub children: Vec<Box<dyn Widget + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl VerticalLayout {
|
||||||
|
pub fn new(children: Vec<Box<dyn Widget + Send>>) -> Box<VerticalLayout> {
|
||||||
|
Box::new(VerticalLayout { children: children })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for VerticalLayout {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
|
||||||
|
for child in &self.children {
|
||||||
|
let size = child.size();
|
||||||
|
if size.0 > width {
|
||||||
|
width = size.0;
|
||||||
|
}
|
||||||
|
height += size.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let mut offset = pos.1;
|
||||||
|
let mut width = 0;
|
||||||
|
let mut damage = Vec::new();
|
||||||
|
let mut full_damage = false;
|
||||||
|
|
||||||
|
for child in &mut self.children {
|
||||||
|
let mut report = child.draw(ctx, (pos.0, offset))?;
|
||||||
|
if report.width > width {
|
||||||
|
width = report.width;
|
||||||
|
}
|
||||||
|
offset += report.height;
|
||||||
|
full_damage |= report.full_damage;
|
||||||
|
damage.append(&mut report.damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DrawReport {
|
||||||
|
width,
|
||||||
|
height: offset - pos.1,
|
||||||
|
damage,
|
||||||
|
full_damage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(
|
||||||
|
&mut self,
|
||||||
|
keysym: u32,
|
||||||
|
modifier_state: ModifiersState,
|
||||||
|
key_state: KeyState,
|
||||||
|
interpreted: Option<String>,
|
||||||
|
) {
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.keyboard_input(keysym, modifier_state, key_state, interpreted.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
||||||
|
let mut height = 0;
|
||||||
|
|
||||||
|
for child in &mut self.children {
|
||||||
|
let size = child.size();
|
||||||
|
if pos.1 >= height && pos.1 < height + size.1 {
|
||||||
|
let pos = (pos.0, pos.1 - height);
|
||||||
|
child.mouse_click(button, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
height += size.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
||||||
|
let mut height = 0;
|
||||||
|
|
||||||
|
for child in &mut self.children {
|
||||||
|
let size = child.size();
|
||||||
|
if pos.1 >= height && pos.1 < height + size.1 {
|
||||||
|
let pos = (pos.0, pos.1 - height);
|
||||||
|
child.mouse_scroll(scroll, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
height += size.1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HorizontalLayout {
|
||||||
|
pub children: Vec<Box<dyn Widget + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl HorizontalLayout {
|
||||||
|
pub fn new(children: Vec<Box<dyn Widget + Send>>) -> Box<HorizontalLayout> {
|
||||||
|
Box::new(HorizontalLayout { children: children })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for HorizontalLayout {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
let mut width = 0;
|
||||||
|
let mut height = 0;
|
||||||
|
|
||||||
|
for child in &self.children {
|
||||||
|
let size = child.size();
|
||||||
|
if size.1 > height {
|
||||||
|
height = size.1;
|
||||||
|
}
|
||||||
|
width += size.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
(width, height)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let mut offset = pos.0;
|
||||||
|
let mut height = 0;
|
||||||
|
let mut damage = Vec::new();
|
||||||
|
let mut full_damage = false;
|
||||||
|
|
||||||
|
for child in &mut self.children {
|
||||||
|
let mut report = child.draw(ctx, (offset, pos.1))?;
|
||||||
|
if report.height > height {
|
||||||
|
height = report.height;
|
||||||
|
}
|
||||||
|
offset += report.width;
|
||||||
|
full_damage |= report.full_damage;
|
||||||
|
damage.append(&mut report.damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: offset - pos.0,
|
||||||
|
height,
|
||||||
|
damage,
|
||||||
|
full_damage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(
|
||||||
|
&mut self,
|
||||||
|
keysym: u32,
|
||||||
|
modifier_state: ModifiersState,
|
||||||
|
key_state: KeyState,
|
||||||
|
interpreted: Option<String>,
|
||||||
|
) {
|
||||||
|
for child in &mut self.children {
|
||||||
|
child.keyboard_input(keysym, modifier_state, key_state, interpreted.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
||||||
|
let mut width = 0;
|
||||||
|
|
||||||
|
for child in &mut self.children {
|
||||||
|
let size = child.size();
|
||||||
|
if pos.0 >= width && pos.0 < width + size.0 {
|
||||||
|
let pos = (pos.0 - width, pos.1);
|
||||||
|
child.mouse_click(button, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
width += size.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
||||||
|
let mut width = 0;
|
||||||
|
|
||||||
|
for child in &mut self.children {
|
||||||
|
let size = child.size();
|
||||||
|
if pos.0 >= width && pos.0 < width + size.0 {
|
||||||
|
let pos = (pos.0 - width, pos.1);
|
||||||
|
child.mouse_scroll(scroll, pos);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
width += size.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Margin {
|
||||||
|
pub child: Box<dyn Widget + Send>,
|
||||||
|
pub margins: (u32, u32, u32, u32), // left, right, top, bottom
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl Margin {
|
||||||
|
pub fn new(margins: (u32, u32, u32, u32), child: Box<dyn Widget + Send>) -> Box<Margin> {
|
||||||
|
Box::new(Margin {
|
||||||
|
child: child,
|
||||||
|
margins: margins,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Margin {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
let size = self.child.size();
|
||||||
|
(
|
||||||
|
size.0 + self.margins.0 + self.margins.1,
|
||||||
|
size.1 + self.margins.2 + self.margins.3,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let report = self
|
||||||
|
.child
|
||||||
|
.draw(ctx, (pos.0 + self.margins.0, pos.1 + self.margins.2))?;
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: report.width + self.margins.0 + self.margins.1,
|
||||||
|
height: report.height + self.margins.2 + self.margins.3,
|
||||||
|
damage: report.damage,
|
||||||
|
full_damage: report.full_damage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(
|
||||||
|
&mut self,
|
||||||
|
keysym: u32,
|
||||||
|
modifier_state: ModifiersState,
|
||||||
|
key_state: KeyState,
|
||||||
|
interpreted: Option<String>,
|
||||||
|
) {
|
||||||
|
self.child
|
||||||
|
.keyboard_input(keysym, modifier_state, key_state, interpreted);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
||||||
|
let pos = (
|
||||||
|
pos.0.saturating_sub(self.margins.0),
|
||||||
|
pos.1.saturating_sub(self.margins.2),
|
||||||
|
);
|
||||||
|
self.child.mouse_click(button, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
||||||
|
let pos = (
|
||||||
|
pos.0.saturating_sub(self.margins.0),
|
||||||
|
pos.1.saturating_sub(self.margins.2),
|
||||||
|
);
|
||||||
|
self.child.mouse_scroll(scroll, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Fixed {
|
||||||
|
pub child: Box<dyn Widget + Send>,
|
||||||
|
pub size: (u32, u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl Fixed {
|
||||||
|
pub fn new(size: (u32, u32), child: Box<dyn Widget + Send>) -> Box<Fixed> {
|
||||||
|
Box::new(Fixed {
|
||||||
|
child: child,
|
||||||
|
size: size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Fixed {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
self.size
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let report = self.child.draw(ctx, pos)?;
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: self.size.0,
|
||||||
|
height: self.size.1,
|
||||||
|
damage: report.damage,
|
||||||
|
full_damage: report.full_damage,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(
|
||||||
|
&mut self,
|
||||||
|
keysym: u32,
|
||||||
|
modifier_state: ModifiersState,
|
||||||
|
key_state: KeyState,
|
||||||
|
interpreted: Option<String>,
|
||||||
|
) {
|
||||||
|
self.child
|
||||||
|
.keyboard_input(keysym, modifier_state, key_state, interpreted);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_click(&mut self, button: u32, pos: (u32, u32)) {
|
||||||
|
self.child.mouse_click(button, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mouse_scroll(&mut self, scroll: (f64, f64), pos: (u32, u32)) {
|
||||||
|
self.child.mouse_scroll(scroll, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,21 +1,15 @@
|
|||||||
use crate::buffer::Buffer;
|
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
|
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||||
use crate::modules::module::{Input, ModuleImpl};
|
|
||||||
|
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
use std::io::{Error, ErrorKind};
|
use std::io::{Error, ErrorKind};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
|
|
||||||
pub struct Backlight {
|
pub struct Backlight {
|
||||||
device_path: PathBuf,
|
device_path: PathBuf,
|
||||||
cur_brightness: u64,
|
cur_brightness: u64,
|
||||||
max_brightness: u64,
|
max_brightness: u64,
|
||||||
font: Font,
|
|
||||||
dirty: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_file_as_u64(path: &Path) -> Result<u64, Error> {
|
fn read_file_as_u64(path: &Path) -> Result<u64, Error> {
|
||||||
@@ -36,7 +30,6 @@ impl Backlight {
|
|||||||
pub fn update(&mut self) -> Result<(), Error> {
|
pub fn update(&mut self) -> Result<(), Error> {
|
||||||
self.cur_brightness = read_file_as_u64(self.device_path.join("brightness").as_path())?;
|
self.cur_brightness = read_file_as_u64(self.device_path.join("brightness").as_path())?;
|
||||||
self.max_brightness = read_file_as_u64(self.device_path.join("max_brightness").as_path())?;
|
self.max_brightness = read_file_as_u64(self.device_path.join("max_brightness").as_path())?;
|
||||||
self.dirty = true;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +51,11 @@ impl Backlight {
|
|||||||
(self.cur_brightness as f32 / self.max_brightness as f32)
|
(self.cur_brightness as f32 / self.max_brightness as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set(&mut self, val: f32) -> Result<(), Error> {
|
||||||
|
self.cur_brightness = (self.max_brightness as f32 * val) as u64;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add(&mut self, diff: f32) -> Result<(), Error> {
|
pub fn add(&mut self, diff: f32) -> Result<(), Error> {
|
||||||
let inc = (self.max_brightness as f32 * diff) as i64;
|
let inc = (self.max_brightness as f32 * diff) as i64;
|
||||||
|
|
||||||
@@ -72,7 +70,7 @@ impl Backlight {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new() -> Result<Self, Error> {
|
pub fn new(font_size: f32, length: u32) -> Result<Box<BarWidget>, Error> {
|
||||||
let devices = Path::new("/sys/class/backlight").read_dir()?;
|
let devices = Path::new("/sys/class/backlight").read_dir()?;
|
||||||
|
|
||||||
let first_device = match devices.take(1).next() {
|
let first_device = match devices.take(1).next() {
|
||||||
@@ -86,90 +84,56 @@ impl Backlight {
|
|||||||
device_path: first_device.path(),
|
device_path: first_device.path(),
|
||||||
cur_brightness: 0,
|
cur_brightness: 0,
|
||||||
max_brightness: 0,
|
max_brightness: 0,
|
||||||
font: Font::new(&ROBOTO_REGULAR, 24.0),
|
|
||||||
dirty: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
dev.font.add_str_to_cache("backlight");
|
|
||||||
|
|
||||||
dev.update()?;
|
dev.update()?;
|
||||||
|
|
||||||
Ok(dev)
|
Ok(BarWidget::new_simple(font_size, length, Box::new(dev)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleImpl for Backlight {
|
impl BarWidgetImpl for Backlight {
|
||||||
fn draw(
|
fn name(&self) -> &str {
|
||||||
&self,
|
"backlight"
|
||||||
buf: &mut Buffer,
|
|
||||||
bg: &Color,
|
|
||||||
_time: &DateTime<Local>,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, Error> {
|
|
||||||
buf.memset(bg);
|
|
||||||
let c = Color::new(1.0, 1.0, 1.0, 1.0);
|
|
||||||
self.font.draw_text(
|
|
||||||
buf,
|
|
||||||
bg,
|
|
||||||
&c,
|
|
||||||
"backlight",
|
|
||||||
)?;
|
|
||||||
draw_bar(
|
|
||||||
&mut buf.offset((128, 0))?,
|
|
||||||
&c,
|
|
||||||
464,
|
|
||||||
24,
|
|
||||||
self.brightness(),
|
|
||||||
)?;
|
|
||||||
draw_box(&mut buf.offset((128, 0))?, &c, (464, 24))?;
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
}
|
||||||
|
fn value(&self) -> f32 {
|
||||||
fn update(&mut self, _time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
|
self.brightness()
|
||||||
if self.dirty || force {
|
}
|
||||||
self.dirty = false;
|
fn color(&self) -> Color {
|
||||||
Ok(true)
|
Color::new(1.0, 1.0, 1.0, 1.0)
|
||||||
|
}
|
||||||
|
fn inc(&mut self, inc: f32) {
|
||||||
|
self.add(inc).unwrap();
|
||||||
|
match self.sync() {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error while trying to change brightness: {}", err);
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set(&mut self, val: f32) {
|
||||||
|
self.set(val).unwrap();
|
||||||
|
match self.sync() {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(err) => {
|
||||||
|
eprintln!("Error while trying to change brightness: {}", err);
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn toggle(&mut self) {
|
||||||
|
self.cur_brightness = if self.cur_brightness == 1 {
|
||||||
|
self.max_brightness
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
1
|
||||||
}
|
};
|
||||||
}
|
match self.sync() {
|
||||||
|
Ok(val) => val,
|
||||||
fn input(&mut self, input: Input) {
|
Err(err) => {
|
||||||
match input {
|
eprintln!("Error while trying to change brightness: {}", err);
|
||||||
Input::Scroll {
|
()
|
||||||
pos: _pos,
|
|
||||||
x: _x,
|
|
||||||
y,
|
|
||||||
} => {
|
|
||||||
self.add(y as f32 / -800.0).unwrap();
|
|
||||||
match self.sync() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("Error while trying to change brightness: {}", err);
|
|
||||||
()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Input::Click { pos: _pos, button } => {
|
|
||||||
match button {
|
|
||||||
273 => {
|
|
||||||
// Right click
|
|
||||||
self.cur_brightness = if self.cur_brightness == 1 {
|
|
||||||
self.max_brightness
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
match self.sync() {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("Error while trying to change brightness: {}", err);
|
|
||||||
()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
use crate::color::Color;
|
||||||
|
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
|
||||||
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
||||||
|
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
pub trait BarWidgetImpl {
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
fn value(&self) -> f32;
|
||||||
|
fn color(&self) -> Color;
|
||||||
|
fn inc(&mut self, inc: f32);
|
||||||
|
fn set(&mut self, val: f32);
|
||||||
|
fn toggle(&mut self);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct BarWidget {
|
||||||
|
bar_impl: Box<dyn BarWidgetImpl + Send>,
|
||||||
|
font: Font,
|
||||||
|
font_size: u32,
|
||||||
|
length: u32,
|
||||||
|
dirty: Arc<Mutex<bool>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BarWidget {
|
||||||
|
pub fn new_simple(
|
||||||
|
font_size: f32,
|
||||||
|
length: u32,
|
||||||
|
w: Box<dyn BarWidgetImpl + Send>,
|
||||||
|
) -> Box<BarWidget> {
|
||||||
|
let mut font = Font::new(&ROBOTO_REGULAR, font_size);
|
||||||
|
font.add_str_to_cache(w.name());
|
||||||
|
|
||||||
|
Box::new(BarWidget {
|
||||||
|
bar_impl: w,
|
||||||
|
dirty: Arc::new(Mutex::new(true)),
|
||||||
|
font: font,
|
||||||
|
font_size: font_size as u32,
|
||||||
|
length: length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new<F>(font_size: f32, length: u32, f: F) -> Result<Box<BarWidget>, ::std::io::Error>
|
||||||
|
where
|
||||||
|
F: FnOnce(Arc<Mutex<bool>>) -> Result<Box<dyn BarWidgetImpl + Send>, ::std::io::Error>,
|
||||||
|
F: Send + 'static + Clone,
|
||||||
|
{
|
||||||
|
let dirty = Arc::new(Mutex::new(true));
|
||||||
|
let im = f(dirty.clone())?;
|
||||||
|
|
||||||
|
let mut font = Font::new(&ROBOTO_REGULAR, font_size);
|
||||||
|
font.add_str_to_cache(im.name());
|
||||||
|
|
||||||
|
Ok(Box::new(BarWidget {
|
||||||
|
bar_impl: im,
|
||||||
|
dirty: dirty,
|
||||||
|
font: font,
|
||||||
|
font_size: font_size as u32,
|
||||||
|
length: length,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for BarWidget {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
(self.length, self.font_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let (width, height) = self.size();
|
||||||
|
{
|
||||||
|
let mut d = self.dirty.lock().unwrap();
|
||||||
|
if !*d && !ctx.force {
|
||||||
|
return Ok(DrawReport::empty(width, height));
|
||||||
|
}
|
||||||
|
*d = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = &mut ctx.buf.subdimensions((pos.0, pos.1, width, height))?;
|
||||||
|
buf.memset(ctx.bg);
|
||||||
|
|
||||||
|
let c = Color::new(1.0, 1.0, 1.0, 1.0);
|
||||||
|
self.font.draw_text(buf, ctx.bg, &c, self.bar_impl.name())?;
|
||||||
|
|
||||||
|
let c = self.bar_impl.color();
|
||||||
|
let bar_off = 5 * self.font_size;
|
||||||
|
let mut val = self.bar_impl.value();
|
||||||
|
draw_bar(
|
||||||
|
&mut buf.offset((bar_off, 0))?,
|
||||||
|
&c,
|
||||||
|
self.length - bar_off,
|
||||||
|
self.font_size,
|
||||||
|
val,
|
||||||
|
)?;
|
||||||
|
let mut iter = 1.0;
|
||||||
|
while val > 1.0 {
|
||||||
|
let c = &Color::new(0.75 / iter, 0.25 / iter, 0.25 / iter, 1.0);
|
||||||
|
val -= 1.0;
|
||||||
|
iter += 1.0;
|
||||||
|
draw_bar(
|
||||||
|
&mut buf.offset((bar_off, 0))?,
|
||||||
|
&c,
|
||||||
|
self.length - bar_off,
|
||||||
|
self.font_size,
|
||||||
|
val,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
draw_box(
|
||||||
|
&mut buf.offset((bar_off, 0))?,
|
||||||
|
&c,
|
||||||
|
(self.length - bar_off, self.font_size),
|
||||||
|
)?;
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
damage: vec![buf.get_signed_bounds()],
|
||||||
|
full_damage: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(&mut self, _: u32, _: ModifiersState, _: KeyState, _: Option<String>) {}
|
||||||
|
fn mouse_click(&mut self, button: u32, (x, _): (u32, u32)) {
|
||||||
|
*self.dirty.lock().unwrap() = true;
|
||||||
|
match button {
|
||||||
|
272 => {
|
||||||
|
let offset = 5 * self.font_size;
|
||||||
|
if x > offset {
|
||||||
|
self.bar_impl
|
||||||
|
.set(((x - offset) + 1) as f32 / (self.length - offset) as f32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
273 => self.bar_impl.toggle(),
|
||||||
|
x => {
|
||||||
|
dbg!(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn mouse_scroll(&mut self, (_, y): (f64, f64), _: (u32, u32)) {
|
||||||
|
*self.dirty.lock().unwrap() = true;
|
||||||
|
self.bar_impl.inc(y as f32 / -800.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
use crate::buffer::Buffer;
|
|
||||||
use crate::cmd::Cmd;
|
use crate::cmd::Cmd;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
|
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||||
use crate::modules::module::{Input, ModuleImpl};
|
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
use dbus;
|
use dbus;
|
||||||
|
|
||||||
fn get_upower_property(
|
fn get_upower_property(
|
||||||
@@ -40,14 +37,12 @@ fn get_upower_property(
|
|||||||
|
|
||||||
pub struct UpowerBattery {
|
pub struct UpowerBattery {
|
||||||
device_path: String,
|
device_path: String,
|
||||||
font: Font,
|
|
||||||
inner: Arc<Mutex<UpowerBatteryInner>>,
|
inner: Arc<Mutex<UpowerBatteryInner>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct UpowerBatteryInner {
|
pub struct UpowerBatteryInner {
|
||||||
state: UpowerBatteryState,
|
state: UpowerBatteryState,
|
||||||
capacity: f64,
|
capacity: f64,
|
||||||
dirty: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum UpowerBatteryState {
|
enum UpowerBatteryState {
|
||||||
@@ -116,88 +111,89 @@ impl UpowerBattery {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut font = Font::new(&ROBOTO_REGULAR, 24.0);
|
|
||||||
font.add_str_to_cache("battery");
|
|
||||||
|
|
||||||
Ok(UpowerBattery {
|
Ok(UpowerBattery {
|
||||||
device_path,
|
device_path,
|
||||||
font: font,
|
|
||||||
inner: Arc::new(Mutex::new(UpowerBatteryInner {
|
inner: Arc::new(Mutex::new(UpowerBatteryInner {
|
||||||
capacity: capacity,
|
capacity: capacity,
|
||||||
state: state,
|
state: state,
|
||||||
dirty: true,
|
|
||||||
})),
|
})),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(listener: Sender<Cmd>) -> Result<Self, ::std::io::Error> {
|
pub fn new(
|
||||||
let d = UpowerBattery::from_device("BAT0")?;
|
font_size: f32,
|
||||||
let path = d.device_path.clone();
|
length: u32,
|
||||||
let inner = d.inner.clone();
|
sender: Sender<Cmd>,
|
||||||
let _ = thread::Builder::new()
|
) -> Result<Box<BarWidget>, ::std::io::Error> {
|
||||||
.name("battery_monitor".to_string())
|
BarWidget::new(font_size, length, move |dirty| {
|
||||||
.spawn(move || {
|
let d = UpowerBattery::from_device("BAT0")?;
|
||||||
let con = dbus::Connection::get_private(dbus::BusType::System)
|
let path = d.device_path.clone();
|
||||||
.expect("Failed to establish D-Bus connection.");
|
let inner = d.inner.clone();
|
||||||
let rule = format!(
|
let _ = thread::Builder::new()
|
||||||
"type='signal',\
|
.name("battery_monitor".to_string())
|
||||||
path='{}',\
|
.spawn(move || {
|
||||||
interface='org.freedesktop.DBus.Properties',\
|
let con = dbus::Connection::get_private(dbus::BusType::System)
|
||||||
member='PropertiesChanged'",
|
.expect("Failed to establish D-Bus connection.");
|
||||||
path
|
let rule = format!(
|
||||||
);
|
"type='signal',\
|
||||||
|
path='{}',\
|
||||||
|
interface='org.freedesktop.DBus.Properties',\
|
||||||
|
member='PropertiesChanged'",
|
||||||
|
path
|
||||||
|
);
|
||||||
|
|
||||||
// First we're going to get an (irrelevant) NameAcquired event.
|
// First we're going to get an (irrelevant) NameAcquired event.
|
||||||
con.incoming(10_000).next();
|
con.incoming(10_000).next();
|
||||||
|
|
||||||
con.add_match(&rule)
|
con.add_match(&rule)
|
||||||
.expect("Failed to add D-Bus match rule.");
|
.expect("Failed to add D-Bus match rule.");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if con.incoming(10_000).next().is_some() {
|
if con.incoming(10_000).next().is_some() {
|
||||||
let capacity = get_upower_property(&con, &path, "Percentage")
|
let capacity = get_upower_property(&con, &path, "Percentage")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get1::<dbus::arg::Variant<f64>>()
|
.get1::<dbus::arg::Variant<f64>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0;
|
.0;
|
||||||
let state = match get_upower_property(&con, &path, "State")
|
let state = match get_upower_property(&con, &path, "State")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get1::<dbus::arg::Variant<u32>>()
|
.get1::<dbus::arg::Variant<u32>>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0
|
.0
|
||||||
{
|
{
|
||||||
1 => UpowerBatteryState::Charging,
|
1 => UpowerBatteryState::Charging,
|
||||||
2 => UpowerBatteryState::Discharging,
|
2 => UpowerBatteryState::Discharging,
|
||||||
3 => UpowerBatteryState::Empty,
|
3 => UpowerBatteryState::Empty,
|
||||||
4 => UpowerBatteryState::Full,
|
4 => UpowerBatteryState::Full,
|
||||||
5 => UpowerBatteryState::NotCharging,
|
5 => UpowerBatteryState::NotCharging,
|
||||||
6 => UpowerBatteryState::Discharging,
|
6 => UpowerBatteryState::Discharging,
|
||||||
_ => UpowerBatteryState::Unknown,
|
_ => UpowerBatteryState::Unknown,
|
||||||
};
|
};
|
||||||
let mut inner = inner.lock().unwrap();
|
let mut inner = inner.lock().unwrap();
|
||||||
inner.state = state;
|
inner.state = state;
|
||||||
inner.capacity = capacity;
|
inner.capacity = capacity;
|
||||||
inner.dirty = true;
|
*dirty.lock().unwrap() = true;
|
||||||
listener.send(Cmd::Draw).unwrap();
|
sender.send(Cmd::Draw).unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
|
|
||||||
Ok(d)
|
Ok(Box::new(d))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleImpl for UpowerBattery {
|
impl BarWidgetImpl for UpowerBattery {
|
||||||
fn draw(
|
fn name(&self) -> &str {
|
||||||
&self,
|
"battery"
|
||||||
buf: &mut Buffer,
|
}
|
||||||
bg: &Color,
|
fn value(&self) -> f32 {
|
||||||
_time: &DateTime<Local>,
|
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
|
||||||
buf.memset(bg);
|
|
||||||
let inner = self.inner.lock().unwrap();
|
let inner = self.inner.lock().unwrap();
|
||||||
let text_color = Color::new(1.0, 1.0, 1.0, 1.0);
|
(inner.capacity as f32) / 100.0
|
||||||
let bar_color = match inner.state {
|
}
|
||||||
|
fn color(&self) -> Color {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
match inner.state {
|
||||||
UpowerBatteryState::Discharging | UpowerBatteryState::Unknown => {
|
UpowerBatteryState::Discharging | UpowerBatteryState::Unknown => {
|
||||||
if inner.capacity > 10.0 {
|
if inner.capacity > 10.0 {
|
||||||
Color::new(1.0, 1.0, 1.0, 1.0)
|
Color::new(1.0, 1.0, 1.0, 1.0)
|
||||||
@@ -211,39 +207,9 @@ impl ModuleImpl for UpowerBattery {
|
|||||||
UpowerBatteryState::NotCharging | UpowerBatteryState::Empty => {
|
UpowerBatteryState::NotCharging | UpowerBatteryState::Empty => {
|
||||||
Color::new(1.0, 0.5, 0.5, 1.0)
|
Color::new(1.0, 0.5, 0.5, 1.0)
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
self.font.draw_text(
|
|
||||||
&mut buf.offset((0, 0))?,
|
|
||||||
bg,
|
|
||||||
&text_color,
|
|
||||||
"battery",
|
|
||||||
)?;
|
|
||||||
draw_bar(
|
|
||||||
&mut buf.offset((128, 0))?,
|
|
||||||
&bar_color,
|
|
||||||
464,
|
|
||||||
24,
|
|
||||||
(inner.capacity as f32) / 100.0,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
draw_box(
|
|
||||||
&mut buf.offset((128, 0))?,
|
|
||||||
&bar_color,
|
|
||||||
(464, 24),
|
|
||||||
)?;
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, _time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
|
|
||||||
let mut inner = self.inner.lock().unwrap();
|
|
||||||
if inner.dirty || force {
|
|
||||||
inner.dirty = false;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn inc(&mut self, _: f32) {}
|
||||||
fn input(&mut self, _input: Input) {}
|
fn set(&mut self, _: f32) {}
|
||||||
|
fn toggle(&mut self) {}
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,16 @@
|
|||||||
use crate::buffer::Buffer;
|
use crate::buffer::Buffer;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{Font, DEJAVUSANS_MONO, ROBOTO_REGULAR};
|
use crate::draw::{Font, DEJAVUSANS_MONO, ROBOTO_REGULAR};
|
||||||
use crate::modules::module::{Input, ModuleImpl};
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
||||||
|
|
||||||
use chrono::{Date, DateTime, Datelike, Local};
|
use chrono::{Date, Datelike, Local};
|
||||||
|
|
||||||
pub struct Calendar {
|
pub struct Calendar {
|
||||||
cur_date: Date<Local>,
|
cur_date: Date<Local>,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
offset: f64,
|
offset: f64,
|
||||||
|
sections: u32,
|
||||||
|
font_size: u32,
|
||||||
calendar_cache: Font,
|
calendar_cache: Font,
|
||||||
month_cache: Font,
|
month_cache: Font,
|
||||||
year_cache: Font,
|
year_cache: Font,
|
||||||
@@ -55,7 +57,7 @@ impl Calendar {
|
|||||||
)?;
|
)?;
|
||||||
if time.year() != orig.year() {
|
if time.year() != orig.year() {
|
||||||
self.year_cache.draw_text(
|
self.year_cache.draw_text(
|
||||||
&mut buf.offset((320, 0))?,
|
&mut buf.offset((self.font_size * 20, 0))?,
|
||||||
background_color,
|
background_color,
|
||||||
&Color::new(0.8, 0.8, 0.8, 1.0),
|
&Color::new(0.8, 0.8, 0.8, 1.0),
|
||||||
&format!("{:}", time.year()),
|
&format!("{:}", time.year()),
|
||||||
@@ -79,7 +81,10 @@ impl Calendar {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.day_cache.draw_text(
|
self.day_cache.draw_text(
|
||||||
&mut buf.offset((idx * 48 + 4, (y_off * 32) + 64))?,
|
&mut buf.offset((
|
||||||
|
idx * self.font_size * 3 + self.font_size / 5,
|
||||||
|
y_off * self.font_size * 2 + self.font_size * 4,
|
||||||
|
))?,
|
||||||
background_color,
|
background_color,
|
||||||
&Color::new(1.0, 1.0, 1.0, 1.0),
|
&Color::new(1.0, 1.0, 1.0, 1.0),
|
||||||
&wk_chr,
|
&wk_chr,
|
||||||
@@ -105,7 +110,7 @@ impl Calendar {
|
|||||||
//
|
//
|
||||||
let wk = time.iso_week();
|
let wk = time.iso_week();
|
||||||
self.calendar_cache.draw_text(
|
self.calendar_cache.draw_text(
|
||||||
&mut buf.offset((0 * 48, (y_off * 32) + 64))?,
|
&mut buf.offset((0, y_off * self.font_size * 2 + self.font_size * 4))?,
|
||||||
background_color,
|
background_color,
|
||||||
&Color::new(0.75, 0.75, 0.75, 1.0),
|
&Color::new(0.75, 0.75, 0.75, 1.0),
|
||||||
&format!("{:02}", wk.week()),
|
&format!("{:02}", wk.week()),
|
||||||
@@ -123,7 +128,10 @@ impl Calendar {
|
|||||||
};
|
};
|
||||||
|
|
||||||
self.calendar_cache.draw_text(
|
self.calendar_cache.draw_text(
|
||||||
&mut buf.offset((x_pos * 48, (y_off * 32) + 64))?,
|
&mut buf.offset((
|
||||||
|
x_pos * self.font_size * 3,
|
||||||
|
y_off * self.font_size * 2 + self.font_size * 4,
|
||||||
|
))?,
|
||||||
background_color,
|
background_color,
|
||||||
&c,
|
&c,
|
||||||
&format!("{:02}", time.day()),
|
&format!("{:02}", time.day()),
|
||||||
@@ -145,38 +153,63 @@ impl Calendar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Calendar {
|
impl Calendar {
|
||||||
pub fn new() -> Calendar {
|
pub fn new(font_size: f32, sections: u32) -> Box<Calendar> {
|
||||||
let mut calendar_cache = Font::new(&DEJAVUSANS_MONO, 32.0);
|
let mut calendar_cache = Font::new(&DEJAVUSANS_MONO, font_size * 2.0);
|
||||||
calendar_cache.add_str_to_cache("0123456789");
|
calendar_cache.add_str_to_cache("0123456789");
|
||||||
let mut month_cache = Font::new(&ROBOTO_REGULAR, 64.0);
|
let mut month_cache = Font::new(&ROBOTO_REGULAR, font_size * 4.0);
|
||||||
month_cache.add_str_to_cache("JanuryFebMchApilJgstSmOoNvD");
|
month_cache.add_str_to_cache("JanuryFebMchApilJgstSmOoNvD");
|
||||||
let mut year_cache = Font::new(&DEJAVUSANS_MONO, 24.0);
|
let mut year_cache = Font::new(&DEJAVUSANS_MONO, font_size * 1.5);
|
||||||
year_cache.add_str_to_cache("-0123456789");
|
year_cache.add_str_to_cache("-0123456789");
|
||||||
let mut day_cache = Font::new(&DEJAVUSANS_MONO, 16.0);
|
let mut day_cache = Font::new(&DEJAVUSANS_MONO, font_size);
|
||||||
day_cache.add_str_to_cache("MONTUEWDHFRISA");
|
day_cache.add_str_to_cache("MONTUEWDHFRISA");
|
||||||
Calendar {
|
Box::new(Calendar {
|
||||||
cur_date: Local::now().date(),
|
cur_date: Local::now().date(),
|
||||||
dirty: true,
|
dirty: true,
|
||||||
offset: 0.0,
|
offset: 0.0,
|
||||||
|
sections: sections,
|
||||||
|
font_size: font_size as u32,
|
||||||
calendar_cache: calendar_cache,
|
calendar_cache: calendar_cache,
|
||||||
month_cache: month_cache,
|
month_cache: month_cache,
|
||||||
year_cache: year_cache,
|
year_cache: year_cache,
|
||||||
day_cache: day_cache,
|
day_cache: day_cache,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleImpl for Calendar {
|
impl Widget for Calendar {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
let cal_width = 7 * self.font_size * 3 + self.font_size * 2;
|
||||||
|
let cal_pad = self.font_size * 3;
|
||||||
|
(
|
||||||
|
cal_width * self.sections + cal_pad * (self.sections - 1),
|
||||||
|
(self.font_size as f32 * 21.5) as u32,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&mut self,
|
||||||
buf: &mut Buffer,
|
ctx: &mut DrawContext,
|
||||||
background_color: &Color,
|
pos: (u32, u32),
|
||||||
time: &DateTime<Local>,
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
let (width, height) = self.size();
|
||||||
buf.memset(background_color);
|
if ctx.time.date() == self.cur_date && !ctx.force && !self.dirty {
|
||||||
let time = time.date();
|
return Ok(DrawReport::empty(width, height));
|
||||||
|
}
|
||||||
|
self.dirty = false;
|
||||||
|
self.cur_date = ctx.time.date();
|
||||||
|
|
||||||
|
let buf = &mut ctx.buf.subdimensions((pos.0, pos.1, width, height))?;
|
||||||
|
buf.memset(ctx.bg);
|
||||||
|
let time = ctx.time.date();
|
||||||
let mut t = time.with_day(1).unwrap();
|
let mut t = time.with_day(1).unwrap();
|
||||||
let o = (self.offset / 100.0) as i32;
|
let o = (self.offset / 100.0) as i32;
|
||||||
|
|
||||||
|
let cals = self.sections - 1;
|
||||||
|
let pre_cals = cals / 2;
|
||||||
|
for _ in 0..pre_cals {
|
||||||
|
t = t.pred().with_day(1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
if o != 0 {
|
if o != 0 {
|
||||||
let mut month = (t.month() - 1) as i32 + o;
|
let mut month = (t.month() - 1) as i32 + o;
|
||||||
let mut year = t.year();
|
let mut year = t.year();
|
||||||
@@ -194,64 +227,40 @@ impl ModuleImpl for Calendar {
|
|||||||
.with_month((month + 1) as u32)
|
.with_month((month + 1) as u32)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
self.draw_month(
|
let cal_pad = 7 * self.font_size * 3 + self.font_size * 5;
|
||||||
&mut buf.offset((0, 0))?,
|
for idx in 0..self.sections {
|
||||||
background_color,
|
self.draw_month(&mut buf.offset((cal_pad * idx, 0))?, ctx.bg, &time, &t)?;
|
||||||
&time,
|
|
||||||
&t.pred().with_day(1).unwrap(),
|
t = if t.month() == 12 {
|
||||||
)?;
|
t.with_year(t.year() + 1).unwrap().with_month(1).unwrap()
|
||||||
self.draw_month(
|
} else {
|
||||||
&mut buf.offset((432, 0))?,
|
t.with_month(t.month() + 1).unwrap()
|
||||||
background_color,
|
};
|
||||||
&time,
|
}
|
||||||
&t,
|
Ok(DrawReport {
|
||||||
)?;
|
width: width,
|
||||||
let n = if t.month() == 12 {
|
height: height,
|
||||||
t.with_year(t.year() + 1).unwrap().with_month(1).unwrap()
|
damage: vec![buf.get_signed_bounds()],
|
||||||
} else {
|
full_damage: false,
|
||||||
t.with_month(t.month() + 1).unwrap()
|
})
|
||||||
};
|
|
||||||
self.draw_month(
|
|
||||||
&mut buf.offset((864, 0))?,
|
|
||||||
background_color,
|
|
||||||
&time,
|
|
||||||
&n,
|
|
||||||
)?;
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
|
fn keyboard_input(&mut self, _: u32, _: ModifiersState, _: KeyState, _: Option<String>) {}
|
||||||
if self.dirty {
|
fn mouse_click(&mut self, _: u32, (x, _): (u32, u32)) {
|
||||||
self.dirty = false;
|
let cal_pad = 7 * self.font_size * 3 + self.font_size * 5;
|
||||||
Ok(true)
|
let cals = self.sections - 1;
|
||||||
} else if time.date() != self.cur_date || force {
|
let pre_cals = cals / 2;
|
||||||
self.cur_date = time.date();
|
if x < pre_cals * cal_pad {
|
||||||
Ok(true)
|
self.offset -= 100.0;
|
||||||
|
} else if x >= (pre_cals + 1) * cal_pad {
|
||||||
|
self.offset += 100.0;
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
self.offset = 0.0;
|
||||||
}
|
}
|
||||||
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
fn mouse_scroll(&mut self, (_, y): (f64, f64), _: (u32, u32)) {
|
||||||
fn input(&mut self, input: Input) {
|
self.offset += y;
|
||||||
match input {
|
self.dirty = true;
|
||||||
Input::Scroll { pos: _, x: _, y } => {
|
|
||||||
self.offset += y;
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
Input::Click {
|
|
||||||
pos: (x, _),
|
|
||||||
button: _,
|
|
||||||
} => {
|
|
||||||
if x < 448 {
|
|
||||||
self.offset -= 100.0;
|
|
||||||
} else if x >= 896 {
|
|
||||||
self.offset += 100.0;
|
|
||||||
} else {
|
|
||||||
self.offset = 0.0;
|
|
||||||
}
|
|
||||||
self.dirty = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
use crate::cmd::Cmd;
|
||||||
|
use crate::color::Color;
|
||||||
|
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||||
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
||||||
|
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
use chrono::{DateTime, Datelike, Duration, Local, Timelike};
|
||||||
|
|
||||||
|
pub struct Clock {
|
||||||
|
cur_time: DateTime<Local>,
|
||||||
|
clock_cache: Font,
|
||||||
|
size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clock {
|
||||||
|
pub fn new(size: f32, ch: Sender<Cmd>) -> Box<Clock> {
|
||||||
|
let _ = std::thread::Builder::new()
|
||||||
|
.name("clock_ticker".to_string())
|
||||||
|
.spawn(move || loop {
|
||||||
|
let n = Local::now();
|
||||||
|
let target = (n + Duration::seconds(60))
|
||||||
|
.with_second(0)
|
||||||
|
.unwrap()
|
||||||
|
.with_nanosecond(0)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let d = target - n;
|
||||||
|
|
||||||
|
std::thread::sleep(d.to_std().unwrap());
|
||||||
|
ch.send(Cmd::Draw).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut clock_cache = Font::new(&ROBOTO_REGULAR, size);
|
||||||
|
clock_cache.add_str_to_cache("0123456789:");
|
||||||
|
|
||||||
|
let time = Local::now();
|
||||||
|
|
||||||
|
Box::new(Clock {
|
||||||
|
cur_time: time.with_year(time.year().saturating_sub(1)).unwrap(),
|
||||||
|
clock_cache: clock_cache,
|
||||||
|
size: size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Clock {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
((2.0 * self.size).ceil() as u32, self.size.ceil() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let (width, height) = self.size();
|
||||||
|
|
||||||
|
if !(ctx.time.date() != self.cur_time.date()
|
||||||
|
|| ctx.time.hour() != self.cur_time.hour()
|
||||||
|
|| ctx.time.minute() != self.cur_time.minute()
|
||||||
|
|| ctx.force)
|
||||||
|
{
|
||||||
|
return Ok(DrawReport::empty(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cur_time = ctx.time.clone();
|
||||||
|
|
||||||
|
let buf = &mut ctx.buf.subdimensions((pos.0, pos.1, width, height))?;
|
||||||
|
buf.memset(ctx.bg);
|
||||||
|
|
||||||
|
let digit = (self.size * 0.45).ceil() as u32;
|
||||||
|
let colon = (self.size * 0.20).ceil() as u32;
|
||||||
|
self.clock_cache.draw_text_fixed_width(
|
||||||
|
buf,
|
||||||
|
ctx.bg,
|
||||||
|
&Color::new(1.0, 1.0, 1.0, 1.0),
|
||||||
|
&[digit, digit, colon, digit, digit],
|
||||||
|
&format!("{:02}:{:02}", ctx.time.hour(), ctx.time.minute()),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
damage: vec![(pos.0 as i32, pos.1 as i32, width as i32, height as i32)],
|
||||||
|
full_damage: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(&mut self, _: u32, _: ModifiersState, _: KeyState, _: Option<String>) {}
|
||||||
|
fn mouse_click(&mut self, _: u32, _: (u32, u32)) {}
|
||||||
|
fn mouse_scroll(&mut self, _: (f64, f64), _: (u32, u32)) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use crate::color::Color;
|
||||||
|
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||||
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
||||||
|
|
||||||
|
use chrono::{DateTime, Datelike, Local};
|
||||||
|
|
||||||
|
pub struct Date {
|
||||||
|
cur_time: DateTime<Local>,
|
||||||
|
date_cache: Font,
|
||||||
|
size: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Date {
|
||||||
|
pub fn new(size: f32) -> Box<Date> {
|
||||||
|
let mut date_cache = Font::new(&ROBOTO_REGULAR, size);
|
||||||
|
date_cache
|
||||||
|
.add_str_to_cache("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789,/ ");
|
||||||
|
|
||||||
|
let time = Local::now();
|
||||||
|
|
||||||
|
Box::new(Date {
|
||||||
|
cur_time: time.with_year(time.year().saturating_sub(1)).unwrap(),
|
||||||
|
date_cache: date_cache,
|
||||||
|
size: size,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Date {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
((6.5 * self.size).ceil() as u32, self.size.ceil() as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let (width, height) = self.size();
|
||||||
|
|
||||||
|
if !(ctx.time.date() != self.cur_time.date() || ctx.force) {
|
||||||
|
return Ok(DrawReport::empty(width, height));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cur_time = ctx.time.clone();
|
||||||
|
|
||||||
|
let buf = &mut ctx.buf.subdimensions((pos.0, pos.1, width, height))?;
|
||||||
|
buf.memset(ctx.bg);
|
||||||
|
self.date_cache.draw_text(
|
||||||
|
buf,
|
||||||
|
ctx.bg,
|
||||||
|
&Color::new(1.0, 1.0, 1.0, 1.0),
|
||||||
|
&format!(
|
||||||
|
"{:?}, {:02}/{:02}/{:4}",
|
||||||
|
ctx.time.weekday(),
|
||||||
|
ctx.time.day(),
|
||||||
|
ctx.time.month(),
|
||||||
|
ctx.time.year()
|
||||||
|
),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
damage: vec![(pos.0 as i32, pos.1 as i32, width as i32, height as i32)],
|
||||||
|
full_damage: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(&mut self, _: u32, _: ModifiersState, _: KeyState, _: Option<String>) {}
|
||||||
|
fn mouse_click(&mut self, _: u32, _: (u32, u32)) {}
|
||||||
|
fn mouse_scroll(&mut self, _: (f64, f64), _: (u32, u32)) {}
|
||||||
|
}
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
use crate::buffer::Buffer;
|
||||||
|
use crate::cmd::Cmd;
|
||||||
|
use crate::color::Color;
|
||||||
|
use crate::desktop::{load_desktop_files, Desktop};
|
||||||
|
use crate::draw::{Font, ROBOTO_REGULAR};
|
||||||
|
use crate::widget::{DrawContext, DrawReport, KeyState, ModifiersState, Widget};
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use std::process::Command;
|
||||||
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
|
use fuzzy_matcher::skim::{fuzzy_indices, fuzzy_match};
|
||||||
|
use smithay_client_toolkit::keyboard::keysyms;
|
||||||
|
|
||||||
|
pub struct Launcher {
|
||||||
|
options: Vec<Desktop>,
|
||||||
|
term_opener: String,
|
||||||
|
app_opener: String,
|
||||||
|
url_opener: String,
|
||||||
|
matches: Vec<Desktop>,
|
||||||
|
input: String,
|
||||||
|
result: Option<String>,
|
||||||
|
offset: usize,
|
||||||
|
font: RefCell<Font>,
|
||||||
|
font_size: u32,
|
||||||
|
length: u32,
|
||||||
|
dirty: bool,
|
||||||
|
tx: Sender<Cmd>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Launcher {
|
||||||
|
pub fn new(font_size: f32, length: u32, listener: Sender<Cmd>, app: String, term: String, url: String) -> Box<Launcher> {
|
||||||
|
Box::new(Launcher {
|
||||||
|
options: load_desktop_files(),
|
||||||
|
term_opener: term,
|
||||||
|
app_opener: app,
|
||||||
|
url_opener: url,
|
||||||
|
matches: vec![],
|
||||||
|
input: "".to_string(),
|
||||||
|
result: None,
|
||||||
|
offset: 0,
|
||||||
|
font: RefCell::new(Font::new(&ROBOTO_REGULAR, font_size)),
|
||||||
|
font_size: font_size as u32,
|
||||||
|
length: length,
|
||||||
|
dirty: true,
|
||||||
|
tx: listener,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_launcher(&self, buf: &mut Buffer, bg: &Color) -> Result<(), ::std::io::Error> {
|
||||||
|
let mut x_off = if self.input.len() > 0 {
|
||||||
|
let c = if self.matches.len() == 0 {
|
||||||
|
Color::new(1.0, 0.5, 0.5, 1.0)
|
||||||
|
} else {
|
||||||
|
Color::new(1.0, 1.0, 1.0, 1.0)
|
||||||
|
};
|
||||||
|
let dim = self
|
||||||
|
.font
|
||||||
|
.borrow_mut()
|
||||||
|
.auto_draw_text(buf, bg, &c, &self.input)?;
|
||||||
|
|
||||||
|
dim.0 + self.font_size / 4
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut width_remaining: i32 = (self.length - x_off) as i32;
|
||||||
|
for (idx, m) in self.matches.iter().enumerate() {
|
||||||
|
let mut b = match buf.offset((x_off, 0)) {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(_) => break,
|
||||||
|
};
|
||||||
|
let size = if idx == self.offset && self.input.len() > 0 {
|
||||||
|
let (_, indices) =
|
||||||
|
fuzzy_indices(&m.name.to_lowercase(), &self.input.to_lowercase()).unwrap();
|
||||||
|
let mut colors = Vec::with_capacity(m.name.len());
|
||||||
|
for pos in 0..m.name.len() {
|
||||||
|
if indices.contains(&pos) {
|
||||||
|
colors.push(Color::new(1.0, 1.0, 1.0, 1.0));
|
||||||
|
} else {
|
||||||
|
colors.push(Color::new(0.75, 0.75, 0.75, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.font
|
||||||
|
.borrow_mut()
|
||||||
|
.auto_draw_text_individual_colors(&mut b, bg, &colors, &m.name)?
|
||||||
|
} else {
|
||||||
|
self.font.borrow_mut().auto_draw_text(
|
||||||
|
&mut b,
|
||||||
|
bg,
|
||||||
|
&Color::new(0.5, 0.5, 0.5, 1.0),
|
||||||
|
&m.name,
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
|
x_off += size.0 + self.font_size / 4;
|
||||||
|
width_remaining -= (size.0 + self.font_size / 4) as i32;
|
||||||
|
|
||||||
|
if width_remaining < 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_calc(&self, buf: &mut Buffer, bg: &Color) -> Result<(), ::std::io::Error> {
|
||||||
|
let x_off = self
|
||||||
|
.font
|
||||||
|
.borrow_mut()
|
||||||
|
.auto_draw_text(buf, bg, &Color::new(1.0, 1.0, 0.0, 1.0), "=")?
|
||||||
|
.0
|
||||||
|
+ self.font_size / 4;
|
||||||
|
|
||||||
|
let x_off = if self.input.len() > 0 {
|
||||||
|
let dim = self.font.borrow_mut().auto_draw_text(
|
||||||
|
&mut buf.offset((x_off, 0))?,
|
||||||
|
bg,
|
||||||
|
&Color::new(1.0, 1.0, 1.0, 1.0),
|
||||||
|
&self.input[1..],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
x_off + dim.0 + self.font_size / 4
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(result) = &self.result {
|
||||||
|
self.font.borrow_mut().auto_draw_text(
|
||||||
|
&mut buf.offset((x_off, 0))?,
|
||||||
|
bg,
|
||||||
|
&Color::new(0.75, 0.75, 0.75, 1.0),
|
||||||
|
&format!(" = {:}", result),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw_shell(&self, buf: &mut Buffer, bg: &Color) -> Result<(), ::std::io::Error> {
|
||||||
|
let x_off = self
|
||||||
|
.font
|
||||||
|
.borrow_mut()
|
||||||
|
.auto_draw_text(
|
||||||
|
&mut buf.offset((0, 0))?,
|
||||||
|
bg,
|
||||||
|
&Color::new(1.0, 1.0, 0.0, 1.0),
|
||||||
|
"!",
|
||||||
|
)?
|
||||||
|
.0
|
||||||
|
+ self.font_size / 4;
|
||||||
|
|
||||||
|
if self.input.len() > 0 {
|
||||||
|
self.font.borrow_mut().auto_draw_text(
|
||||||
|
&mut buf.offset((x_off, 0))?,
|
||||||
|
bg,
|
||||||
|
&Color::new(1.0, 1.0, 1.0, 1.0),
|
||||||
|
&self.input[1..],
|
||||||
|
)?;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ivy")]
|
||||||
|
fn calc(s: &str) -> Result<String, String> {
|
||||||
|
libivy::eval(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "rcalc")]
|
||||||
|
fn calc(s: &str) -> Result<String, String> {
|
||||||
|
rcalc_lib::parse::eval(s, &mut rcalc_lib::parse::CalcState::new())
|
||||||
|
.map(|x| format!("{}", x).to_string())
|
||||||
|
.map_err(|x| format!("{}", x).to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "bc")]
|
||||||
|
fn calc(s: &str) -> Result<String, String> {
|
||||||
|
use std::io::Write;
|
||||||
|
let mut child = std::process::Command::new("bc")
|
||||||
|
.stdin(std::process::Stdio::piped())
|
||||||
|
.stdout(std::process::Stdio::piped())
|
||||||
|
.stderr(std::process::Stdio::piped())
|
||||||
|
.arg("--mathlib")
|
||||||
|
.spawn()
|
||||||
|
.map_err(|_| "bc not available".to_string())?;
|
||||||
|
{
|
||||||
|
let stdin = child.stdin.as_mut().unwrap();
|
||||||
|
stdin
|
||||||
|
.write_all(s.as_bytes())
|
||||||
|
.map_err(|_| "unable to write to stdin".to_string())?;
|
||||||
|
stdin
|
||||||
|
.write_all("\n".as_bytes())
|
||||||
|
.map_err(|_| "unable to write to stdin".to_string())?;
|
||||||
|
}
|
||||||
|
let output = child
|
||||||
|
.wait_with_output()
|
||||||
|
.map_err(|_| "unable to run bc".to_string())?;
|
||||||
|
Ok(std::str::from_utf8(&output.stdout)
|
||||||
|
.map_err(|_| "unable to read from bc")?
|
||||||
|
.trim()
|
||||||
|
.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(feature = "ivy", feature = "bc", feature = "rcalc")))]
|
||||||
|
fn calc(_s: &str) -> Result<String, String> {
|
||||||
|
Err("no calculator implementation available".to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wlcopy(s: &str) -> Result<(), String> {
|
||||||
|
let mut child = std::process::Command::new("wl-copy")
|
||||||
|
.stdin(std::process::Stdio::null())
|
||||||
|
.stdout(std::process::Stdio::null())
|
||||||
|
.stderr(std::process::Stdio::null())
|
||||||
|
.arg(s)
|
||||||
|
.spawn()
|
||||||
|
.map_err(|_| "wl-copy not available".to_string())?;
|
||||||
|
child
|
||||||
|
.wait()
|
||||||
|
.map_err(|_| "unable to run wl-copy".to_string())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Widget for Launcher {
|
||||||
|
fn size(&self) -> (u32, u32) {
|
||||||
|
(self.length, self.font_size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(
|
||||||
|
&mut self,
|
||||||
|
ctx: &mut DrawContext,
|
||||||
|
pos: (u32, u32),
|
||||||
|
) -> Result<DrawReport, ::std::io::Error> {
|
||||||
|
let (width, height) = self.size();
|
||||||
|
if !self.dirty && !ctx.force {
|
||||||
|
return Ok(DrawReport::empty(width, height));
|
||||||
|
}
|
||||||
|
self.dirty = false;
|
||||||
|
|
||||||
|
match self.input.chars().next() {
|
||||||
|
Some('=') => {
|
||||||
|
if self.input.len() > 1 {
|
||||||
|
match calc(&self.input.chars().skip(1).collect::<String>()) {
|
||||||
|
Ok(v) => self.result = Some(v),
|
||||||
|
Err(_) => self.result = None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some('!') => (),
|
||||||
|
_ => {
|
||||||
|
let mut m = self
|
||||||
|
.options
|
||||||
|
.iter()
|
||||||
|
.map(|x| {
|
||||||
|
(
|
||||||
|
fuzzy_match(&x.name.to_lowercase(), &self.input.to_lowercase()),
|
||||||
|
x,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.filter(|(x, _)| x.is_some())
|
||||||
|
.map(|(x, y)| (x.unwrap(), y.clone()))
|
||||||
|
.collect::<Vec<(i64, Desktop)>>();
|
||||||
|
|
||||||
|
m.sort_by(|(x1, y1), (x2, y2)| {
|
||||||
|
if x1 > x2 {
|
||||||
|
Ordering::Less
|
||||||
|
} else if x1 < x2 {
|
||||||
|
Ordering::Greater
|
||||||
|
} else if y1.name.len() < y2.name.len() {
|
||||||
|
Ordering::Less
|
||||||
|
} else if y1.name.len() > y2.name.len() {
|
||||||
|
Ordering::Greater
|
||||||
|
} else {
|
||||||
|
y1.cmp(y2)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
self.matches = m.into_iter().map(|(_, x)| x).collect();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let buf = &mut ctx.buf.subdimensions((pos.0, pos.1, width, height))?;
|
||||||
|
buf.memset(ctx.bg);
|
||||||
|
|
||||||
|
match self.input.chars().next() {
|
||||||
|
Some('=') => self.draw_calc(buf, ctx.bg),
|
||||||
|
Some('!') => self.draw_shell(buf, ctx.bg),
|
||||||
|
_ => self.draw_launcher(buf, ctx.bg),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
Ok(DrawReport {
|
||||||
|
width: width,
|
||||||
|
height: height,
|
||||||
|
damage: vec![buf.get_signed_bounds()],
|
||||||
|
full_damage: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboard_input(
|
||||||
|
&mut self,
|
||||||
|
key: u32,
|
||||||
|
_: ModifiersState,
|
||||||
|
_: KeyState,
|
||||||
|
interpreted: Option<String>,
|
||||||
|
) {
|
||||||
|
match key {
|
||||||
|
keysyms::XKB_KEY_BackSpace => {
|
||||||
|
if self.input.len() > 0 {
|
||||||
|
self.input = self.input[..self.input.len() - 1].to_string();
|
||||||
|
self.offset = 0;
|
||||||
|
self.result = None;
|
||||||
|
self.dirty = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keysyms::XKB_KEY_Return => {
|
||||||
|
match self.input.chars().next() {
|
||||||
|
Some('=') => match self.result {
|
||||||
|
Some(ref v) => {
|
||||||
|
let _ = wlcopy(&v);
|
||||||
|
}
|
||||||
|
None => (),
|
||||||
|
},
|
||||||
|
Some('!') => {
|
||||||
|
let _ = Command::new("sh")
|
||||||
|
.arg("-c")
|
||||||
|
.arg(self.input.chars().skip(1).collect::<String>())
|
||||||
|
.spawn();
|
||||||
|
self.tx.send(Cmd::Exit).unwrap();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if self.matches.len() > self.offset {
|
||||||
|
let d = &self.matches[self.offset];
|
||||||
|
if let Some(exec) = &d.exec {
|
||||||
|
let exec = exec
|
||||||
|
.replace("%f", "")
|
||||||
|
.replace("%F", "")
|
||||||
|
.replace("%u", "")
|
||||||
|
.replace("%U", "");
|
||||||
|
let prefix = if d.term {
|
||||||
|
&self.term_opener
|
||||||
|
} else {
|
||||||
|
&self.app_opener
|
||||||
|
};
|
||||||
|
|
||||||
|
let lexed = if prefix.len() > 0 {
|
||||||
|
let mut prefix = shlex::split(prefix).unwrap();
|
||||||
|
prefix.push(exec);
|
||||||
|
prefix
|
||||||
|
} else {
|
||||||
|
shlex::split(&exec).unwrap()
|
||||||
|
};
|
||||||
|
if lexed.len() > 0 {
|
||||||
|
let _ =
|
||||||
|
Command::new(lexed[0].clone()).args(&lexed[1..]).spawn();
|
||||||
|
self.tx.send(Cmd::Exit).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(url) = &d.url {
|
||||||
|
if self.url_opener.len() > 0 {
|
||||||
|
let mut lexed = shlex::split(&self.url_opener).unwrap();
|
||||||
|
lexed.push(url.to_string());
|
||||||
|
if lexed.len() > 0 {
|
||||||
|
let _ = Command::new(lexed[0].clone())
|
||||||
|
.args(&lexed[1..])
|
||||||
|
.spawn();
|
||||||
|
self.tx.send(Cmd::Exit).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
keysyms::XKB_KEY_Right => {
|
||||||
|
if self.matches.len() > 0 && self.offset < self.matches.len() - 1 {
|
||||||
|
self.offset += 1;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keysyms::XKB_KEY_Left => {
|
||||||
|
if self.matches.len() > 0 && self.offset > 0 {
|
||||||
|
self.offset -= 1;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => match interpreted {
|
||||||
|
Some(v) => {
|
||||||
|
self.input += &v;
|
||||||
|
self.offset = 0;
|
||||||
|
self.result = None;
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn mouse_click(&mut self, _: u32, _: (u32, u32)) {}
|
||||||
|
fn mouse_scroll(&mut self, _: (f64, f64), _: (u32, u32)) {}
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
pub mod backlight;
|
pub mod backlight;
|
||||||
|
pub mod bar_widget;
|
||||||
pub mod battery;
|
pub mod battery;
|
||||||
pub mod calendar;
|
pub mod calendar;
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
|
pub mod date;
|
||||||
pub mod launcher;
|
pub mod launcher;
|
||||||
pub mod module;
|
|
||||||
pub mod sound;
|
pub mod sound;
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
use crate::buffer::Buffer;
|
|
||||||
use crate::cmd::Cmd;
|
use crate::cmd::Cmd;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::draw::{draw_bar, draw_box, Font, ROBOTO_REGULAR};
|
use crate::widgets::bar_widget::{BarWidget, BarWidgetImpl};
|
||||||
use crate::modules::module::{Input, ModuleImpl};
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@@ -12,8 +10,6 @@ use std::sync::mpsc::{channel, Sender};
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
|
|
||||||
use libpulse_binding::callbacks::ListResult;
|
use libpulse_binding::callbacks::ListResult;
|
||||||
use libpulse_binding::context::{
|
use libpulse_binding::context::{
|
||||||
flags, introspect::ServerInfo, introspect::SinkInfo, subscribe::subscription_masks,
|
flags, introspect::ServerInfo, introspect::SinkInfo, subscribe::subscription_masks,
|
||||||
@@ -385,12 +381,17 @@ impl PulseAudioSoundDevice {
|
|||||||
Ok(device)
|
Ok(device)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn muted(&self) -> bool {
|
||||||
|
let inner = self.inner.lock().unwrap();
|
||||||
|
inner.muted
|
||||||
|
}
|
||||||
|
|
||||||
fn volume(&self) -> f32 {
|
fn volume(&self) -> f32 {
|
||||||
let inner = self.inner.lock().unwrap();
|
let inner = self.inner.lock().unwrap();
|
||||||
inner.volume_avg
|
inner.volume_avg
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_volume(&mut self, step: f32) -> Result<(), ::std::io::Error> {
|
fn inc_volume(&mut self, step: f32) -> Result<(), ::std::io::Error> {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
let mut volume = match inner.volume {
|
let mut volume = match inner.volume {
|
||||||
Some(volume) => volume,
|
Some(volume) => volume,
|
||||||
@@ -427,6 +428,38 @@ impl PulseAudioSoundDevice {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_volume(&mut self, val: f32) -> Result<(), ::std::io::Error> {
|
||||||
|
let mut inner = self.inner.lock().unwrap();
|
||||||
|
let mut volume = match inner.volume {
|
||||||
|
Some(volume) => volume,
|
||||||
|
None => {
|
||||||
|
return Err(::std::io::Error::new(
|
||||||
|
::std::io::ErrorKind::Other,
|
||||||
|
"unable to set volume",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// apply step to volumes
|
||||||
|
volume.scale(Volume((val * VOLUME_NORM.0 as f32).round() as u32));
|
||||||
|
|
||||||
|
let name = inner
|
||||||
|
.name
|
||||||
|
.clone()
|
||||||
|
.unwrap_or_else(|| inner.default_sink.clone());
|
||||||
|
|
||||||
|
// update volumes
|
||||||
|
inner.volume = Some(volume);
|
||||||
|
inner.volume_avg = volume.avg().0 as f32 / VOLUME_NORM.0 as f32;
|
||||||
|
self.client
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.send(PulseAudioClientRequest::SetSinkVolumeByName(
|
||||||
|
None, name, volume,
|
||||||
|
))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn toggle(&mut self) -> Result<(), ::std::io::Error> {
|
fn toggle(&mut self) -> Result<(), ::std::io::Error> {
|
||||||
let mut inner = self.inner.lock().unwrap();
|
let mut inner = self.inner.lock().unwrap();
|
||||||
inner.muted = !inner.muted;
|
inner.muted = !inner.muted;
|
||||||
@@ -447,91 +480,46 @@ impl PulseAudioSoundDevice {
|
|||||||
|
|
||||||
pub struct PulseAudio {
|
pub struct PulseAudio {
|
||||||
device: PulseAudioSoundDevice,
|
device: PulseAudioSoundDevice,
|
||||||
font: Font,
|
|
||||||
dirty: Arc<Mutex<bool>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PulseAudio {
|
impl PulseAudio {
|
||||||
pub fn new(listener: Sender<Cmd>) -> Result<PulseAudio, ::std::io::Error> {
|
pub fn new(
|
||||||
let mut font = Font::new(&ROBOTO_REGULAR, 24.0);
|
font_size: f32,
|
||||||
font.add_str_to_cache("volume");
|
length: u32,
|
||||||
let dirty = Arc::new(Mutex::new(true));
|
sender: Sender<Cmd>,
|
||||||
let dev_dirty = dirty.clone();
|
) -> Result<Box<BarWidget>, ::std::io::Error> {
|
||||||
let device = PulseAudioSoundDevice::new(move || {
|
BarWidget::new(font_size, length, move |dirty| {
|
||||||
*dev_dirty.lock().unwrap() = true;
|
let device = PulseAudioSoundDevice::new(move || {
|
||||||
listener.send(Cmd::Draw).unwrap();
|
*dirty.lock().unwrap() = true;
|
||||||
})?;
|
sender.send(Cmd::Draw).unwrap();
|
||||||
|
})?;
|
||||||
let pa = PulseAudio {
|
Ok(Box::new(PulseAudio { device: device }))
|
||||||
device: device,
|
})
|
||||||
font: font,
|
|
||||||
dirty: dirty,
|
|
||||||
};
|
|
||||||
Ok(pa)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleImpl for PulseAudio {
|
impl BarWidgetImpl for PulseAudio {
|
||||||
fn draw(
|
fn name(&self) -> &str {
|
||||||
&self,
|
"volume"
|
||||||
buf: &mut Buffer,
|
}
|
||||||
bg: &Color,
|
fn value(&self) -> f32 {
|
||||||
_time: &DateTime<Local>,
|
self.device.volume()
|
||||||
) -> Result<Vec<(i32, i32, i32, i32)>, ::std::io::Error> {
|
}
|
||||||
let muted = self.device.inner.lock().unwrap().muted;
|
fn color(&self) -> Color {
|
||||||
let mut vol = self.device.volume();
|
let muted = self.device.muted();
|
||||||
buf.memset(bg);
|
if muted {
|
||||||
let c = if muted {
|
|
||||||
Color::new(1.0, 1.0, 0.0, 1.0)
|
Color::new(1.0, 1.0, 0.0, 1.0)
|
||||||
} else {
|
} else {
|
||||||
Color::new(1.0, 1.0, 1.0, 1.0)
|
Color::new(1.0, 1.0, 1.0, 1.0)
|
||||||
};
|
|
||||||
self.font.draw_text(
|
|
||||||
buf,
|
|
||||||
bg,
|
|
||||||
&Color::new(1.0, 1.0, 1.0, 1.0),
|
|
||||||
"volume",
|
|
||||||
)?;
|
|
||||||
draw_bar(&mut buf.offset((128, 0))?, &c, 464, 24, vol)?;
|
|
||||||
let mut iter = 1.0;
|
|
||||||
while vol > 1.0 {
|
|
||||||
let c = &Color::new(0.75 / iter, 0.25 / iter, 0.25 / iter, 1.0);
|
|
||||||
vol -= 1.0;
|
|
||||||
iter += 1.0;
|
|
||||||
draw_bar(&mut buf.offset((128, 0))?, &c, 464, 24, vol)?;
|
|
||||||
}
|
|
||||||
draw_box(&mut buf.offset((128, 0))?, &c, (464, 24))?;
|
|
||||||
Ok(vec![buf.get_signed_bounds()])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, _time: &DateTime<Local>, force: bool) -> Result<bool, ::std::io::Error> {
|
|
||||||
let mut d = self.dirty.lock().unwrap();
|
|
||||||
if *d || force {
|
|
||||||
*d = false;
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn inc(&mut self, inc: f32) {
|
||||||
fn input(&mut self, input: Input) {
|
self.device.inc_volume(inc).unwrap();
|
||||||
match input {
|
}
|
||||||
Input::Scroll {
|
fn set(&mut self, val: f32) {
|
||||||
pos: _pos,
|
self.device.set_volume(val).unwrap();
|
||||||
x: _x,
|
}
|
||||||
y,
|
fn toggle(&mut self) {
|
||||||
} => {
|
self.device.toggle().unwrap();
|
||||||
self.device.set_volume(y as f32 / -800.0).unwrap();
|
|
||||||
*self.dirty.lock().unwrap() = true;
|
|
||||||
}
|
|
||||||
Input::Click { pos: _pos, button } => match button {
|
|
||||||
273 => {
|
|
||||||
self.device.toggle().unwrap();
|
|
||||||
*self.dirty.lock().unwrap() = true;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
},
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user