Very resourcefriendly and feature-rich replacement for i3status, written in pure Rust
This is a WiP replacement for i3status, aiming to provide the most feature-complete and resource friendly implementation of the i3bar protocol available. We are currently looking for help in implementing more Blocks. It supports:
- flexibility through theming
- icons (optional)
- individual update intervals per block to reduce system calls
- click actions
- blocks can trigger updates asynchronously, which allows for things like dbus signaling, to avoid periodic refreshing of data that rarely changes (example: music block)
i3, rustc and cargo. Only tested on Arch Linux. If you want to use the font icons on Arch, install ttf-font-awesome from the AUR.
- Clone the repository:
git clone https://github.com/XYunknown/i3status-rust.git - run
cd i3status-rust && cargo build --release - Edit example_config.json to your liking and put it to a sensible place (e.g. ~/.config/i3/status.json)
- Edit your i3 config
-
In your i3 config, put the path to the output binary as argument for 'status_command'
-
Add the path to your config file as first argument, you can also configure theme and icon theme as arguments to i3status-rs. See i3status-rs --help for more.
Example of the 'bar' section in the i3 config from my personal i3 config (Requires awesome-ttf-fonts). The colors block is optional, just my taste:
bar { font pango:DejaVu Sans Mono, Icons 12 position top status_command <PATH_TO_i3STATUS>/i3status-rs <PATH_TO_CONFIG>/config.json --icons awesome --theme solarized-dark colors { separator #666666 background #222222 statusline #dddddd focused_workspace #0088CC #0088CC #ffffff active_workspace #333333 #333333 #ffffff inactive_workspace #333333 #333333 #888888 urgent_workspace #2f343a #900000 #ffffff } }
-
- Reload i3:
i3 reload
Creates a block which display the current time.
Example
{"block": "time", "interval": 60, "format": "%a %d/%m %R"},Options
| Key | Values | Required | Default |
|---|---|---|---|
| format | Format string. See chrono docs for all options. |
No | %a %d/%m %R |
| interval | Update interval in seconds | No | 5 |
Creates a block displaying memory and swap usage.
By default, the format of this module is ": {MFm}MB/{MTm}MB({Mp}%)" (Swap values accordingly). That behaviour can be changed within config.json.
This module keeps track of both Swap and Memory. By default, a click switches between them.
Example
{"block": "memory",
"format_mem": "{Mum}MB/{MTm}MB({Mup}%)", "format_swap": "{SUm}MB/{STm}MB({SUp}%)",
"type": "memory", "icons": "true", "clickable": "true", "interval": "5",
"warning_mem": 80, "warning_swap": 80, "critical_mem": 95, "critical_swap": 95
},Options
| Key | Values | Required | Default |
|---|---|---|---|
| format_mem | Format string for Memory view. All format values are described below. | No | {MFm}MB/{MTm}MB({Mp}%) |
| format_swap | Format string for Swap view. | No | {SFm}MB/{STm}MB({Sp}%) |
| type | Default view displayed on startup. Options are memory, swap |
No | memory |
| icons | Whether the format string should be prepended with Icons. Options are true, false |
No | true |
| clickable | Whether the view should switch between memory and swap on click. Options are true, false |
No | true |
| interval | The delay in seconds between an update. If clickable, an update is triggered on click. Integer values only. |
No | 5 |
| warning_mem | Percentage of memory usage, where state is set to warning | No | 80.0 |
| warning_swap | Percentage of swap usage, where state is set to warning | No | 80.0 |
| critical_mem | Percentage of memory usage, where state is set to critical | No | 95.0 |
| critical_swap | Percentage of swap usage, where state is set to critical | No | 95.0 |
| Key | Value |
|---|---|
| {MTg} | Memory total (GiB) |
| {MTm} | Memory total (MiB) |
| {MAg} | Available emory, including cached memory and buffers (GiB) |
| {MAm} | Available memory, including cached memory and buffers (MiB) |
| {MAp} | Available memory, including cached memory and buffers (%) |
| {MFg} | Memory free (GiB) |
| {MFm} | Memory free (MiB) |
| {MFp} | Memory free (%) |
| {Mug} | Memory used, excluding cached memory and buffers; similar to htop's green bar (GiB) |
| {Mum} | Memory used, excluding cached memory and buffers; similar to htop's green bar (MiB) |
| {Mup} | Memory used, excluding cached memory and buffers; similar to htop's green bar (%) |
| {MUg} | Total memory used (GiB) |
| {MUm} | Total memory used (MiB) |
| {MUp} | Total memory used (%) |
| {Cg} | Cached memory, similar to htop's yellow bar (GiB) |
| {Cm} | Cached memory, similar to htop's yellow bar (MiB) |
| {Cp} | Cached memory, similar to htop's yellow bar (%) |
| {Bg} | Buffers, similar to htop's blue bar (GiB) |
| {Bm} | Buffers, similar to htop's blue bar (MiB) |
| {Bp} | Buffers, similar to htop's blue bar (%) |
| {STg} | Swap total (GiB) |
| {STm} | Swap total (MiB) |
| {SFg} | Swap free (GiB) |
| {SFm} | Swap free (MiB) |
| {SFp} | Swap free (%) |
| {SUg} | Swap used (GiB) |
| {SUm} | Swap used (MiB) |
| {SUp} | Swap used (%) |
Creates a block which can display the current song title and artist, in a fixed width marquee fashion. It uses dbus signaling to fetch new tracks, so no periodic updates are needed. It supports all Players that implement the MediaPlayer2 Interface. This includes spotify, vlc and many more. Also provides buttons for play/pause, previous and next title.
Example
{"block": "music", "player": "spotify", "buttons": ["play", "next"]},Options
| Key | Values | Required | Default |
|---|---|---|---|
| player | Name of the music player.Must be the same name the player is registered with the MediaPlayer2 Interface. |
Yes | - |
| max_width | Max width of the block in characters, not including the buttons | No | 21 |
| marquee | Bool to specify if a marquee style rotation should be used every 10s if the title + artist is longer than max-width |
No | true |
| buttons | Array of control buttons to be displayed. Options are prev (previous title), play (play/pause) and next (next title) |
No | [] |
Creates a block which displays the system load average.
Example
{"block": "load", "format": "{1m} {5m}", "interval": 1},Options
| Key | Values | Required | Default |
|---|---|---|---|
| format | Format string. You can use the placeholders 1m 5m and 15m, eg "1min avg: {1m}" |
No | {1m} |
| interval | Update interval in seconds | No | 3 |
Creates a block which displays the overall CPU utilization, calculated from /proc/stat.
Example
{"block": "cpu", "interval": 1},Options
| Key | Values | Required | Default |
|---|---|---|---|
| interval | Update interval in seconds | No | 1 |
Creates a block which displays the current battery state (Full, Charging or Discharging) and percentage charged.
Example
{"block": "battery", "interval": 10},Options
| Key | Values | Required | Default |
|---|---|---|---|
| interval | Update interval in seconds | No | 10 |
| device | Which BAT device in /sys/class/power_supply/ to read from. | No | 0 |
Creates a block that display the output of custom commands
Example
{"block": "custom", "interval": 100, "command": "uname"}{"block": "custom", "interval": 1, "cycle": ["echo ON", "echo OFF"], "on_click": "<command>"}Note that content and cycle are mutually exclusive.
| Key | Values | Required | Default |
|---|---|---|---|
| interval | Update interval in seconds | No | 10 |
| command | Shell Command to execute & display | No | None |
| on_click | Command to execute when the button is clicked | No | None |
| cycle | Commands to execute and change when the button is clicked | No | None |
Creates a toggle block. You can add commands to be executed to disable the toggle (command_off), and to enable it (command_on).
You also need to specify a command to determine the (initial) state of the toggle (command_state). When the command outputs nothing, the toggle is disabled, otherwise enabled.
By specifying the interval property you can let the command_state be executed continuously.
Example This is what I use to toggle my external monitor configuration:
{"block": "toggle",
"text": "4k",
"command_state": "xrandr | grep DP1\\ connected\\ 38 | grep -v eDP1",
"command_on": "~/.screenlayout/4kmon_default.sh",
"command_off": "~/.screenlayout/builtin.sh",
"interval": 5}| Key | Values | Required | Default |
|---|---|---|---|
| interval | Update interval in seconds | No | Never |
| command_on | Shell Command to enable the toggle | Yes | None |
| command_off | Shell Command to disable the toggle | Yes | None |
| command_state | Shell Command to determine toggle state. Empty output => off. Any output => on. |
Yes | None |
Creates a block which displays the pending updates available on pacman.
Example
{"block": "pacman", "interval": 10},Options
| Key | Values | Required | Default |
|---|---|---|---|
| interval | Update interval in seconds | No | 600 (10min) |
Creates a block which displays disk space information.
Example
{"block": "disk_space", "path": "/", "alias": "/", "type": "available", "unit": "GB", "interval": 20},Options
| Key | Values | Required | Default |
|---|---|---|---|
| path | Path to collect information from | No | / |
| alias | Alias that is displayed for path | No | / |
| type | Currently supported options are available and free | No | available |
| unit | Unit that is used to display disk space. Options are MB, MiB, GB and GiB | No | GB |
| interval | Update interval in seconds | No | 20 |
Creates a block which displays the current Master volume (currently based on amixer output). Click to toggle mute.
Example
{"block": "sound", "interval": 10},Options
| Key | Values | Required | Default |
|---|---|---|---|
| interval | Update interval in seconds | No | 2 |
Creates a block which displays the system temperature, based on lm_sensors' sensors output. The block is collapsed by default, and can be expanded by clicking, showing max and avg temperature. When collapsed, the color of the temperature block gives a quick indication as to the temperature (Critical when maxtemp > 80°, Warning when > 60°). Currently, you can only adjust these thresholds in source code. Depends on lm_sensors being installed and configured!
Example
{"block": "temperature", "interval": 10, "collapsed": false},Options
| Key | Values | Required | Default |
|---|---|---|---|
| interval | Update interval in seconds | No | 5 |
| collapsed | Collapsed by default? | No | true |
Create a block by copying the template: cp src/blocks/template.rs src/blocks/<block_name>.rs Obviously, you have to be in the main repo directory and replace <block_name> with the name of your block.
Your block needs a struct to store it's state. First, replace all the occurences of 'Template' in the file with the name of your block. Then edit the struct and add all Fields which you may need to store either options from the block config or state values (e.g. free disk space or current load). Use Widgets to display something in the i3Bar, you can have multiple Text or Button widgets on a Block. These have to be returned in the view() function and they need to be updated from the update() function. They also handle icons and theming for you.
You now need to write a constructor (new()) to create your Block from a piece of JSON (from the config file section of your block). Access values from the config here with config["name"], then use .as_str() or as_u64() to convert the argument to the right type, and unwrap it with expect() or unwrap_or() to give it a default value. Alternatively, you can use the helper macros get_str/u64/bool to extract a string/ u64 and add appropriate error handeling. You can set a default value in the macro as you can see below. The template shows you how to instantiate a simple Text widget. For more info on how to use widgets, just look into other Blocks. More documentation to come. The sender object can be used to send asynchronous update request for any block from a separate thread, provide you know the Block's ID.This advanced feature can be used to reduce the number of system calls by asynchrounosly waiting for events. A usage example can be found in the Music block, which updates only when dbus signals a new song.
Example:
pub fn new(config: Value, tx: Sender<Task>, theme: Value) -> Template {
let text = TextWidget::new(theme.clone()).with_text("I'm a Template!");
Template {
id: Uuid::new_v4().simple().to_string(),
update_interval: Duration::new(get_u64_default!(config, "interval", 5), 0),
text: text,
tx_update_request: tx,
theme: theme,
}
}All blocks are basically structs which implement the trait (interface) Block. This interface defines the following features:
Use this function to update the internal state of your block, for example during periodic updates. Return the duration until your block wants to be updated next. For example, a clock could request only to be updated every 60 seconds by returning Some(Duration::new(60, 0)) every time. If you return None, this function will not be called again automatically.
Example:
fn update(&mut self) -> Option<Duration> {
self.time.set_text(format!("{}", Local::now().format(&self.format)));
Some(self.update_interval.clone())
}Use this function to return the widgets that comprise the UI of your component. The music block may, for example, be comprised of a text widget and multiple buttons. Use a vec to wrap the references to your view.
Example:
fn view(&self) -> Vec<&I3BarWidget> {
vec![&self.time]
}You need to return a unique identifier for your block here. In the template you will already find a UUID implementation being used here. This is needed, for example, to send update requests (callbacks) from a different thread.
Example:
fn id(&self) -> &str {
&self.id
}Here you can react to the user clicking your block. The i3barEvent instance contains all fields to describe the click action, including mouse button and location down to the pixel. You may also update the internal state here. Note that this event is sent to every block on every click. To filter, use the event.name property, which corresponds to the name property on widgets!
Example:
if event.name.is_some() {
let action = match &event.name.clone().unwrap() as &str {
"play" => "PlayPause",
"next" => "Next",
"prev" => "Previous",
_ => ""
};
}
}Edit src/blocks/mod.rs and add:
- A module export line:
pub mod <name>; - A use directive:
use self::<name>::*; - Mapping to a name string:
"<name>" => boxed!(<name>::new(config)),
Congratulations You're done. Recompile and just add the block to your config file now.