Writing Your Own Blocklet

Grant Pauker edited this page Apr 9, 2018 · 3 revisions

Welcome. This guide is here to help you on the road to writing your very first blocklet. We will be covering many of the features of i3blocks and showing Bourn shell example code.

To begin, let us see what i3blocks is. i3blocks is a codec (coder-decoder) that can convert to and from the i3bar protocol. The i3bar protocol is the ultimate authority on what can be done. If i3bar does not have a parameter to support some desired feature, it cannot be done in i3blocks. i3blocks simply makes it easier to use multiple programs (blocklets) simultaneously to send data to i3bar through the i3bar protocol. i3blocks receives data from programs by way of the STDOUT file descriptor and then converts this to the JSON based i3bar protocol. Any returning i3bar events (e.g. mouse clicks) are then stored in environment variables to be passed to the executed program. This is to say, your blocklet will receive i3blocks inputs from environment variables and produce output through STDOUT.

Config

To begin, let's look at the config of any blocklet to discuss what your program will deal with when called.

[script]
#label=
command=script_name.ext -f -l -s
interval=30
#signal=
#full_text=
#short_text=
#color=
#min_width=
#align=
#name=
#instance=
#urgent=
#separator=
#separator_block_width=
#markup=
#format=

Above is the full config as of version 1.4 that may be defined for any blocklet. The first area is the blocklet name [script]. The blocklet name is usually irrelevant. However, this name is stored in the $BLOCK_NAME variable when the program is executed. Some people choose to use this to select the binary they will run under the command= option by setting command=/path/to/i3block/blocklets/$BLOCK_NAME. This is an interesting use but not perfect. The reason is that this section heading MUST be unique within the config. This means multiple instances of a blocklet (e.g. for multiple network interfaces) is not possible.

The first option label= is not important to a blocklet developer. The label is a string that is prepended to the output of the blocklet for the purpose of a label. This is user specified and should be left up to the user to choose.

The next option is the command= option. This is the path to your blocklet plus any additional command arguments. This is effectively how your blocklet will be run.

The next option is what i3blocks will use to time the calls of your blocklet. This is something you should think about when creating your blocklet. The first possible setting is to use a number to represent the number of seconds between each execution of your script. Note that subsecond execution is not possible given this method. The next setting is the 'once' setting. This setting is most useful for blocklets in one of three categories. The first and most obvious is a blocklet that never changes. The second is a script that updates at such a large interval that a manual update is most useful. The third ties into the signal= option. When using the signal= option, it is usually preferable to not have both a signal to update the blocklet and a fixed time. Using the 'once' setting in the case is usually desirous. The next possible setting is the 'repeat' setting. This is the exact opposite of the 'once' setting. 'repeat' will indefinitely run the command right after it finishes executing (a while true loop). This setting should be avoided unless your blocklet implements its own waiting mechanism. The last setting is the 'persist' setting. This setting is for blocklets that are daemons. The blocklet's STDOUT is pulled in realtime and pushed to i3bar. This is a rather advanced use but is still a tool available to you.

The next option is the signal= option. This option selects one of the 32 UNIX process signals that i3blocks should trap to instead execute the blocklet. This option allows your blocklet to be updated when you run pkill -RTMIN+$YOUR_SIGNAL i3blocks. This command sends $YOUR_SIGNAL to i3blocks and will update all blocklets with that signal set. It is important to note that by convention, only SIGUSR1(#10) and SIGUSR2(#12) are free of POSIX assigned use. However, you can select any signal you desire, nothing stops you.

The last field format= is the last of i3blocks' non-i3bar options. This option allows your blocklet to directly output i3bar protocol for your blocklet. By setting this to 'json' you can directly produce the JSON i3bar block data that will be sent to i3bar. Unless your program wants to do this, you will not need to worry about this field.

Every other field is a field of the i3bar protocol. You can set any of them directly. However, your blocklet can also set these. Each line of text (\n ended) will override each option in order. Obviously, full_text= is first as this would be a simple single line output. Next is short_text= which is what i3bar will use to try to shorten the bar when there is not enough screen available to use full_text=. Next, color=. This is a setting that users do not enjoy programs overwriting. This is because it forces a color choice on the user. The preferred method of including color within a program is through pango anyways (discussed later) as it provides fine grain control. min_width= and align= are pretty self explanatory. name= is used to overwrite the name which will be pulled from [script] otherwise.

Back to more important and blocklet programming related options. The instance= option is used to send a string to the blocklet program in the $BLOCK_INSTANCE variable. This option should be used for selecting a major target of the blocklet. For example, using this to select a networking interface or block device would make sense. This should be the only thing a user needs to change when they would run multiple instances of your blocklet.

Next up is urgent=. This option has two options, true and false. This is entirely handled by i3bar. As of the time of writing, i3bar will color the background of the blocklet with the window urgancy color when true. However, this option is almost never set through the option. Instead, i3blocks will set this option to true if you blocklet exits with code 33. If your blocklet returns with any code but 0 or 33, i3blocks considers your blocklet to have failed. i3blocks will still display the first line of the output in STDOUT however.

The next option is usually something best left up to the user. separator= is a true/false option that chooses if i3bar will display a separator after the block. Also, separator_block_width= just specifies the space allocated for the separator. These are always best left up to the user.

Finally, another useful option, markup=. This option will likely be set by the user but will be critical to your code. This is because setting this option to 'pango' allows a form of sudo-HTML markup called Pango. Pango is a GNOME library for rendering this sudo-HTML into font outputs. To see what tags may be used, see the Pango docs.

Environment Variables

i3blocks uses environment variables to pass your script information. The first two, $BLOCK_NAME and $BLOCK_INSTANCE have already been discussed in the above section.

The next 3 environment variables are used to return mouse events from i3bar. When a user clicks your block, your script is run again with the following environment variables. The first is $BLOCK_BUTTON which will be an integer indicating the button. These are standard for human interface devices under evdev (1 = Left, 2 = Middle, 3 = Right, 4 = Scroll Up, 5 = Scroll Down, 6 = Custom, 7 = Custom, 8 = Upper Thumb, 9 = Lower Thumb, 10+ = Custom). The other two environment variables, $BLOCK_X and $BLOCK_Y will relay the coordinates where the click occurred. This is useful for creating buttons but requires some jiggling. The X and Y values you receive is relative to the top left of the screen, not within the block output. This means the block is tied to the system's screen plus both i3bar and i3blocks configs. Without lots of calculation to determine the block's location on the screen, this feature is only usable in personal scripts.

Examples

Hello World

To begin, we need to setup a config. Use the following to start:

[my_blocklet]
label=My Script:
command=$HOME/my_blocklet
interval=30
#signal=
#instance=
#markup=
#format=

This will let us play with many features. Now, create a file in your home directory called my_blocklet and give it execute permissions. Good. Begin by typing the following:

#!/bin/bash

echo 'Hello World!'

Output:

Finally, we must restart i3 to see your changes. To do this, execute i3-msg restart. Did you get your block to show? If not, double check everything. Is your script executable and named correctly? Do you have Bash installed? Is i3's i3bar section calling i3blocks? If you still have problems here, feel free to open an issue so we can help you further. Seriously though, you shouldn't have a problem.

Pango

So, let's edit my_blocklet to play with some pango. First, change your config #markup= to markup=pango. Try the following:

#!/bin/bash

echo '<span foreground="#FF0000">[<i>Testing</i>]</span><sup>+1</sup>'

Output:

This shows you some of the fun you can begin to have. With Pango, many really fun formatting tricks are possible.

My External IP

Now, let's do something that isn't trivial. We will be writing a blocklet that allows us to get our external IP address. We will also include functionality to allow us to copy it to the clipboard on right click. Use the following:

#!/bin/bash

IP=$(curl ifconfig.me/ip)

case $BLOCK_BUTTON in
    3) echo "$IP" | xclip ;;
    *) echo "$IP" ;;
esac

Output:

This script introduces the $BLOCK_BUTTON variable. This variable stores the button used when a click event is sent to i3bar. This script also requires xclip. If you do not have xclip installed, do so before testing this script. The script works by using curl to poll a cool website called http://ifconfig.me/ that will return just your external IP address in plain text. The case section will copy it on right click (mouse button 3) and just print the IP on any other execution.

mpd

Many people love using mpd to listen to their music collection. If you are one of these people, you are going to love the following. This example provides an example that simply builds on what we have learned so far. Type out the following:

#!/bin/bash

# Pass the password in the block instance
if [[ -n $BLOCK_INSTANCE ]]; then
    password=("-h" "$BLOCK_INSTANCE@localhost")
fi

filter() {
    echo -n '['
    tr '\n' ' ' | grep -Po '.*(?= \[playing\])|paused' | tr -d '\n'
    echo -n ']'
}

case $BLOCK_BUTTON in
    3) mpc $password toggle | filter ;;  # right click, pause/unpause
    4) mpc $password prev   | filter ;;  # scroll up, previous
    5) mpc $password next   | filter ;;  # scroll down, next
    *) mpc $password status | filter ;;
esac

Output:

This is the first nontrivial example. Here you have the general blocklet setup. First, you handle your inputs. This script uses the instance variable to pass an mdp password. If the instance variable is set, it will build the -h option to pass the password. Next, a filter is built. Here, the filter will pull the name of the playing song if it is playing or the work paused if mpd is paused. Finally, there is a mouse interactive output. This script will handle right clicks (3), scroll up (4), and scroll down (5).

Further examples welcomed.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.