Added cpu temperature and battery

This commit is contained in:
mustafa salih 2021-01-09 04:10:19 +03:00
parent ff8ccbd2ca
commit 2983e00786
11 changed files with 829 additions and 95 deletions

View File

@ -1,20 +0,0 @@
name: Rust
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose

76
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at mustafasalih1991@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

1
CONTRIBUTING.md Normal file
View File

@ -0,0 +1 @@
All Contributions are welcome.

378
Cargo.lock generated
View File

@ -1,11 +1,165 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
[[package]]
name = "ahash"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e"
[[package]]
name = "alsa"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb213f6b3e4b1480a60931ca2035794aa67b73103d254715b1db7b70dcb3c934"
dependencies = [
"alsa-sys",
"bitflags",
"libc",
"nix 0.15.0",
]
[[package]]
name = "alsa-sys"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db8fee663d06c4e303404ef5f40488a53e062f89ba8bfed81f42325aafad1527"
dependencies = [
"libc",
"pkg-config",
]
[[package]]
name = "async-channel"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59740d83946db6a5af71ae25ddf9562c2b176b2ca42cf99a455f09f4a220d6b9"
dependencies = [
"concurrent-queue",
"event-listener",
"futures-core",
]
[[package]]
name = "async-io"
version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9315f8f07556761c3e48fec2e6b276004acf426e6dc068b2c2251854d65ee0fd"
dependencies = [
"concurrent-queue",
"fastrand",
"futures-lite",
"libc",
"log",
"nb-connect",
"once_cell",
"parking",
"polling",
"vec-arena",
"waker-fn",
"winapi",
]
[[package]]
name = "async-net"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06de475c85affe184648202401d7622afb32f0f74e02192857d0201a16defbe5"
dependencies = [
"async-io",
"blocking",
"fastrand",
"futures-lite",
]
[[package]]
name = "async-task"
version = "4.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0"
[[package]]
name = "atomic-waker"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.0.1" version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "blocking"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9"
dependencies = [
"async-channel",
"async-task",
"atomic-waker",
"fastrand",
"futures-lite",
"once_cell",
]
[[package]]
name = "breadx"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bff92e2ec549fe3f20386d73efb30263be7467463def0c2a780d7cee5dbf5e3"
dependencies = [
"async-io",
"async-net",
"blocking",
"bytemuck",
"cfg-if 1.0.0",
"cty",
"futures-lite",
"hashbrown",
"log",
"memchr",
"nix 0.19.1",
"ref_slice",
"tinyvec",
]
[[package]]
name = "bytemuck"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41aa2ec95ca3b5c54cf73c91acf06d24f4495d5f1b1c12506ae3483d646177ac"
[[package]]
name = "cache-padded"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
[[package]]
name = "cc"
version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.19" version = "0.4.19"
@ -19,6 +173,81 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "concurrent-queue"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3"
dependencies = [
"cache-padded",
]
[[package]]
name = "cty"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7313c0d620d0cb4dbd9d019e461a4beb501071ff46ec0ab933efb4daa76d73e3"
[[package]]
name = "event-listener"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59"
[[package]]
name = "fastrand"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca5faf057445ce5c9d4329e382b2ce7ca38550ef3b73a5348362d5f24e0c7fe3"
dependencies = [
"instant",
]
[[package]]
name = "futures-core"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748"
[[package]]
name = "futures-io"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"
[[package]]
name = "futures-lite"
version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4481d0cd0de1d204a4fa55e7d45f07b1d958abcb06714b3446438e2eff695fb"
dependencies = [
"fastrand",
"futures-core",
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"waker-fn",
]
[[package]]
name = "hashbrown"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
dependencies = [
"ahash",
]
[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.81" version = "0.2.81"
@ -31,6 +260,62 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]]
name = "log"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
dependencies = [
"cfg-if 0.1.10",
]
[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
[[package]]
name = "minreq"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2466d0a7e6bfcd54f69e4a17d4a4318985aaaf7fe3df4cd3b6f11ff551129ca3"
[[package]]
name = "nb-connect"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "nix"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229"
dependencies = [
"bitflags",
"cc",
"cfg-if 0.1.10",
"libc",
"void",
]
[[package]]
name = "nix"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ccba0cfe4fdf15982d1674c69b1fd80bad427d293849982668dfe454bd61f2"
dependencies = [
"bitflags",
"cc",
"cfg-if 1.0.0",
"libc",
]
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.44" version = "0.1.44"
@ -51,10 +336,57 @@ dependencies = [
] ]
[[package]] [[package]]
name = "rsblocks" name = "once_cell"
version = "0.1.1" version = "1.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
[[package]]
name = "parking"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
[[package]]
name = "pin-project-lite"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"
[[package]]
name = "pkg-config"
version = "0.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c"
[[package]]
name = "polling"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2a7bc6b2a29e632e45451c941832803a18cce6781db04de8a04696cdca8bde4"
dependencies = [ dependencies = [
"cfg-if 0.1.10",
"libc",
"log",
"wepoll-sys",
"winapi",
]
[[package]]
name = "ref_slice"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e1b7878800220a76a08f32c057829511440f65528b63b940f2f2bc145d7ac68"
[[package]]
name = "rsblocks"
version = "0.1.5"
dependencies = [
"alsa",
"breadx",
"chrono", "chrono",
"minreq",
"nix 0.19.1",
"yaml-rust", "yaml-rust",
] ]
@ -69,12 +401,54 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "tinyvec"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "vec-arena"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eafc1b9b2dfc6f5529177b62cf806484db55b32dc7c9658a118e11bbeb33061d"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "waker-fn"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.10.0+wasi-snapshot-preview1" version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wepoll-sys"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fcb14dea929042224824779fbc82d9fab8d2e6d3cbc0ac404de8edf489e77ff"
dependencies = [
"cc",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rsblocks" name = "rsblocks"
version = "0.1.2" version = "0.1.5"
authors = ["mustafa salih <mustafasalih1991@gmail.com>"] authors = ["mustafa salih <mustafasalih1991@gmail.com>"]
edition = "2018" edition = "2018"
readme = "README.md" readme = "README.md"
@ -12,4 +12,11 @@ description = "a multi threaded status bar for dwm window manager for linux"
[dependencies] [dependencies]
chrono = "0.4" chrono = "0.4"
yaml-rust = "0.4" yaml-rust = "0.4"
minreq = "2.2.1"
alsa = "0.4.3"
nix = "0.19.1"
[dependencies.breadx]
version = "0.1.11"
features = ["async"]

View File

@ -1,15 +1,22 @@
# rsblocks # rsblocks
A minimal multi threaded fast status bar for dwm window manager written in **Rust** 🦀 [<img alt="github" src="https://img.shields.io/static/v1?label=github&message=rsblocks&color=acb0d0&logo=Github&style=flat-square&logoColor=a9b1d6" height="20">](https://github.com/MustafaSalih1993/rsblocks)
[<img alt="crates" src="https://img.shields.io/crates/v/rsblocks?logo=rust&logoColor=a9b1d6&style=flat-square&color=fc8d62" height="20">](https://crates.io/crates/rsblocks)
A multi threaded fast status bar for dwm window manager written in **Rust** 🦀
<p> <p>
<img align="center" src="./screenshots/1.png"/> <img align="center" src="./screenshots/2.png"/>
</p><br/> </p><br/>
## Features ## Features
* Multi Threads * Multi Threads
* Time/Date * Time/Date
* Used Memory * Memory Usage
* Used Disk space * Disk Usage
* Sound volume _reads from `alsa-utils` for now_ * Weather Temperature
* Sound Volume
* Battery Percentage
* Cpu Temperature
* Easy to configure with `rsblocks.yml` file * Easy to configure with `rsblocks.yml` file
@ -58,5 +65,8 @@ cp ./rsblocks.yml ~/.config/rsblocks/rsblocks.yml
## Contributions ## Contributions
All Contributions are welcome. All Contributions are welcome.
## Credits
* [wttr.in](https://github.com/chubin/wttr.in) for using their weather API
## License ## License
[MIT](./LICENSE) [MIT](./LICENSE)

View File

@ -1,28 +1,60 @@
# This is the full configuration template available for rsblocks tool # This is the full configuration template available for rsblocks.
# the names are clearly defines itself.
# NOTE: the (delay) is **seconds** with the float point to update the item in the bar. # NOTE: the (delay) is in **SECONDS** and the float point "." is required.
general: general:
seperator: '┃' seperator: '┃'
time: time:
icon: '' icon: ''
format: '%d %b, %I:%M:%S %p' format: '%d %b, %I:%M:%S %p'
delay: 1.0 delay: 1.0
memory: memory:
enable: true enable: true
icon: '▦' icon: '▦'
delay: 2.0 delay: 2.0
disk: disk:
enable: true enable: true
icon: '' icon: ''
delay: 120.0 delay: 120.0
# reads from alsa-utils
# reads from alsa, alsa-utils package should
# be installed on the system to make it work.
volume: volume:
enable: false enable: true
icon: '' icon: ''
delay: 0.18 delay: 0.18
card: 'PULSE'
# weather format options is available on
# https://github.com/chubin/wttr.in
# NOTES:
# - if the city field set to empty, it will try to read location automatically.
# - if you have multiple formats and want to insert space between them use '+' instead.
# - giving (city) a value is recommended.
weather:
enable: true
icon: ''
city: ''
format: '%l:+%t'
delay: 7200.0 # 7200 seconds = 2 hours
battery:
source: 'BAT0'
icon: ''
enable: false
delay: 120.0
cpu_temperature:
icon: ''
enable: true
delay: 120.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

BIN
screenshots/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -1,9 +1,10 @@
use alsa::mixer::{Mixer, SelemChannelId, SelemId};
use breadx::{display::*, window::Window};
use chrono::prelude::*; use chrono::prelude::*;
use std::env; use minreq;
use std::fs::File; use std::fs::File;
use std::io::Error; use std::io::Error;
use std::io::Read; use std::io::Read;
use std::process::Command;
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@ -15,6 +16,9 @@ pub enum ThreadsData {
Disk(String), Disk(String),
Memory(String), Memory(String),
Time(String), Time(String),
Weather(String),
Battery(String),
CpuTemp(String),
} }
#[derive(Clone)] #[derive(Clone)]
@ -24,6 +28,9 @@ pub struct Config {
pub memory: Memory, pub memory: Memory,
pub disk: Disk, pub disk: Disk,
pub volume: Volume, pub volume: Volume,
pub weather: Weather,
pub battery: Battery,
pub cpu_temperature: CpuTemp,
} }
#[derive(Clone)] #[derive(Clone)]
@ -51,10 +58,56 @@ pub struct Volume {
pub icon: String, pub icon: String,
pub enabled: bool, pub enabled: bool,
pub delay: f64, pub delay: f64,
pub card: String,
} }
#[derive(Clone)]
pub struct Weather {
pub city: String,
pub format: String,
pub icon: String,
pub enabled: bool,
pub delay: f64,
}
#[derive(Clone)]
pub struct Battery {
pub source: String,
pub icon: String,
pub enabled: bool,
pub delay: f64,
}
#[derive(Clone)]
pub struct CpuTemp {
pub icon: String,
pub enabled: bool,
pub delay: f64,
}
/* TODOS
TODO 1: Error handling required if rsblocks.yml file is empty.
TODO 2: This lib file growing and soon it will be annoying to move
arround, need fix soon.
TODO 3: Need a better comments in code, or no one will understand what happens.
TODO 4: Need a documentation.
TODO 5: Fix repeated code for threads in `run` function.
*/
/*this function is responsible to check if the rsblocks.yml file
exists to call parse_config to read it OTHERWISE
it will call load_defaults to load a hardcoded default configuration
it will always return a valid configuration inside a Result.
*/
pub fn load_config() -> Result<Config, Error> { pub fn load_config() -> Result<Config, Error> {
let yml_source = env::var("HOME").unwrap() + "/.config/rsblocks/rsblocks.yml"; let yml_source = std::env::var("HOME").unwrap() + "/.config/rsblocks/rsblocks.yml";
let mut data = String::new(); let mut data = String::new();
let mut file = match File::open(&yml_source) { let mut file = match File::open(&yml_source) {
Ok(file) => file, Ok(file) => file,
@ -64,12 +117,14 @@ pub fn load_config() -> Result<Config, Error> {
} }
}; };
file.read_to_string(&mut data)?; file.read_to_string(&mut data)?;
let yml_content = &YamlLoader::load_from_str(&data).unwrap()[0]; let yml_content = &YamlLoader::load_from_str(&data).unwrap()[0];
let config = parse_config(yml_content); let config = parse_config(yml_content);
Ok(config) Ok(config)
} }
/*
this is simply returns a hardcoded configuration as default
*/
fn load_defaults() -> Config { fn load_defaults() -> Config {
Config { Config {
seperator: String::from("|"), seperator: String::from("|"),
@ -92,29 +147,72 @@ fn load_defaults() -> Config {
icon: String::from(""), icon: String::from(""),
enabled: false, enabled: false,
delay: 0.17, delay: 0.17,
card: String::from("ALSA"),
},
weather: Weather {
city: String::from(""),
format: String::from("+%t"),
icon: String::from(""),
enabled: false,
delay: 7200.0, //7200 seconds = 2 hours
},
battery: Battery {
source: String::from("BAT0"),
icon: String::from(""),
enabled: false,
delay: 120.0,
},
cpu_temperature: CpuTemp {
icon: String::from(""),
enabled: false,
delay: 120.0,
}, },
} }
} }
/*
it will read and parse the rsblocks.yml file content and return a valid configuration
IF some content is missing in the rsblocks.yml file, it will set
a default values to that
*/
fn parse_config(doc: &yaml::Yaml) -> Config { fn parse_config(doc: &yaml::Yaml) -> Config {
// parsing icons and set default if not exist in the config file // parsing icons and set default if not exist in the config file
let seperator = get_or_set_string(doc, "general", "seperator", "|"); let seperator = get_or_set_string(doc, "general", "seperator", "|");
let time_icon = get_or_set_string(doc, "time", "icon", ""); let time_icon = get_or_set_string(doc, "time", "icon", "");
let time_format = get_or_set_string(doc, "time", "format", "%T");
let mem_icon = get_or_set_string(doc, "memory", "icon", ""); let mem_icon = get_or_set_string(doc, "memory", "icon", "");
let disk_icon = get_or_set_string(doc, "disk", "icon", ""); let disk_icon = get_or_set_string(doc, "disk", "icon", "");
let volume_icon = get_or_set_string(doc, "volume", "icon", ""); let volume_icon = get_or_set_string(doc, "volume", "icon", "");
let weather_icon = get_or_set_string(doc, "weather", "icon", "");
let battery_icon = get_or_set_string(doc, "battery", "icon", "");
let cpu_temperature_icon = get_or_set_string(doc, "cpu_temperature", "icon", "");
//parsing formats and city weather
let time_format = get_or_set_string(doc, "time", "format", "%T");
let weather_format = get_or_set_string(doc, "weather", "format", "%l:+%t");
let weather_city = get_or_set_string(doc, "weather", "city", "");
// parsing enabled state (everything false by default) // parsing enabled state (everything false by default)
let disk_enabled = get_or_set_bool(doc, "disk", "enable"); let disk_enabled = get_or_set_bool(doc, "disk", "enable");
let memory_enabled = get_or_set_bool(doc, "memory", "enable"); let memory_enabled = get_or_set_bool(doc, "memory", "enable");
let volume_enabled = get_or_set_bool(doc, "volume", "enable"); let volume_enabled = get_or_set_bool(doc, "volume", "enable");
let weather_enabled = get_or_set_bool(doc, "weather", "enable");
let battery_enabled = get_or_set_bool(doc, "battery", "enable");
let cpu_temperature_enabled = get_or_set_bool(doc, "cpu_temperature", "enable");
// parsing update_delay state (should be all seconds in f64 type) // parsing update_delay state (should be all seconds in f64 type)
let time_delay = get_or_set_f32(doc, "time", "delay", 1.0); let time_delay = get_or_set_f64(doc, "time", "delay", 1.0);
let disk_delay = get_or_set_f32(doc, "disk", "delay", 60.0); let disk_delay = get_or_set_f64(doc, "disk", "delay", 120.0);
let memory_delay = get_or_set_f32(doc, "memory", "delay", 2.0); let memory_delay = get_or_set_f64(doc, "memory", "delay", 2.0);
let volume_delay = get_or_set_f32(doc, "volume", "delay", 0.17); let volume_delay = get_or_set_f64(doc, "volume", "delay", 0.17);
let weather_delay = get_or_set_f64(doc, "weather", "delay", 7200.0);
let battery_delay = get_or_set_f64(doc, "battery", "delay", 120.0);
let cpu_temperature_delay = get_or_set_f64(doc, "cpu_temperature", "delay", 120.0);
// parsing card for volume, ALSA or PULSE
let volume_card = get_or_set_string(doc, "volume", "card", "ALSA");
// parsing battery source name
let battery_source = get_or_set_string(doc, "battery", "source", "BAT0");
Config { Config {
seperator, seperator,
@ -137,12 +235,31 @@ fn parse_config(doc: &yaml::Yaml) -> Config {
icon: volume_icon, icon: volume_icon,
enabled: volume_enabled, enabled: volume_enabled,
delay: volume_delay, delay: volume_delay,
card: volume_card,
},
weather: Weather {
city: weather_city,
format: weather_format,
icon: weather_icon,
enabled: weather_enabled,
delay: weather_delay,
},
battery: Battery {
source: battery_source,
icon: battery_icon,
enabled: battery_enabled,
delay: battery_delay,
},
cpu_temperature: CpuTemp {
icon: cpu_temperature_icon,
enabled: cpu_temperature_enabled,
delay: cpu_temperature_delay,
}, },
} }
} }
// getting a f32 value from rsblocks.yml file or set default (last argument) // getting a f32 value from rsblocks.yml file or set default (last argument)
fn get_or_set_f32(doc: &yaml::Yaml, parent: &str, child: &str, default: f64) -> f64 { fn get_or_set_f64(doc: &yaml::Yaml, parent: &str, child: &str, default: f64) -> f64 {
let val: f64 = if doc[parent][child].is_badvalue() { let val: f64 = if doc[parent][child].is_badvalue() {
default default
} else { } else {
@ -178,20 +295,30 @@ fn get_or_set_string(doc: &yaml::Yaml, parent: &str, child: &str, default_val: &
*/ */
/* Running the program: // Running the program:
TODO: this is sucks, repeated code in threads below, fix me you fucking asshole
*/
pub fn run(config: Config) { pub struct Blocks {
disp: Display<name::NameConnection>,
root: Window,
}
impl Blocks {
pub fn new() -> Self {
let disp = Display::create(None, None).expect("Failed to create x11 connection");
let root = disp.default_screen().root;
Self { disp, root }
}
}
pub fn run(config: Config, mut blocks: Blocks) {
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
// volume thread // volume thread
if config.volume.enabled { if config.volume.enabled {
let volume_tx = tx.clone(); let volume_tx = tx.clone();
let configcp = config.clone(); let configcp = config.clone();
let mut vol_data = ThreadsData::Sound(get_volume(&configcp)); let mut vol_data = ThreadsData::Sound(get_volume(&configcp));
thread::spawn(move || loop { thread::spawn(move || loop {
let _ = volume_tx.send(vol_data); volume_tx.send(vol_data).unwrap();
vol_data = ThreadsData::Sound(get_volume(&configcp)); vol_data = ThreadsData::Sound(get_volume(&configcp));
thread::sleep(Duration::from_secs_f64(configcp.volume.delay)) thread::sleep(Duration::from_secs_f64(configcp.volume.delay))
}); });
@ -222,6 +349,45 @@ pub fn run(config: Config) {
}); });
} }
// Weather thread
if config.weather.enabled {
let weather_tx = tx.clone();
let configcp = config.clone();
let weather_data = get_weather(&configcp);
let mut weather_data = ThreadsData::Weather(weather_data);
thread::spawn(move || loop {
weather_tx.send(weather_data).unwrap();
weather_data = ThreadsData::Weather(get_weather(&configcp));
thread::sleep(Duration::from_secs_f64(configcp.weather.delay))
});
}
// Battery thread
if config.battery.enabled {
let battery_tx = tx.clone();
let configcp = config.clone();
let battery_data = get_battery(&configcp).unwrap();
let mut battery_data = ThreadsData::Battery(battery_data);
thread::spawn(move || loop {
battery_tx.send(battery_data).unwrap();
battery_data = ThreadsData::Battery(get_battery(&configcp).unwrap());
thread::sleep(Duration::from_secs_f64(configcp.battery.delay))
});
}
// Cpu temperature thread
if config.cpu_temperature.enabled {
let cpu_temp_tx = tx.clone();
let configcp = config.clone();
let cpu_temp_data = get_cpu_temp(&configcp).unwrap();
let mut cpu_temp_data = ThreadsData::CpuTemp(cpu_temp_data);
thread::spawn(move || loop {
cpu_temp_tx.send(cpu_temp_data).unwrap();
cpu_temp_data = ThreadsData::CpuTemp(get_cpu_temp(&configcp).unwrap());
thread::sleep(Duration::from_secs_f64(configcp.cpu_temperature.delay))
});
}
// Time thread // Time thread
{ {
let time_tx = tx; let time_tx = tx;
@ -237,34 +403,36 @@ pub fn run(config: Config) {
//Main //Main
{ {
// NOTE: order matters to the final format // NOTE: order matters to the final format
let mut bar: Vec<String> = vec!["".to_string(); 7];
let mut bar: Vec<String> = vec!["".to_string(); 4];
//iterating the values recieved from the threads //iterating the values recieved from the threads
for data in rx { for data in rx {
match data { match data {
ThreadsData::Sound(x) => bar[0] = x, ThreadsData::Sound(x) => bar[0] = x,
ThreadsData::Disk(x) => bar[1] = x, ThreadsData::Weather(x) => bar[1] = x,
ThreadsData::Memory(x) => bar[2] = x, ThreadsData::Disk(x) => bar[2] = x,
ThreadsData::Time(x) => bar[3] = x, ThreadsData::Memory(x) => bar[3] = x,
ThreadsData::CpuTemp(x) => bar[4] = x,
ThreadsData::Battery(x) => bar[5] = x,
ThreadsData::Time(x) => bar[6] = x,
} }
// match ends here // match ends here
update(&bar); update(&bar, &mut blocks);
} }
} }
} }
pub fn update(bar: &Vec<String>) { pub fn update(bar: &Vec<String>, blocks: &mut Blocks) {
// TODO: FIX ME, this solution sucks // TODO: FIX ME, this solution sucks
let mut x = String::new(); let mut x = String::new();
for i in bar.iter() { for i in bar.iter() {
x.push_str(i.as_str()); x.push_str(i.as_str());
} }
Command::new("xsetroot")
.arg("-name") blocks
.arg(x) .root
.output() .set_title(&mut blocks.disp, &x)
.unwrap(); .expect("Failed to set title of root");
} }
/*############################ RUNNING THE PROGRAM ENDS HERE ###########################################*/ /*############################ RUNNING THE PROGRAM ENDS HERE ###########################################*/
@ -276,6 +444,7 @@ pub fn update(bar: &Vec<String>) {
############################# HELPER FUNCTIONS BELOW ################################### ############################# HELPER FUNCTIONS BELOW ###################################
*/ */
pub fn get_time(config: &Config) -> String { pub fn get_time(config: &Config) -> String {
let now = Local::now(); let now = Local::now();
@ -287,50 +456,80 @@ pub fn get_time(config: &Config) -> String {
) )
} }
/*
CREDIT: thanks for wttr.in to use their API
will make a GET request from wttr.in
*/
fn get_weather(config: &Config) -> String {
let format = if config.weather.format.is_empty() {
String::from("%l:+%t")
} else {
config.weather.format.clone()
};
let url = format!("http://wttr.in/{}?format=\"{}", config.weather.city, format);
let err_string = String::from("Error");
let res = match minreq::get(url).send() {
Ok(resp) => match resp.as_str() {
Ok(res_str) => res_str.trim_matches('"').to_string(),
Err(_) => err_string,
},
Err(_) => err_string,
};
format!(" {} {} {}", config.weather.icon, res, config.seperator)
}
// getting disk usage
pub fn get_disk(config: &Config) -> String { pub fn get_disk(config: &Config) -> String {
let cmd = Command::new("sh") const GB: u64 = (1024 * 1024) * 1024;
.arg("-c") let statvfs = nix::sys::statvfs::statvfs("/").unwrap();
.args(&["df -h"])
.output()
.unwrap();
let output = String::from_utf8_lossy(&cmd.stdout);
let mut disk_used = String::new(); let mut disk_used = String::new();
for line in output.lines() {
if line.ends_with('/') { let total = (statvfs.blocks() * statvfs.fragment_size()) / GB;
let splited = line.split_whitespace().collect::<Vec<&str>>(); let available = (statvfs.blocks_free() * statvfs.fragment_size()) / GB;
disk_used = splited[2].to_string(); let used = total - available;
break;
} disk_used.push_str(&format!("{}G", used));
}
format!( format!(
" {} {} {}", " {} {} {}",
config.disk.icon, disk_used, config.seperator config.disk.icon, disk_used, config.seperator
) )
} }
// TODO: what a horrible solution to get the sound, i dont like it
// find another way
pub fn get_volume(config: &Config) -> String { pub fn get_volume(config: &Config) -> String {
let cmd_content = Command::new("amixer") let card = if config.volume.card == "PULSE" {
.args(&["-D", "pulse", "get", "Master"]) "pulse"
.output() } else {
.expect("Make sure that you have alsa-utils installed on your system"); "default"
};
let vol: String = String::from_utf8_lossy(&cmd_content.stdout) let mixer = Mixer::new(card, false).expect("Failed to open mixer");
.lines() let selem_id = SelemId::new("Master", 0);
.last() let selem = mixer.find_selem(&selem_id).expect("Couldn't find selem");
.expect("failed to get sound volume") let selem_chan_id = SelemChannelId::FrontLeft;
.split('[')
.collect::<Vec<&str>>()[1]
.split(']')
.collect::<Vec<&str>>()[0]
.trim()
.to_string();
format!(" {} {} {}", config.volume.icon, vol, config.seperator) let (min, max) = selem.get_playback_volume_range();
let mut raw_volume = selem
.get_playback_volume(selem_chan_id)
.expect("Failed to get raw_volume");
let range = max - min;
let vol = if range == 0 {
0
} else {
raw_volume -= min;
((raw_volume as f64 / range as f64) * 100.) as u64
};
format!(" {} {}% {}", config.volume.icon, vol, config.seperator)
} }
/*
mem_used = (mem_total + shmem - mem_free - mem_buffers - mem_cached - mem_srecl
thanks for htop's developer on stackoverflow for providing this algorithm to
calculate used memory.
*/
pub fn get_memory(config: &Config) -> Result<String, std::io::Error> { pub fn get_memory(config: &Config) -> Result<String, std::io::Error> {
let mut buf = String::new(); let mut buf = String::new();
@ -392,7 +591,12 @@ pub fn get_memory(config: &Config) -> Result<String, std::io::Error> {
} }
Ok(result) Ok(result)
} }
// helper function for the get_memory function
/*
this helper function will split the line(first argument) by the character(:)
and then parse the right splited item as u32
then assign that to the "assignable"(2nd argument).
*/
fn assign_val(line: &str, assignable: &mut u32) { fn assign_val(line: &str, assignable: &mut u32) {
let parsed: u32 = line.split(':').collect::<Vec<&str>>()[1] let parsed: u32 = line.split(':').collect::<Vec<&str>>()[1]
.trim() .trim()
@ -402,3 +606,52 @@ fn assign_val(line: &str, assignable: &mut u32) {
.unwrap(); .unwrap();
*assignable = parsed; *assignable = parsed;
} }
// getting battery percentage
pub fn get_battery(config: &Config) -> Result<String, Error> {
let battery_full_cap_file = format!(
"/sys/class/power_supply/{}/charge_full_design",
config.battery.source
);
let battery_charge_now_file = format!(
"/sys/class/power_supply/{}/charge_now",
config.battery.source
);
let mut buf = String::new();
// FIXME: ugly error handling AGAIN fixing later, im lazy
match File::open(&battery_full_cap_file) {
Ok(mut file) => file.read_to_string(&mut buf)?,
Err(_) => return Ok(String::from("check your battery source name")),
};
let full_design = buf.trim().parse::<u32>().unwrap();
buf.clear();
// NOTE: no need to error check if passed the above match
File::open(&battery_charge_now_file)?.read_to_string(&mut buf)?;
let charge_now = buf.trim().parse::<u32>().unwrap();
let battery_percentage = (charge_now as f32 / full_design as f32) * 100.0;
let result = format!(
" {} {:.0}% {}",
config.battery.icon, battery_percentage, config.seperator
);
Ok(result)
}
// getting cpu temperature
pub fn get_cpu_temp(config: &Config) -> Result<String, std::io::Error> {
let mut buf = String::new();
File::open("/sys/class/thermal/thermal_zone0/temp")?.read_to_string(&mut buf)?;
let value = buf.trim().parse::<f32>().unwrap();
let result = format!(
" {} {}° {}",
config.cpu_temperature.icon,
value / 1000.0,
config.seperator
);
Ok(result)
}

View File

@ -2,5 +2,6 @@ use rsblocks;
fn main() { fn main() {
let config = rsblocks::load_config().unwrap(); let config = rsblocks::load_config().unwrap();
rsblocks::run(config); let blocks = rsblocks::Blocks::new();
rsblocks::run(config, blocks);
} }