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.

Monday, October 26, 2015

What studying Machine Learning has really taught me

I find myself surprised, even a little disappointed, by how emotions drive many of my decisions. Emotions feel like a necessary evil; many times they prevent me from making morally wrong decisions, much like religion does, but at other times they cloud my judgement and make it hard for me to think logically. I think we all have something to learn from Machine Learning here.

Whenever I am presented with a tough choice, more often than not I am able to reason about the merits and demerits of each option. This makes sense - years or training data provided to the system in my brain, along with positive and negative feedback, are helping it draw sharper boundaries based on the different attribute values presented to it. But here's where things get tricky. While I am able to reason about the positive and negative feedback that I received from previous choices that I have made, there are elements of the classification system that have formed as a result of emotions, possessing weights that I have no idea how to interpret. Let me explain that a bit better. Say I watch a violent movie by telling myself that I won't be affected by it (I do this often, because I believe that I understand better than to let some movie affect me). But this is analogous to saying, "Hey I'll just pass these (partially) misclassified data points through the training module of this Machine Learning system, but it's all good! I have added conditional statements to prevent these points from affecting these few weight values, so the integrity of the whole system will be preserved". While I can be pretty sure that doing this won't significantly affect the weight values that I have kept fixed, I can make no such assumptions about weights that I am unaware of. So the repercussions of passing such points through my classification system could be catastrophic to the accuracy and general coherency of the system. While it is true that the brain is a much more complex system than I have outlined and this might not be how it works, it is highly likely that watching that violent movie does affect weight values somewhere in a way that I cannot hope to understand. Over time a combination of these modified weight values could mutate the decision boundaries produced by the system to such an extent that I would be shocked the next time that I attempt to understand the decision making process.
Coming back to how I started this post, I am now able to relate the involvement of emotions in the decisions that I make and the inscrutable weight values in my classification system. It is pretty clear now that the reason that I am shocked when emotions affect my decisions is that I did not know the decision boundaries had been bent so. However, there is one aspect of this that doesn't fit well into this whole picture. Whenever I am presented with a choice, I see not one decision boundary, but two or more of them. One boundary that is the result of logical thinking, formed by weighing the pros and cons of each option, and another one, that is the result of emotions. Of course, I am oversimplifying; the two decision boundaries are not completely independent of each other, and sometimes there are way more than two sets of boundaries. But let's start with the easy case of two independent boundaries and solve that first.
The fact that there I am presented with two boundaries that I am able to perceive leads me to believe that the classification system in my brain uses an ensemble of classifiers to arrive at a decision. This raises many pertinent issues; are all the classifiers independent of each other? Were they trained independently, i.e. emotion related data points in a separate classifier and other points elsewhere (if this is even possible)? What is the function used to combine their output decisions? Why do I feel that the output produced by the emotion-only classifier is weighted much higher than the others in the combination function?
I've made a promise to myself to better analyze these data points and decision boundaries to try to understand how the classification system works. Furthermore, until I've done so to a satisfactory level, I'm going to assume that even mundane things that I do everyday are affecting weight values in ways that I do not understand, and so I need to keep a close watch on my emotions whenever I receive any training data (which is just about all the time!). Also, if anyone has any data files that can help me study this phenomenon, please email the labelled examples (preferably in CSV format) to me for analysis.
Lol jk :)