Search This Blog

Tuesday, October 27, 2015

My precious... (Part 1)

So I've been meaning to make (semi) detailed notes of my Arch Linux config files for a while now, in the hope that these notes will help me set up my Arch quickly if I ever have to do so from scratch. This is how my Arch currently looks. If you like what you see, read ahead!

Home sweet home

Work in progress

The WM and related scripts:

Okay, so first things first, no DEs here (in the interest of being lightweight and productive). Right now I'm running i3wm with small (and dirty) custom scripts to add some common DE-like functionality that I find useful. So what all am I referring to?
  • A modified i3bar that shows me the information that I require and of course looks good too. There are some scripts in here to fetch and display the information that I require, but I'll get to that in a bit. 
  • Some custom i3 keyboard bindings to speed common tasks. 
  • Labelling of workspaces to organize my windows.
  • A wallpaper changer to shuffle and display my massive wallpaper collection.
  • A notification daemon and scripts to display some useful notifications.
  • A modification to dmenu to detach launched processes from their parent shells.
  • A good command line file manager.
So let me get right to it!

First up, modifying i3bar to make it prettier and to extend its functionality. First up, the modifications to the bar settings in the i3config file:

bar {
  mode dock
  font pango:DejaVuSansCondensed regular 6.5
  colors {
  # Show status text in light blue color
    #statusline #ADD8E6
    background #222222
    statusline #dddddd
    separator  #666666
    active_workspace   #333333 #333333 #888888
    focused_workspace  #0088CC #0088CC #ffffff
    inactive_workspace #333333 #333333 #888888
    urgent_workspace   #2f343a #900000 #ffffff
  }

  status_command conky -c ~/.conkyrc
  status_command /home/anant/.i3/i3bar-wrapper.sh
}
Nothing fancy here, some color settings, an appropriate font and size, and specifying dock mode. The last two lines are important. I am using conky 1.10.0 to add the colors and formatting. The first command tells conky to use the .conkyrc config file in my home folder. This is the file (most of the stuff in there is pretty easy to understand):

out_to_x no
out_to_console yes
short_units yes
update_interval 1
#use_spacer left
#pad_percents 2
if_up_strictness address

TEXT

[\
# Music:
{"full_text":"${if_match ${exec ~/.i3/testmusic.sh}>=1}♫","color":"\#ffffff","separator":false,"separator_block_width":5},\
{"full_text":"${exec ~/.i3/music.sh}","color":"\#EC3B83","separator":true,"separator_block_width":8},\
{"full_text":"${endif}"},\
# Mounted Devices:
{"full_text":"${if_match ${exec ~/.i3/hasmounted.sh}>=1} USB","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"${exec ~/.i3/getmounteddevices.sh} ","color":"\#F0DC64","separator":true,"separator_block_width":6},\
{"full_text":"${endif}"},\
# Disk Usage:
{"full_text":" 💾","color":"\#aaaaaa","separator":false,"separator_block_width":6},\
{"full_text":"${execi 3600 df -h --output=avail / | tail -n1 | sed -e 's/\s//g'} ","color":"\#FF9933","separator":true,"separator_block_width":6},\
# Internet:
{"full_text":"${if_up enp60s0}"},\
{"full_text":" LAN","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"${addr enp60s0} ","color":"\#00ff00","separator":true,"separator_block_width":9},\
{"full_text":"${endif}"},\
{"full_text":"${if_up br0}"},\
{"full_text":" BRIDGE","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"${addr br0} ","color":"\#00ff00","separator":true,"separator_block_width":9},\
{"full_text":"${endif}"},\
{"full_text":"${if_up enp0s20u1}"},\
{"full_text":" USB","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"${addr enp0s20u1} ","color":"\#00ff00","separator":true,"separator_block_width":9},\
{"full_text":"${endif}"},\
{"full_text":"${if_up wls1}"},\
{"full_text":" Wi-Fi","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"(${exec iwgetid -r})","color":"\#EC3B83","separator":false,"separator_block_width":6},\
{"full_text":"${addr wls1} ","color":"\#00ff00","separator":true,"separator_block_width":9},\
{"full_text":"${endif}"},\
{"full_text":"${if_up enp60s0}"},\
{"full_text":"${else}"},\
{"full_text":"${if_up enp0s20u1}"},\
{"full_text":"${else}"},\
{"full_text":"${if_up br0}"},\
{"full_text":"${else}"},\
{"full_text":"${if_up wls1}"},\
{"full_text":"${else}"},\
{"full_text":" No Internet ","color":"\#ff3300","separator":true,"separator_block_width":9},\
{"full_text":"${endif}${endif}${endif}${endif}"},\
# CPU temperature:
{"full_text":" CPU","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"${if_match ${hwmon 0 temp 1}<50}${hwmon 0 temp 1}","color":"\#AAF096","separator":false,"separator_block_width":0},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${hwmon 0 temp 1}<55}${hwmon 0 temp 1}","color":"\#F0DC64","separator":false,"separator_block_width":0},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${hwmon 0 temp 1}<60}${hwmon 0 temp 1}","color":"\#FF9933","separator":false,"separator_block_width":0},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${hwmon 0 temp 1}>=60}${hwmon 0 temp 1}","color":"\#FF3333","separator":false,"separator_block_width":0},\
{"full_text":"${else}"},\
{"full_text":"${endif}${endif}${endif}${endif}"},\
{"full_text":"°C ","color":"\#7FFFD4","separator":true,"separator_block_width":6},\
# GPU temperature:
{"full_text":" GPU","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"${execi 5 nvidia-settings -q gpucoretemp -t | head -n1}","color":"\#AAF096","separator":false,"separator_block_width":0},\
{"full_text":"°C ","color":"\#7FFFD4","separator":true,"separator_block_width":6},\
# Memory:
{"full_text":" MEM","color":"\#888888","separator":false,"separator_block_width":6},\
{"full_text":"${if_match ${memperc}<30}${mem} / ${memmax} ","color":"\#AAF096","separator":true,"separator_block_width":9},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${memperc}<60}${mem} / ${memmax} ","color":"\#F0DC64","separator":true,"separator_block_width":9},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${memperc}<85}${mem} / ${memmax} ","color":"\#FF9933","separator":true,"separator_block_width":9},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${memperc}>=85}${mem} / ${memmax} ","color":"\#FF3333","separator":true,"separator_block_width":9},\
{"full_text":"${endif}${endif}${endif}${endif}"},\
# CPU:
{"full_text":" 💻","color":"\#aaaaaa","separator":false,"separator_block_width":6},\
{"full_text":"${if_match ${cpu cpu0}<25}${cpu cpu0}","color":"\#AAF096","separator":false,"separator_block_width":0},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${cpu cpu0}<50}${cpu cpu0}","color":"\#F0DC64","separator":false,"separator_block_width":0},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${cpu cpu0}<75}${cpu cpu0}","color":"\#FF9933","separator":false,"separator_block_width":0},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${cpu cpu0}<=100}${cpu cpu0}","color":"\#FF3333","separator":false,"separator_block_width":0},\
{"full_text":"${endif}${endif}${endif}${endif}"},\
{"full_text":"% ","color":"\#EEEEEE","separator":true,"separator_block_width":8},\
# Battery:
{"full_text":"${if_match ${exec ~/.i3/batterystatus.sh}>=2} 🔌","color":"\#aaaaaa","separator":false,"separator_block_width":5},\
{"full_text":"ON ","color":"\#00ff00","separator":true,"separator_block_width":6},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${exec ~/.i3/batterystatus.sh}>=1} 🔌","color":"\#aaaaaa","separator":false,"separator_block_width":5},\
{"full_text":"${else}"},\
{"full_text":" 🔋","color":"\#aaaaaa","separator":false,"separator_block_width":7},\
{"full_text":"${endif}"},\
{"full_text":"${if_match ${battery_percent}>80}${battery_percent}% : ${battery_time} ","color":"\#AAF096","separator":true,"separator_block_width":6},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${battery_percent}>50}${battery_percent}% : ${battery_time} ","color":"\#F0DC64","separator":true,"separator_block_width":6},\
{"full_text":"${else}"},\
{"full_text":"${if_match ${battery_percent}>25}${battery_percent}% : ${battery_time} ","color":"\#FF9933","separator":true,"separator_block_width":6},\
{"full_text":"${else}"},\
{"full_text":"${battery_percent}% : ${battery_time} ","color":"\#FF3333","separator":true,"separator_block_width":6},\
{"full_text":"${endif}${endif}${endif}"},\
{"full_text":"${endif}"},\
# Backlight:
{"full_text":" 🔅","color":"\#FFFFFF","separator":false,"separator_block_width":6},\
{"full_text":"${exec xbacklight | cut -d '.' -f1}% ","color":"\#ff8080","separator":true,"separator_block_width":6},\
# Volume:
{"full_text":" 🔊","color":"\#AAAAAA","separator":false,"separator_block_width":7},\
{"full_text":"${exec ~/.i3/getvolume.sh} ","color":"\#ff8080","separator":true,"separator_block_width":8},\
# Date:
{"full_text":" 📅","color":"\#aaaaaa","separator":false,"separator_block_width":8},\
{"full_text":"${time %D} ${execi 3600 date | cut -d ' ' -f1 | awk '{print toupper($0)}'} ","color":"\#7FFFD4","separator":true,"separator_block_width":8},\
# Time:
{"full_text":" ⏰","color":"\#FFFFFF","separator":false,"separator_block_width":7},\
{"full_text":"${time %r}","color":"\#7FFFD4","separator":true}\
],
Before I get to the scripts mentioned in the conky config file, let me just touch on the last thing, the wrapper for i3bar. The last line in the bar part of the i3 config file points to a wrapper for i3bar that lets it display information from conky. This is the content of i3bar-wrapper.sh:

#!/bin/sh

echo '{"version":1}'
echo '['

echo '[],'

exec conky -c /home/$USER/.conkyrc
So the first line tells i3bar that we are using JSON, and the second line starts the endless array of inputs. Finally the last two lines are the loop part where an empty array of blocks and the information from conky are sent to i3bar.

Now for the scripts in the conkyrc file. All the commands/scripts are simple to understand and don't really need any explanation. The only script that probably warrants an explanation is music.sh. I use cmus as my music player so the script as-is will work only for if you use cmus too. However, with a couple of minor modifications, it should be possible to extend this to any music player that supports querying track information from the command line. The testmusic.sh script just tests if cmus is active using cmus-remote and returns 1 if it is active. The interesting script is music.sh:

#!/bin/sh

artist=$(cmus-remote -Q | grep ' artist ' | cut -d ' ' -f3- | sed -e 's/\&/and/g')
song=$(cmus-remote -Q | grep ' title ' | cut -d ' ' -f3- | sed -e 's/\&/and/g')

songlen=${#song}
artistlen=${#artist}

duration=$(cmus-remote -Q | grep duration | cut -d ' ' -f2-)
elapsed=$(cmus-remote -Q | grep position | cut -d ' ' -f2-)
dmin=$(($duration/60))
dsec=$(($duration%60))
if [ "$dsec" -lt "10" ]; then
  dsec="0$dsec"
fi
emin=$(($elapsed/60))
esec=$(($elapsed%60))
if [ "$esec" -lt "10" ]; then
  esec="0$esec"
fi

if [ "$songlen" -gt "20" ]; then
  modlen=$songlen
  offset=$(($elapsed % $modlen))
  if [ "$offset" -ge "$(($songlen-10))" ]; then
    song=$(echo $song | cut -c $(($songlen-19))-)
  elif [ "$offset" -gt "10" ]; then
    offset=$(($offset - 10))
    song=$(echo $song | cut -c $offset-$(($offset+19)))
  else
    song=$(echo $song | cut -c -20)
  fi
fi

if [ "$artistlen" -gt "15" ]; then
  modlen=$artistlen
  offset=$(($elapsed % $modlen))
  if [ "$offset" -ge "$(($artistlen-5))" ]; then
    artist=$(echo $artist | cut -c $(($artistlen-14))-)
  elif [ "$offset" -gt "10" ]; then
    offset=$(($offset - 10))
    artist=$(echo $artist | cut -c $offset-$(($offset+14)))
  else
    artist=$(echo $artist | cut -c -15)
  fi
fi

echo "$song -- $artist ($emin:$esec/$dmin:$dsec) "
Okay, so what's going on here? Remember those Mp3 players that we used to get in the early 2000s with inbuilt flash memory and small digital displays? Remember how the song names used to scroll on the screen, character by character, at the rate of one character a second? That is exactly what this script does. It returns a substring of the song name and the artist name to conky, and this text when displayed on the screen makes it look like the song text is scrolling.
The intended output is that the first 20 (15) characters of the song (artist) name should be displayed for 10 seconds. Then the rest of the song (artist) name should scroll on the screen, one character at at time, and finally it should pause at the last 20 characters of the song (artist) name for 5 seconds before the same cycle starts again. After we extract the song name, artist and elapsed time, we format the elapsed and total time so that they can be displayed beautifully (8 seconds should be displayed as 00:08 and not 0:8). Then if the song name length is greater than 20 characters, we find out what range of characters need to be displayed depending on the elapsed time (some fairly straightforward math in there), and use cut to pick out those characters. We repeat the same for the artist name. So now we have a working music player widget for our bar!

The result


That's all there is to the beautiful looking i3bar.

Next up, the important bindings in the i3 config file:

bindsym $mod+Return exec urxvtc

# hide the i3bar
bindsym $mod+m bar mode toggle

# hide the window titles
bindsym $mod+b border toggle
for_window [class="^.*"] border pixel 1

# Volume controls
bindsym XF86AudioRaiseVolume exec /usr/bin/bash /home/anant/.i3/increasevolume.sh && /usr/bin/bash /home/anant/.i3/volume.sh && killall -SIGUSR1 conky
bindsym XF86AudioLowerVolume exec /usr/bin/bash /home/anant/.i3/decreasevolume.sh && /usr/bin/bash /home/anant/.i3/volume.sh && killall -SIGUSR1 conky
bindsym XF86AudioMute exec amixer -q set Master toggle && killall -SIGUSR1 conky

bindsym XF86MonBrightnessUp exec xbacklight -inc 5 && killall -SIGUSR1 conky
bindsym XF86MonBrightnessDown exec xbacklight -dec 5 && killall -SIGUSR1 conky

# Keyboard backlight scripts for ASUS
bindsym XF86KbdBrightnessUp exec sudo /home/anant/.i3/asus_kbd_backlight_permission.sh && /usr/bin/bash /home/anant/.i3/asus_kbd_backlight.sh inc
bindsym XF86KbdBrightnessDown exec sudo /home/anant/.i3/asus_kbd_backlight_permission.sh && /usr/bin/bash /home/anant/.i3/asus_kbd_backlight.sh dec

bindsym $mod+F2 exec xbacklight -dec 5 && killall -SIGUSR1 conky
bindsym $mod+F3 exec xbacklight -inc 5 && killall -SIGUSR1 conky

# Lock screen shortcut
bindsym Mod4+Shift+l exec i3lock -i /home/anant/.config/variety/Downloaded/wallbase_macro/wallpaper-2684199.png -p default -d -n -f
bindsym Mod4+Shift+s exec systemctl suspend

bindsym $mod+Shift+n exec /usr/bin/variety --next
bindsym $mod+Shift+p exec /usr/bin/variety --previous

exec --no-startup-id /usr/bin/variety
exec --no-startup-id /usr/bin/dunst
exec --no-startup-id /home/anant/.i3/lowbattery.sh

# Creating named workspaces
set $tag1 "1: Web"
bindsym $mod+1 workspace $tag1
bindsym $mod+Shift+1 move container to workspace $tag1

set $tag2 "2: DC++"
bindsym $mod+2 workspace $tag2
bindsym $mod+Shift+2 move container to workspace $tag2

set $tag3 "3: Music"
bindsym $mod+3 workspace $tag3
bindsym $mod+Shift+3 move container to workspace $tag3

set $tag4 "4: Code"
bindsym $mod+4 workspace $tag4
bindsym $mod+Shift+4 move container to workspace $tag4

set $tag5 "5: ToDo"
bindsym $mod+5 workspace $tag5
bindsym $mod+Shift+5 move container to workspace $tag5

# Media controls
bindsym $mod+Shift+Home exec /usr/bin/cmus-remote -n && killall -SIGUSR1 conky
bindsym $mod+Shift+End exec /usr/bin/cmus-remote -r && killall -SIGUSR1 conky
bindsym XF86AudioNext exec /usr/bin/cmus-remote -n && killall -SIGUSR1 conky
bindsym XF86AudioPrev exec /usr/bin/cmus-remote -r && killall -SIGUSR1 conky
bindsym XF86AudioPlay exec /bin/bash /home/anant/.i3/cmusplaypause
bindsym XF86AudioPause exec /usr/bin/cmus-remote -u
bindsym $mod+Shift+Delete exec /bin/bash /home/anant/.i3/cmusplaypause

# Move to left/right workspace
bindsym Control+$mod+Left workspace prev
bindsym Control+$mod+Right workspace next
None of this really needs any explaining, but the next few paragraphs will explain some of the referenced scripts.

I use variety + feh to get the beautiful looking clock on my desktop and to shuffle through my wallpaper collection. To get variety to use feh (this is disabled by default due to some issues in Ubuntu), uncomment the line that executes the feh command in set_wallpaper in your variety config folder. The font used for the clock in the screenshot is called Akbar Plain, and the date font is called Alba Super Regular.

The variety clock+date setup


The notification daemon that I use is called dunst; it is nice and simple, and serves all my purposes. I use it primarily to receive notifications about two types of events - volume changes and low battery warnings. This is a simple script that displays the volume as a notification:

#!/bin/bash

vol=$(amixer get Master | grep % | cut -d ' ' -f7 | cut -c 2- | cut -d ']' -f1 | head -n1)

if amixer get Master | grep -q "off"; then
  dunstify -r 999 -u critical -t 1 "Volume: $vol (Muted)"
else
  dunstify -r 999 -u normal -t 1 "Volume: $vol"
fi

Volume notifications


The first line gets the volume value and the subsequent call to dunstify displays it. The -u critical/normal flags are just used to display different colors when muted/unmuted. The -r ensures that the volume notification always gets the same notification ID (if there are many calls to the script in a short span of time, the previous notifications shouldn't linger, rather, they should be replaced by the newer notifications).

Now for the low battery warning script:

#!/bin/bash

while true; do
  state=$(acpi -b | cut -d ' ' -f3 | head -c-2)
  perc=$(acpi -b | cut -d ' ' -f4 | head -c-3)
  if [ "$state" == "Discharging" ]; then
    if [ "$perc" -lt "15" ]; then
      /usr/bin/dunstify -r 998 -u critical "Battery Critically Low! (${perc}%)\n$(acpi -b | cut -d ' ' -f 5-)"
      sleep 60
    elif [ "$perc" -lt "20" ]; then
      /usr/bin/dunstify -r 998 -u normal "Battery Level Low (${perc}%)\n$(acpi -b | cut -d ' ' -f 5-)"
      sleep 120
    elif [ "$perc" -lt "30" ]; then
      /usr/bin/dunstify -r 998 -u low "Battery Level Low (${perc}%)\n$(acpi -b | cut -d ' ' -f 5-)"
      sleep 240
    else
      sleep 300
    fi
  else
    if [ "$perc" == "100" ]; then
      /usr/bin/dunstify -r 998 -u low "Battery Full (100%)"
      sleep 600
    else
      sleep 300
    fi
  fi
done

We check if the battery is discharging, the warning notifications are only displayed then. If the battery is full and discharging, the full battery notification is displayed. The rest is fairly straightforward, the percentage and time remaining and retrieved and displayed. Finally, depending on the percentage value, the script is put to sleep for either a minute, 2 minutes, 4 minutes or 5 minutes.

Battery notifications using dunst


Two final things: the script /usr/bin/dmenu_run can be modified to detach created processes from their parent shells by changing the execute command to: exec $(dmenu_path | dmenu "$@"), and a command line file manager like ranger can be used to make the task of browsing files easier.

Phew! I guess that covers the WM configuration part. That's all the time I have right now, so I'm going to defer explaining the configuration settings for my terminal emulator (urxvtd+c), text editor (vim), media player + nVidia integration (mpv), music player (cmus) and shell (zsh) to future blog posts.