diff --git a/.Rprofile b/.Rprofile
new file mode 100644
index 00000000..23e4cbaa
--- /dev/null
+++ b/.Rprofile
@@ -0,0 +1,5 @@
+# The .First function is called after everything else in .Rprofile is executed
+#.First <- function() {
+#message("Welcome back ", Sys.getenv("USER"),"!\n","working directory is:", getwd())
+#}
+options(repos=structure(c(CRAN="http://cran.revolutionanalytics.com/")))
diff --git a/.Xdefaults b/.Xdefaults
new file mode 100644
index 00000000..6d1175cb
--- /dev/null
+++ b/.Xdefaults
@@ -0,0 +1,57 @@
+*foreground: #00cc00
+*foreground_bold: #a8a19f
+*cursor: #a8a19f
+!!background = #1b1918
+!!*background: rgba(0, 0, 0, .7)
+
+!! black
+*color0: #1b1918
+*color8: #766e6b
+!! red
+*color1: #f22c40
+*color9: #f22c40
+!! green
+*color2: #5ab738
+*color10: #5ab738
+!! yellow
+*color3: #d5911a
+*color11: #d5911a
+!! blue
+*color4: #407ee7
+*color12: #407ee7
+!! magenta
+*color5: #6666ea
+*color13: #6666ea
+!! cyan
+*color6: #00ad9c
+*color14: #00ad9c
+!! white
+*color7: #a8a19f
+*color15: #f1efee
+
+URxvt.intensityStyles: false
+URxvt.background: [70]#000000
+URxvt.depth: 32
+URxvt.font: xft:monospace:size=11
+URxvt.scrollBar: false
+URxvt.cursorColor: white
+URxvt.perl-ext-common: default,matcher,resize-font
+URxvt.perl-lib: $HOME/.config/Scripts
+URxvt.url-launcher: /usr/bin/xdg-open
+URxvt.matcher.button: 1
+URxvt.resize-font.smaller: C-Down
+URxvt.resize-font.bigger: C-Up
+
+URxvt.colorUL: #4682B4
+rofi.color-enabled: true
+rofi.color-window: #000, #000, #000
+rofi.color-normal: #111, #819396, #222, #008ed4, #ffffff
+rofi.color-active: #002b37, #008ed4, #003643, #008ed4, #66c6ff
+rofi.color-urgent: #002b37, #da4281, #003643, #008ed4, #890661
+
+rofi.fake-transparency: true
+rofi.lines: 3
+rofi.bw: 0
+rofi.opacity: "10"
+rofi.hide-scrollbar: true
+rofi.width: 30
diff --git a/.asoudrc b/.asoudrc
new file mode 100644
index 00000000..3d265408
--- /dev/null
+++ b/.asoudrc
@@ -0,0 +1,13 @@
+pcm.softvol {
+ type softvol
+ slave.pcm "cards.pcm.default"
+ control {
+ name "Software"
+ card 0
+ }
+ max_db 20.0
+}
+pcm.!default {
+ type plug
+ slave.pcm "softvol"
+}
diff --git a/.bash_profile b/.bash_profile
new file mode 100644
index 00000000..a410cf32
--- /dev/null
+++ b/.bash_profile
@@ -0,0 +1,14 @@
+#
+# ~/.bash_profile
+#
+
+[[ -f ~/.bashrc ]] && . ~/.bashrc
+
+#if [ -z "$DISPLAY" ] && [ "$(fgconsole)" -eq 1 ]; then
+ #startx
+#fi
+
+#[ -z "$DISPLAY" -a "$(fgconsole)" -eq 1 ] && exec startx
+
+
+export BROWSER=firefox
diff --git a/.config/Scripts/audio.sh b/.config/Scripts/audio.sh
new file mode 100644
index 00000000..5e61e20c
--- /dev/null
+++ b/.config/Scripts/audio.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+#This is the ffmpeg command that the audio shortcut in i3 will run.
+
+#Picks a file name for the output file based on availability:
+
+if [[ -f ~/output.flac ]]
+ then
+ n=1
+ while [[ -f ~/output_$n.flac ]]
+ do
+ n=$((n+1))
+ done
+ filename="output_$n.flac"
+ else
+ filename="output.flac"
+fi
+
+#The actual ffmpeg command:
+
+ffmpeg \
+-thread_queue_size 1024 \
+ -f alsa -ar 44100 -i hw:1 \
+-af "volume=12" \
+ -c:v libx264 -r 30 -c:a flac $filename
+ #-c:v ffvhuff -r 30 -c:a flac $filename
diff --git a/.config/Scripts/aurinstall.sh b/.config/Scripts/aurinstall.sh
new file mode 100644
index 00000000..94733882
--- /dev/null
+++ b/.config/Scripts/aurinstall.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+wget https://aur.archlinux.org/cgit/aur.git/snapshot/$1.tar.gz
+tar -xvzf $1.tar.gz
+cd $1 && makepkg --no-confirm -si
+cd .. && rm -rf $1 $1.tar.gz
diff --git a/.config/Scripts/bashrc b/.config/Scripts/bashrc
new file mode 100644
index 00000000..648758d6
--- /dev/null
+++ b/.config/Scripts/bashrc
@@ -0,0 +1,139 @@
+stty -ixon
+
+export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][\[$(tput setaf 3)\]\u\[$(tput setaf 2)\]@\[$(tput setaf 4)\]\h \[$(tput setaf 5)\]\W\[$(tput setaf 1)\]]\[$(tput setaf 7)\]\\$ \[$(tput sgr0)\]"
+
+if [[ -f /usr/bin/iceweasel ]]; then
+ export BROWSER="iceweasel"
+else
+ export BROWSER="firefox"
+fi
+
+shopt -s autocd
+
+#if [[ -z $DISPLAY ]] && [[ $(tty) = /dev/tty1 ]]; then
+ #exec startx
+#fi
+
+
+#Basic Aliases
+alias v="vim"
+#alias sv="sudo vim"
+alias r="ranger"
+alias ls='ls -hN --color=auto --group-directories-first'
+alias lsa='ls -A'
+g() { cd $1 && ls -a ;}
+alias b="cd .. && ls -a"
+alias ?="man"
+alias q="exit"
+alias e="exit"
+
+alias mutt="mutt -F ~/.config/mutt/muttrc"
+alias mocp="mocp --config ~/.config/moc/config"
+alias calcurse="calcurse -D ~/.config/calcurse"
+alias weechat="weechat -d ~/.config/weechat"
+alias syncthing="syncthing -home='$HOME/.syncthing'"
+alias tmux="tmux -f ~/.config/Scripts/tmux.conf"
+#NEWSBEUTER and W3M
+
+alias Xi="sudo xbps-install"
+alias Xu="sudo xbps-remove -R"
+alias Xup="sudo xbps-install -Suv"
+alias Xc="xbps-remove -Oo"
+alias Xq="xbps-query -Rs"
+alias XI="xbps-query -s"
+
+#Term only
+alias mute="pamixer -m"
+alias vd="pamixer -d 10"
+alias vu="pamixer -i 10"
+alias p="mocp -G &> /dev/null"
+alias next="mocp -f &> /dev/null"
+alias prev="mocp -r &> /dev/null"
+alias mnt="sudo bash ~/.config/Scripts/mount.sh"
+alias umnt="sudo bash ~/.config/Scripts/unmount.sh"
+alias sdn="sudo shutdown now"
+
+alias screenfetch="screenfetch -t"
+alias yt="youtube-dl -ic"
+alias yta="youtube-dl -xic"
+
+alias webedit="ssh -l lukesmith -p 2222 lukesmith.xyz"
+alias web="ssh -l lukesmith -p 2222 lukesmith.xyz"
+alias desk="ssh -l luke -p 22 10.193.0.46"
+serversend() { rsync -avr --rsh='ssh -p2222' $1 lukesmith@lukesmith.xyz:/home1/lukesmith/public_html ;}
+alias ein="ssh -l einchan -p 22 104.238.215.7"
+alias starwars="telnet towel.blinkenlights.nl"
+
+alias rcsync="rsync -avr --rsh='ssh -p2222' .bashrc .vimrc .Xdefaults .tmux.conf .muttrc .bash_profile .vim .w3m .moc lukesmith@lukesmith.xyz:/home1/lukesmith/config"
+
+alias newnet="sudo systemctl restart NetworkManager"
+alias atltime="sudo timedatectl set-timezone America/New_York && i3 restart"
+alias tuctime="sudo timedatectl set-timezone America/Phoenix && i3 restart"
+
+bl() { convert $@ -resize 1440x1080\> bl_$@ ;}
+
+alias pingme="ping lukesmith.xyz"
+
+alias youtube="youtube-viewer"
+alias YT="youtube-viewer"
+alias syt="youtube-viewer"
+
+alias etym="sdcv -u \"English Etymology\""
+
+alias Txa="cp ~/Documents/LaTeX/article.tex"
+alias Txs="cp ~/Documents/LaTeX/beamer.tex"
+alias Txh="cp ~/Documents/LaTeX/handout.tex"
+alias TC='find . -maxdepth 1 -regextype gnu-awk -regex "^.*\.(pyc|pyo|bak|swp|aux|log|nav|out|snm|toc|bcf|run\.xml|synctex\.gz|blg|bbl)" -delete'
+
+getgit() { git clone http://github.com/$1.git ;}
+
+folder() { echo -e "$1\t$2" >> ~/.config/Scripts/folders && i3 restart ;}
+weath() { curl wttr.in/$1 ;}
+alias work="mkdir ~/Work && cd ~/Work"
+
+
+longterm() { rsync -avr --rsh='ssh -p2222' $1 lukesmith@lukesmith.xyz:/home1/lukesmith/public_html/longterm ;}
+sendmine() { rsync -avr --rsh='ssh -p2222' $1 lukesmith@lukesmith.xyz:/home1/lukesmith/public_html/mine ;}
+
+alias test='~/.config/bumblebee-status/bumblebee-status -m pasink pasource battery nic pacman disk datetime -p disk.path=/home datetime.format="%a, %b %d, %Y at %I:%M %p" -t kulade'
+
+getlt() { wget http://lukesmith.xyz/longterm/$1 ;}
+getmine() { wget http://lukesmith.xyz/mine/$1 --user=lukesmith --ask-password ;}
+
+memefind() { find ~/Pictures/Memes/ -iname "*$1*" ;}
+CF() { cd ~/.config/$1 && ls ;}
+
+alias now="vim ~/Documents/Phonology/squib1.tex"
+
+alias ethspeed="speedometer -r enp0s25"
+
+note() { echo "$@" >> ~/notes ;}
+
+alias extract="~/.config/Script/ext.sh"
+
+alias refresh="python ~/.config/Scripts/shortcuts.py"
+
+backup() { cp $1 $1.bu ;}
+
+#alias man="w3mman"
+
+alias mailsync="bash ~/.config/Scripts/mailsyncloop.sh"
+
+alias wifispeed="speedometer -r wlp2s0"
+
+alias trigger="bash ~/Creations/Scripts/trigger.sh"
+
+alias bbs="python ~/.config/i3/bar/bumblebee-status"
+
+alias tr="transmission-remote"
+
+alias servs="ls /etc/sv"
+
+alias debase="sudo umount /home/Shared/Videos"
+
+alias makemine="sudo chown -R luke:luke /home/luke/Downloads/*"
+
+serven() { sudo ln -s /etc/sv/$1 /var/service/ ;}
+servdis() { sudo rm /var/service/$1 ;}
+
+aurinstall() { curl -O https://aur.archlinux.org/cgit/aur.git/snapshot/$1.tar.gz && tar -xvzf $1.tar.gz && cd $1 && makepkg --noconfirm -si && cd .. && rm -rf $1 $1.tar.gz ;}
diff --git a/.config/Scripts/clear.sh b/.config/Scripts/clear.sh
new file mode 100755
index 00000000..af13a73e
--- /dev/null
+++ b/.config/Scripts/clear.sh
@@ -0,0 +1 @@
+find . -maxdepth 1 -regextype gnu-awk -regex "^.*\.(pyc|p yo|bak|swp|aux|log|lof|nav|out|snm|toc|bcf|run\.xml|synctex\.gz|blg|bbl)" -delete
diff --git a/.config/Scripts/configs b/.config/Scripts/configs
new file mode 100644
index 00000000..ab70dcf3
--- /dev/null
+++ b/.config/Scripts/configs
@@ -0,0 +1,24 @@
+cfb ~/.config/Scripts/bashrc
+cfs ~/.config/Scripts/status.sh
+cfz ~/.zshrc
+cfv ~/.config/Scripts/vimrc
+cfr ~/.config/ranger/rc.conf.base
+cfi ~/.config/i3/config
+cfq ~/.config/qutebrowser/keys.conf.base
+cfQ ~/.config/qutebrowser/qutebrowser.conf
+cfm ~/.config/mutt/muttrc
+cfM ~/.config/moc/keymap
+cff ~/.config/Scripts/folders
+cfc ~/.config/Scripts/configs
+cft ~/.config/termite/config
+cfT ~/.config/Scripts/tmux.conf
+eb ~/Documents/LaTeX/uni.bib
+cv ~/Documents/LaTeX/cv.tex
+cfl ~/.config/mutt/lukexyz.info
+cfx ~/.config/mutt/luxmyth.info
+cfk ~/.config/mutt/kulade.cock
+cfo ~/.config/mutt/kulade.info
+cfa ~/.config/mutt/aliases
+cfp ~/.config/polybar/config
+cfd ~/.Xdefaults
+TO ~/Creations/Videos/todo.md
diff --git a/.config/Scripts/drives b/.config/Scripts/drives
new file mode 100755
index 00000000..3bf38d49
--- /dev/null
+++ b/.config/Scripts/drives
@@ -0,0 +1,7 @@
+wd2 1A3FE4E62BC1A399
+sd 9C33-6BBD
+king 66faa60c-b24f-46a6-92cd-b064e9dbdc6c
+sony4 6C67-A7B4
+black 0ff55b51-5294-4bc2-bbf0-819a804b0503
+archive 0EC4117AC41164ED
+blue 8f0d3707-f2a1-4a8f-910a-49d97ba80931
diff --git a/.config/Scripts/extract.sh b/.config/Scripts/extract.sh
new file mode 100755
index 00000000..5b70a65c
--- /dev/null
+++ b/.config/Scripts/extract.sh
@@ -0,0 +1,24 @@
+if [ -f $1 ] ; then
+ # NAME=${1%.*}
+ # mkdir $NAME && cd $NAME
+ case $1 in
+ *.tar.bz2) tar xvjf ../$1 ;;
+ *.tar.gz) tar xvzf ../$1 ;;
+ *.tar.xz) tar xvJf ../$1 ;;
+ *.lzma) unlzma ../$1 ;;
+ *.bz2) bunzip2 ../$1 ;;
+ *.rar) unrar x -ad ../$1 ;;
+ *.gz) gunzip ../$1 ;;
+ *.tar) tar xvf ../$1 ;;
+ *.tbz2) tar xvjf ../$1 ;;
+ *.tgz) tar xvzf ../$1 ;;
+ *.zip) unzip ../$1 ;;
+ *.Z) uncompress ../$1 ;;
+ *.7z) 7z x ../$1 ;;
+ *.xz) unxz ../$1 ;;
+ *.exe) cabextract ../$1 ;;
+ *) echo "extract: '$1' - unknown archive method" ;;
+ esac
+else
+echo "$1 - file does not exist"
+ fi
diff --git a/.config/Scripts/flash_win.sh b/.config/Scripts/flash_win.sh
new file mode 100644
index 00000000..91d6416c
--- /dev/null
+++ b/.config/Scripts/flash_win.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+#Flashes the active window.
+
+#Requires transset-df and a composite manager, like xcompmgr.
+
+transset -a -m 0
+sleep .1
+transset -a -x 1
+sleep .1
+transset -a -m 0
+sleep .1
+transset -a -x 1
diff --git a/.config/Scripts/folders b/.config/Scripts/folders
new file mode 100755
index 00000000..3826f6dd
--- /dev/null
+++ b/.config/Scripts/folders
@@ -0,0 +1,10 @@
+h ~
+d ~/Documents
+D ~/Downloads
+p ~/Pictures
+v ~/Videos
+m ~/Music
+b ~/Books
+s ~/.config/Scripts
+/ /
+r /
diff --git a/.config/Scripts/imapsync.sh b/.config/Scripts/imapsync.sh
new file mode 100644
index 00000000..47bc43e9
--- /dev/null
+++ b/.config/Scripts/imapsync.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+if [ -f $(pgrep offlineimap) ]; then
+ offlineimap -o
+ echo "Sync begun."
+else
+ echo "Sync in progress."
+fi
diff --git a/.config/Scripts/luke_guide.md b/.config/Scripts/luke_guide.md
new file mode 100644
index 00000000..2d311b38
--- /dev/null
+++ b/.config/Scripts/luke_guide.md
@@ -0,0 +1,154 @@
+# A friendly guide to Luke's i3 rice
+
+
+Use vim keys (h/j/k/l) to navigate this document. Pressing W will fit it to window width. + and - zoom in and out. q to quit. (These are general mupdf shortcuts.)
+
++ Mod+u will show this document at any time.
+
+General questions? Leave a comment on YouTube or email me at luke@lukesmith.xyz.
+
+## Basic goals and principles of my rice:
+
++ Naturalness - I want the number of keypresses I have to make to get what I want as little as possible.
++ Keyboard/vim-centrality - All my terminal apps (and other programs) use vim keys when possible. My hands never need leave the home row or thereabout.
++ Lots of color -- Many rices stick to one general color palatte. I like my system to be very vibrant.
+
+## General changes
+
++ Capslock is now an alternative escape. Makes vim-craft much more efficient.
++ The menu button (usually between the right Alt and Ctrl) is an alternative Super/Mod button. This is to make one-handing on my laptops easier.
+
+# Shortcut keys
+
+## Window basics
+
+Notice the case sensitivity of the shortcuts.
+
+Be sure you play around with these. Be flexible with the basic commands and the rice will grow on you quick.
+
++ Mod+Enter -- Spawn terminal
++ Mod+q or Q -- Close window
++ Mod+d -- rofi (For running commands or programs without shortcuts)
++ Mod+t -- Toggle between spawning vertically and horizontally
++ Mod+f or F11 -- Fullscreen
++ Mod+h/j/k/l -- Move to different windows
++ Mod+H/J/K/L -- Move a window around
++ Mod+Y/U/I/O -- Resize windows
++ Mod+/ -- Spawn vertical terminal
++ Mod+' -- Spawn horizonal terminal
++ Mod+s/S -- Increase/decrease inner gaps
++ Mod+z/Z -- Increase/decrease outer gaps
++ Mod+D -- Reduce gaps to 0
++ Mod+T -- Restore gaps to default (15)
++ Mod+Shift+Space -- Make a window float (you can still resize and move floating windows with the same keys above)
++ Mod+Space -- Switch from a floating window to a non-floating one (or vice versa)
+
+## Basic Programs
+
++ Mod+r -- ranger (file browser/manager)
++ Mod+e -- mutt (email)
++ Mod+m -- Music on Console Player
++ Mod+a -- qalc (calculator)
++ Mod+i -- htop (system info)
++ Mod+N -- newsbeuter (RSS feed reader)
++ Mod+y -- calcurse (calendar and schedule)
++ Mod+Shift+Enter -- tmux
++ Mod+w -- qutebrowser (web browser)
+
+## Larger programs
+
++ Mod+W -- Firefox
++ Mod+E -- Thunderbird (Not installed by default)
++ Mod+B -- Blender
++ Mod+G -- GIMP
++ Mod+P -- Pinta
++ Mod+A -- Audacity
+
+## System
+
++ Mod+R -- Restart/refresh i3 (renews configs, does not close any programs)
++ Mod+x -- i3lock (Enter password to return)
++ Mod+X -- shutdown now (Be careful with this one!)
++ Mod+Shift+Backspace -- reboot (And this one!)
++ Mod+Shift+Escape -- exit i3 (And this one as well!)
++ Mod+F3 -- arandr (For adding screens/HDMI/VGA connections)
++ Mod+F4 -- Hibernate
++ Mod+F5 -- Reset Network Manager
++ Mod+F7 -- Increase window transparency
++ Mod+F8 -- Decrease window transparency
+
+## Audio
+
+I use moc/mocp as a music player. If you prefer cmus, I have commented out shortcuts you can activate for it instead in the i3 config.
+
++ Mod+m -- Music on Console Player
++ Mod+n -- Next track
++ Mod+b -- Previous track
++ Mod+o -- Restart track
++ Mod+p -- Pause
++ Mod+M -- Mute all audio
++ Mod+v -- cli-visualizer
++ Mod+V -- projectM visualizer
++ Mod+- -- Decrease volume (holding shift increases amount)
++ Mod++ -- Increase volume (holding shift increases amount)
++ Mod+[ -- Back 10 seconds (holding shift increases amount)
++ Mod+] -- Forward 10 seconds (holding shift increases amount)
+
+## Workspaces
+
+There are ten workspaces. They work just like those in vanilla i3 with some additions.
+
++ Mod+(Number) -- Go to that number workspace
++ Mod+Shift+(Number) -- Send window to that workspace
++ Mod+Tab -- Go to previous workspace
++ Mod+g or escape -- Go to left workspace
++ Mod+; -- Go to right workspace
+
+## Recording
+
++ Pause -- Begin screencast. Output goes into ~. Will overwrite any previous output.
++ ScrollLock -- Begin audio recording. Same traits as above
++ Mod+either of the above keys -- kills ffmpeg, thus ending recordings
++ ThinkVantage button (on Thinkpads) -- kills ffmpeg, thus ending recordings
++ Print Screen -- Take a scrot screenshot
++ Shift+Print Screen -- Take a scrot screenshot of only selected window
+
+## Other buttons
+
+I've mapped those extra buttons that some keyboards have (play and pause buttons, email, webbrowsing buttons, etc.) to what you would expect.
+
+# Special traits of my rice
+
+## Easy config access
+
+Open a terminal and type "cfc." This will open a file where you will see customizable pairs of key shortcuts and config files. Enter any of these shortcuts in bash or ranger to immediately open the file in vim.
+
+You may add new entries here and they will be refreshed when you refresh i3 (Mod+R).
+
+## Folder and config shortcuts
+
+Open a terminal and type "cff." This opens a file when you can keep and create folder shortcuts. There are only a few here now, because I don't know what your folder structure is going to look like, but on my machine, I have 81 and growing.
+
+Each line has a shortcut key/keys and its target. These can be used in serveral applications. In bash, simply press "d," the shortcut for ~/Documents and you will cd there (and automatically ls -a).
+
+ranger works similarly. When in ranger, just press "g" then the shortcut of the folder you want to go to. You may also press "t" plus the shortcut to open a new tab there. "m" plus the shortcut moves the selected files to the folder and "Y" copies them there. **Get good at this. It will make management of even the most complex file system easy.**
+
+Lastly qutebrowser implements these shortcuts as well. When you see a file or image you want to download, press ";" followed by the folder shortcut and qutebrowser will let you select the file with its hint system. The file will then download to the directory you chose.
+
+## Dynamically constructed configs
+
+To keep these different shortcuts in sync, my rice will dynamically reconstruct the configs to bash, qutebrowser and ranger each time you refresh i3 (Mod+R).
+
+Each time i3 starts or restarts, it will run ~/.config/Scripts/shortcuts.py, which reads the entries in the folder shortcut and config shortcut files and then translate them into the approriate syntax of all three programs.
+
+It then takes that output and appends it to base configs of each program (~/.config/Scripts/bashrc, ~/config/qutebrowser/keys.conf.base, ~/.config/rc.conf.base) and puts the output in the proper places for each program.
+
+## So what do I need to know?
+
+Use the files in "cff" and "cfc" to add/change shortcuts. These shortcuts will be synced between bash, ranger and qutebrowser. Press Mod+R to refresh them. If you want to make permanent changes to your bash/ranger/qutebrowser configs, make them to the base files which you can access with "cfb," "cfr," and "cfq," respectively, then press Mod+R.
+
+# Explore and customize
+
+Don't like something about the rice? Change it. If you have a problem, try figuring it out yourself, but if you can't, ask on my YouTube or elsewhere.
+
+Considering how formidable my rice might seem to greener Linux users, I hope between my vids and my documentation, you can tweak exactly what you want from it.
diff --git a/.config/Scripts/luke_guide.pdf b/.config/Scripts/luke_guide.pdf
new file mode 100644
index 00000000..e7857eb6
Binary files /dev/null and b/.config/Scripts/luke_guide.pdf differ
diff --git a/.config/Scripts/mailsyncloop.sh b/.config/Scripts/mailsyncloop.sh
new file mode 100755
index 00000000..206461df
--- /dev/null
+++ b/.config/Scripts/mailsyncloop.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+offlineimap -o
+while :
+do
+if [ -f $(pgrep offlineimap) ]; then
+ offlineimap -o
+ echo "OfflineIMAP sync complete."
+else
+ echo "OfflineIMAP already running."
+fi
+sleep 60
+done
diff --git a/.config/Scripts/mount.sh b/.config/Scripts/mount.sh
new file mode 100755
index 00000000..80a01f2f
--- /dev/null
+++ b/.config/Scripts/mount.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+for d in /dev/sd*
+do
+
+while IFS=$'\t' read -r col1 col2
+do
+ if [[ $(blkid -o value -s UUID $d) == ${col2} ]]
+ then
+ sudo mkdir /mnt/${col1}
+ sudo mount $d /mnt/${col1}
+ fi
+done < /home/kulade/.config/Scripts/drives
+
+done
+
diff --git a/.config/Scripts/remaps b/.config/Scripts/remaps
new file mode 100644
index 00000000..dce1bcb0
--- /dev/null
+++ b/.config/Scripts/remaps
@@ -0,0 +1 @@
+keycode 135 = Super_R
diff --git a/.config/Scripts/resize-font b/.config/Scripts/resize-font
new file mode 100644
index 00000000..cc89a965
--- /dev/null
+++ b/.config/Scripts/resize-font
@@ -0,0 +1,169 @@
+# vim:ft=perl:fenc=utf-8
+# Copyright (c) 2009-, Simon Lundström
+# Copyright (c) 2014 Maarten de Vries
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Usage:
+# Set your font in ~/.Xresources:
+# urxvt.font: xft:Inconsolata:pixelsize=12
+# to set it with pixels or
+# urxvt.font: xft:Inconsolata:size=12
+# to set it with points.
+
+# And re-bind some keymappings (if you want, below are the defaults):
+# URxvt.keysym.C-minus: resize-font:smaller
+# URxvt.keysym.C-plus: resize-font:bigger
+# URxvt.keysym.C-equal: resize-font:reset
+# URxvt.keysym.C-question: resize-font:show
+#
+
+my @fonts = (
+ {'name' => 'font', 'code' => 710},
+ {'name' => 'boldFont', 'code' => 711},
+ {'name' => 'italicFont', 'code' => 712},
+ {'name' => 'boldItalicFont', 'code' => 713},
+);
+
+my @fixed = qw(4x6 5x7 5x8 6x9 6x10 6x12 6x13 7x13 7x14 8x13 8x16 9x15 9x18 10x20 12x24);
+
+sub on_start {
+ my ($self) = @_;
+
+ foreach (@fonts) {
+ $_->{'default'} = $self->resource($_->{'name'});
+ }
+}
+
+sub on_init {
+ my ($self) = @_;
+ my $commands = {
+ "smaller" => "C-minus",
+ "bigger" => "C-plus",
+ "reset" => "C-equal",
+ "show" => "C-question",
+ };
+ bind_hotkeys($self, $commands);
+
+ ()
+}
+
+sub bind_hotkeys {
+ my ($self, $commands) = @_;
+
+ for (keys %$commands) {
+ my $hotkey = $$commands{$_};
+ my $hotkey_bound = $self->{'term'}->x_resource("keysym.$hotkey");
+ if (!defined($hotkey_bound) ) {
+ # Support old-style key bindings
+ if ($self->x_resource("%.$_")) {
+ $hotkey = $self->x_resource("%.$_");
+ }
+
+ # FIXME If we're bound to a keysym, don't bind the default.
+ $self->bind_action($hotkey, "%:$_") or
+ warn "unable to register '$hotkey' as hotkey for $_";
+ }
+ else {
+ if ($hotkey_bound !~ /^resize-font:/) {
+ warn "Hotkey $$commands{$_} already bound to $hotkey_bound, not binding to resize-font:$_ by default.";
+ }
+ }
+ }
+}
+
+sub on_action {
+ my ($self, $string) = @_;
+ my $regex = qr"(?!pixelsize=)(\d+)";
+
+ if ($string eq "bigger") {
+ foreach (@fonts) {
+ next if not defined($_->{'default'});
+ update_font_size($self, $_, +2);
+ }
+ }
+ elsif ($string eq "smaller") {
+ foreach (@fonts) {
+ next if not defined($_->{'default'});
+ update_font_size($self, $_, -2);
+ }
+ }
+ elsif ($string eq "reset") {
+ foreach (@fonts) {
+ next if not defined($_->{'default'});
+ set_font($self, $_, $_->{'default'});
+ }
+ }
+ elsif ($string eq "show") {
+
+ my $term = $self->{'term'};
+ $term->{'resize-font'}{'overlay'} = {
+ ov => $term->overlay_simple(0, -1, format_font_info($self)),
+ to => urxvt::timer
+ ->new
+ ->start(urxvt::NOW + 1)
+ ->cb(sub {
+ delete $term->{'resize-font'}{'overlay'};
+ }),
+ };
+ }
+
+ ()
+}
+
+sub get_font {
+ my ($self, $name) = @_;
+ return $self->resource($name);
+}
+
+sub set_font {
+ my ($self, $font, $new) = @_;
+ $self->cmd_parse(sprintf("\33]%d;%s\007", $font->{'code'}, $new));
+}
+
+sub update_font_size {
+ my ($self, $font, $delta) = @_;
+ my $regex = qr"(?<=size=)(\d+)";
+ my $current = get_font($self, $font->{'name'});
+
+ my ($index) = grep { $fixed[$_] eq $current } 0..$#fixed;
+ if ($index or $index eq 0) {
+ my $inc = $delta / abs($delta);
+ $index += $inc;
+ if ($index < 0) { $index = 0; }
+ if ($index > $#fixed) { $index = $#fixed; }
+ $current = $fixed[$index];
+ }
+ else {
+ $current =~ s/$regex/$1+$delta/ge;
+ }
+ set_font($self, $font, $current);
+}
+
+sub format_font_info {
+ my ($self) = @_;
+
+ my $width = 0;
+ foreach (@fonts) {
+ my $length = length($_->{'name'});
+ $width = $length > $width ? $length : $width;
+ }
+ ++$width;
+
+ my $info = '';
+ foreach (@fonts) {
+ $info .= sprintf("%-${width}s %s\n", $_->{'name'} . ':', get_font($self, $_->{'name'}));
+ }
+
+ return $info;
+}
diff --git a/.config/Scripts/screencast.sh b/.config/Scripts/screencast.sh
new file mode 100644
index 00000000..0401da62
--- /dev/null
+++ b/.config/Scripts/screencast.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+#This is the ffmpeg command that the screencast shortcut in i3 will run.
+
+#Picks a file name for the output file based on availability:
+
+if [[ -f ~/output.mkv ]]
+ then
+ n=1
+ while [[ -f $HOME/output_$n.mkv ]]
+ do
+ n=$((n+1))
+ done
+ filename="$HOME/output_$n.mkv"
+ else
+ filename="$HOME/output.mkv"
+fi
+
+#The actual ffmpeg command:
+
+ffmpeg -y \
+-f x11grab \
+-s $(xdpyinfo | grep dimensions | awk '{print $2;}') \
+-i :0.0 \
+-thread_queue_size 1024 \
+ -f alsa -ar 44100 -i hw:1 \
+-af "volume=12" \
+ -c:v libx264 -r 30 -c:a flac $filename
+ #-c:v ffvhuff -r 30 -c:a flac $filename
diff --git a/.config/Scripts/shortcuts.py b/.config/Scripts/shortcuts.py
new file mode 100644
index 00000000..154d208d
--- /dev/null
+++ b/.config/Scripts/shortcuts.py
@@ -0,0 +1,41 @@
+import csv
+
+qute = ""
+rang = ""
+bash = ""
+
+with open(".config/qutebrowser/keys.conf.base") as qb:
+ qute+=qb.read()
+with open(".config/ranger/rc.conf.base") as rg:
+ rang+=rg.read()
+with open(".config/Scripts/bashrc") as bsh:
+ bash+=bsh.read()
+
+#First we open the list of folder shortcuts and go down each line adding each in the required syntax to each of the three configs:
+
+with open(".config/Scripts/folders") as fold:
+ for line in csv.reader(fold, dialect="excel-tab"):
+ #Adds the qutebrowser downloads commands:
+ qute+="set storage download-directory "+line[1]+" ;; hint links download\n\t;"+line[0]+"\n"
+ #Adds the ranger go, tab, move and yank commands:
+ rang+=("map g"+line[0]+" cd "+line[1]+"\n")
+ rang+=("map t"+line[0]+" tab_new "+line[1]+"\n")
+ rang+=("map m"+line[0]+" shell mv %s "+line[1]+"\n")
+ rang+=("map Y"+line[0]+" shell cp -r %s "+line[1]+"\n")
+ #Adds the bash shortcuts:
+ bash+=("alias "+line[0]+"=\"cd "+line[1]+" && ls -a\"\n")
+
+#Goes thru the config file file and adds the shortcuts to both bash and ranger.
+
+with open(".config/Scripts/configs") as conf:
+ for line in csv.reader(conf, dialect="excel-tab"):
+ bash+=("alias "+line[0]+"=\"vim "+line[1]+"\"\n")
+ rang+=("map "+line[0]+" shell vim "+line[1]+"\n")
+
+
+with open(".config/ranger/rc.conf", "w") as outrang:
+ outrang.write(rang)
+with open(".config/qutebrowser/keys.conf","w") as outqb:
+ outqb.write(qute)
+with open(".bashrc","w") as outbash:
+ outbash.write(bash)
diff --git a/.config/Scripts/shrinkvid.sh b/.config/Scripts/shrinkvid.sh
new file mode 100755
index 00000000..2ac63d34
--- /dev/null
+++ b/.config/Scripts/shrinkvid.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+sex=$(ffprobe -i $1 -show_entries format=duration -v quiet -of csv="p=0")
+rate=$(qalc -t 1000000000 / $sex)
+#rate=$(( 1000000000 / $sex ))
+
+base=$(basename $1)
+ext="${base##*.}"
+base="${base%.*}"
+
+ffmpeg -i $1 -b $rate $base'_min.'$ext
+echo "sex is $sex"
+echo "rate is $rate"
diff --git a/.config/Scripts/speedvid.sh b/.config/Scripts/speedvid.sh
new file mode 100755
index 00000000..ba3a4f6c
--- /dev/null
+++ b/.config/Scripts/speedvid.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+base=$(basename $1)
+ext="${base##*.}"
+base="${base%.*}"
+
+ffmpeg -i $1 -vf "setpts=$2*PTS" -an $base'_sped.'$ext
diff --git a/.config/Scripts/status.sh b/.config/Scripts/status.sh
new file mode 100644
index 00000000..9952f951
--- /dev/null
+++ b/.config/Scripts/status.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+tmux -f ~/.config/Scripts/tmux.conf new-session -s "status" -d
+tmux split-window -v "htop"
+tmux split-window -h "speedometer -r wlp2s0"
+tmux split-window -v "speedometer -r enp0s25"
+tmux select-pane -t 0
+tmux split-window -h "bash ~/.config/Scripts/mailsyncloop.sh"
+tmux select-pane -t 0
+tmux resize-pane -R 30
+tmux -2 attach-session -d
diff --git a/.config/Scripts/test.sh b/.config/Scripts/test.sh
new file mode 100755
index 00000000..284192ae
--- /dev/null
+++ b/.config/Scripts/test.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+ffmpeg \
+-f pulse -ac 2 -ar 48000 -i alsa_output.pci-0000_00_1b.0.analog-stereo.monitor \
+-f pulse -ac 1 -ar 44100 -i alsa_input.usb-Blue_Microphones_Yeti_Stereo_Microphone_REV8-00.analog-stereo \
+-filter_complex amix=inputs=2 \
+-f x11grab -r 30 -s 1280x800 -i :0.0+0,0 \
+-vcodec libx264 -preset fast -crf 18 \
+-acodec libmp3lame -ar 44100 -q:a 1 \
+-pix_fmt yuv420p \
+this_output.mkv
diff --git a/.config/Scripts/tmux.conf b/.config/Scripts/tmux.conf
new file mode 100644
index 00000000..f255afaa
--- /dev/null
+++ b/.config/Scripts/tmux.conf
@@ -0,0 +1,177 @@
+##############################
+# _
+# | |_ _ __ ___ _ ___ __
+# | __| '_ ` _ \| | | \ \/ /
+# | |_| | | | | | |_| |> <
+# \__|_| |_| |_|\__,_/_/\_\
+#
+#############################
+#
+# COPY AND PASTE
+# http://robots.thoughtbot.com/tmux-copy-paste-on-os-x-a-better-future
+#
+# Use vim keybindings in copy mode
+setw -g mode-keys vi
+
+# Setup 'v' to begin selection as in Vim
+#bind-key -t vi-copy v begin-selection
+#bind-key -t vi-copy y copy-pipe "reattach-to-user-namespace pbcopy"
+
+# Update default binding of `Enter` to also use copy-pipe
+# unbind -t vi-copy Enter
+# bind-key -t vi-copy Enter copy-pipe "reattach-to-user-namespace pbcopy"
+#
+############################################################################
+############################################################################
+# Reset Prefix
+############################################################################
+set -g prefix C-a
+bind-key a send-prefix # for nested tmux sessions
+
+############################################################################
+# Global options
+############################################################################
+# large history
+set-option -g history-limit 10000
+
+# colors
+setw -g mode-bg black
+set-option -g default-terminal "screen-256color" #"xterm-256color" # "screen-256color"
+set-option -g pane-active-border-fg green
+
+# utf8 support
+#set-window-option -g utf8 on
+
+
+# basic settings
+set-window-option -g xterm-keys on # for vim
+set-window-option -g mode-keys vi # vi key
+set-window-option -g monitor-activity on
+set-window-option -g window-status-current-fg white
+setw -g window-status-current-attr reverse
+
+# Automatically set window title
+setw -g automatic-rename
+
+# use mouse # More on mouse support http://floriancrouzat.net/2010/07/run-tmux-with-mouse-support-in-mac-os-x-terminal-app/
+#setw -g mode-mouse on
+#setw -g mouse-resize-pane on
+#set -g mouse-select-window on
+#set -g mouse-select-pane on
+set -g mouse on
+set -g history-limit 30000
+set -g terminal-overrides 'xterm*:smcup@:rmcup@'
+
+# vi movement keys
+# set-option -g status-keys vi
+
+############################################################################
+# Status Bar
+############################################################################
+#set-option -g status-utf8 on
+set-option -g status-justify right
+set-option -g status-bg black # colour213 # pink
+set-option -g status-fg cyan
+set-option -g status-interval 5
+set-option -g status-left-length 30
+set-option -g status-left '#[fg=magenta]» #[fg=blue,bold]#T#[default]'
+set-option -g status-right '#[fg=red,bold][[ #(git branch) branch ]] #[fg=cyan]»» #[fg=blue,bold]###S #[fg=magenta]%R %m-%d#(acpi | cut -d ',' -f 2)#[default]'
+set-option -g visual-activity on
+
+# Titles (window number, program name, active (or not)
+set-option -g set-titles on
+set-option -g set-titles-string '#H:#S.#I.#P #W #T'
+
+
+############################################################################
+# Unbindings
+############################################################################
+#unbind [ # copy mode bound to escape key
+unbind j
+unbind C-b # unbind default leader key
+unbind '"' # unbind horizontal split
+unbind % # unbind vertical split
+
+
+############################################################################
+# Bindings
+############################################################################
+# reload tmux conf
+bind-key r source-file ~/.tmux.conf
+
+#bind Escape copy-mode
+
+# new split in current pane (horizontal / vertical)
+bind-key c split-window -v # split pane horizontally
+bind-key v split-window -h # split pane vertically
+
+# list panes
+bind-key Space list-panes
+
+# break-pane
+bind-key Enter break-pane
+
+# join-pane [-dhv] [-l size | -p percentage] [-s src-pane]
+# [-t:dst-window.dst-pane] (destination window (dot) destination pane
+# (alias: joinp)
+#
+#bind C-j command-prompt "joinp"
+#bind C-j command-prompt "join-pane"
+#bind-key j command-prompt "join-pane -s '%%'"
+#bind-key j command-prompt "joinp -t:0"
+bind-key Space command-prompt "joinp -t:%%" # %% = prompt for window.pane [-V|H] # vert|hor split
+
+#previous pane
+bind-key -n C-up prev
+bind-key -n C-left prev
+
+#next pane
+bind-key -n C-right next
+bind-key -n C-down next
+
+############################################################################
+# windows
+############################################################################
+set-window-option -g window-status-current-bg red
+bind C-j previous-window
+bind C-k next-window
+bind-key C-a last-window # C-a C-a for last active window
+bind A command-prompt "rename-window %%"
+# By default, all windows in a session are constrained to the size of the
+# smallest client connected to that session,
+# even if both clients are looking at different windows.
+# It seems that in this particular case, Screen has the better default
+# where a window is only constrained in size if a smaller client
+# is actively looking at it.
+setw -g aggressive-resize on
+
+############################################################################
+# panes
+############################################################################
+# Navigation ---------------------------------------------------------------
+# use the vim motion keys to move between panes
+bind-key h select-pane -L
+bind-key j select-pane -D
+bind-key k select-pane -U
+bind-key l select-pane -R
+
+# Resizing ---------------------------------------------------------------
+bind-key C-h resize-pane -L
+bind-key C-j resize-pane -D
+bind-key C-k resize-pane -U
+bind-key C-l resize-pane -R
+
+# use vim motion keys while in copy mode
+setw -g mode-keys vi
+
+############################################################################
+# layouts
+############################################################################
+bind o select-layout "active-only"
+bind M-- select-layout "even-vertical"
+bind M-| select-layout "even-horizontal"
+bind M-r rotate-window
+
+
+# focus on first window
+# select-window -t 0
diff --git a/.config/Scripts/transmission.sh b/.config/Scripts/transmission.sh
new file mode 100755
index 00000000..794f6bb6
--- /dev/null
+++ b/.config/Scripts/transmission.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+if [ -f $(pgrep transmission) ];
+ then
+ urxvt -e transmission-remote-cli
+ else
+ transmission-daemon && urxvt -e transmission-remote-cli
+fi
diff --git a/.config/Scripts/unmount.sh b/.config/Scripts/unmount.sh
new file mode 100755
index 00000000..e43053af
--- /dev/null
+++ b/.config/Scripts/unmount.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+sync
+umount /mnt/* 2> /dev/null
+rm -d /mnt/* 2> /dev/null
diff --git a/.config/Scripts/video.sh b/.config/Scripts/video.sh
new file mode 100644
index 00000000..6141b407
--- /dev/null
+++ b/.config/Scripts/video.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+#This is the ffmpeg command that the screencast shortcut in i3 will run.
+
+#Picks a file name for the output file based on availability:
+
+if [[ -f ~/output.mkv ]]
+ then
+ n=1
+ while [[ -f ~/output_$n.mkv ]]
+ do
+ n=$((n+1))
+ done
+ filename="output_$n.mkv"
+ else
+ filename="output.mkv"
+fi
+
+#The actual ffmpeg command:
+
+ffmpeg -y \
+-f x11grab \
+-s $(xdpyinfo | grep dimensions | awk '{print $2;}') \
+-i :0.0 \
+ -c:v libx264 -qp 0 -r 30 $filename
+ #-c:v ffvhuff -r 30 -c:a flac $filename
diff --git a/.config/Scripts/vimrc b/.config/Scripts/vimrc
new file mode 100644
index 00000000..f12d2f65
--- /dev/null
+++ b/.config/Scripts/vimrc
@@ -0,0 +1,166 @@
+execute pathogen#infect()
+set number
+set relativenumber
+set so=10
+
+
+"""BASIC TOOLS
+"Navigating with guides
+inoremap /<++>"_c4l
+vnoremap /<++>"_c4l
+map /<++>"_c4l
+inoremap ;gui <++>
+"For normal mode when in terminals (in X I have caps mapped to esc, this replaces it when I don't have X)
+inoremap jw
+inoremap wj
+inoremap
+"For split navigation
+map h
+map j
+map k
+map l
+
+set nocompatible
+filetype plugin on
+
+"""LATEX
+autocmd FileType tex inoremap ;fr \begin{frame}\frametitle{}<++>\end{frame}<++>6kf}i
+autocmd FileType tex inoremap ;fi \begin{fitch}\end{fitch}<++>3kA
+autocmd FileType tex inoremap ;exe \begin{exe}\ex\end{exe}<++>3kA
+autocmd FileType tex inoremap ;em \emph{}<++>T{i
+autocmd FileType tex inoremap ;bf \textbf{}<++>T{i
+autocmd FileType tex inoremap ;it \textit{}<++>T{i
+autocmd FileType tex inoremap ;ct \textcite{}<++>T{i
+autocmd FileType tex inoremap ;cp \parencite{}<++>T{i
+autocmd FileType tex inoremap ;glos {\gll<++>\\<++>\\\trans{``<++>''}}2k2bcw
+autocmd FileType tex inoremap ;x \begin{xlist}\ex\end{xlist}kA
+autocmd FileType tex inoremap ;ol \begin{enumerate}\end{enumerate}<++>3kA\item
+autocmd FileType tex inoremap ;ul \begin{itemize}\end{itemize}<++>3kA\item
+autocmd FileType tex inoremap ;li \item
+autocmd FileType tex inoremap ;ref \ref{}<++>T{i
+autocmd FileType tex inoremap ;tab \begin{tabular}<++>\end{tabular}<++>4kA{}i
+autocmd FileType tex inoremap ;ot \begin{tableau}\inp{<++>}\const{<++>}<++><++>\end{tableau}<++>5kA{}i
+autocmd FileType tex inoremap ;can \cand{}<++>T{i
+autocmd FileType tex inoremap ;con \const{}<++>T{i
+autocmd FileType tex inoremap ;v \vio{}<++>T{i
+autocmd FileType tex inoremap ;a \href{}{<++>}<++>2T{i
+autocmd FileType tex inoremap ;sc \textsc{}<++>T{i
+autocmd FileType tex inoremap ;sec \section{}<++>2kf}i
+autocmd FileType tex inoremap ;ssec \subsection{}<++>2kf}i
+autocmd FileType tex inoremap ;sssec \subsubsection{}<++>2kf}i
+autocmd FileType tex inoremap ;st F{i*f}i
+autocmd FileType tex inoremap ;beg \begin{%DELRN%}<++>\end{%DELRN%}<++>4kfR:MultipleCursorsFind%DELRN%c
+"autocmd FileType tex inoremap ;up \usepackage{}i
+autocmd FileType tex inoremap ;up /usepackageo\usepackage{}i
+autocmd FileType tex nnoremap ;up /usepackageo\usepackage{}i
+autocmd FileType tex inoremap ;tt \texttt{}<++>T{i
+autocmd FileType tex inoremap ;bt {\blindtext}
+autocmd FileType tex inoremap ;nu $\varnothing$
+autocmd FileType tex inoremap ;col \begin{columns}[T]\begin{column}{.5\textwidth}\end{column}\begin{column}{.5\textwidth}<++>\end{column}\end{columns}5kA
+autocmd FileType tex inoremap ;rn (\ref{})<++>F}i
+"""END
+
+"""Logical Symbols
+autocmd FileType tex inoremap ;m $$<++>2T$i
+autocmd FileType tex inoremap ;M $$$$<++>2k$hi
+autocmd FileType tex inoremap ;neg {\neg}
+autocmd FileType tex inoremap ;V {\vee}
+autocmd FileType tex inoremap ;or {\vee}
+autocmd FileType tex inoremap ;L {\wedge}
+autocmd FileType tex inoremap ;and {\wedge}
+autocmd FileType tex inoremap ;ra {\rightarrow}
+autocmd FileType tex inoremap ;la {\leftarrow}
+autocmd FileType tex inoremap ;lra {\leftrightarrow}
+autocmd FileType tex inoremap ;fa {\forall}
+autocmd FileType tex inoremap ;ex {\exists}
+autocmd FileType tex inoremap ;dia {\Diamond}
+autocmd FileType tex inoremap ;box {\Box}
+"""END
+
+autocmd Filetype tex inoremap ;nom {\textsc{nom}}
+autocmd FileType tex inoremap ;acc {\textsc{acc}}
+autocmd FileType tex inoremap ;dat {\textsc{dat}}
+autocmd FileType tex inoremap ;gen {\textsc{gen}}
+autocmd FileType tex inoremap ;abl {\textsc{abl}}
+autocmd FileType tex inoremap ;voc {\textsc{voc}}
+autocmd FileType tex inoremap ;loc {\textsc{loc}}
+autocmd Filetype tex inoremap ;inst {\textsc{inst}}
+"autocmd FileType tex inoremap ;
+
+"""IPA
+autocmd FileType tex inoremap ;tipa \textipa{}<++>T{i
+autocmd FileType tex inoremap ;ae {\ae}
+autocmd FileType tex inoremap ;A {\textscripta}
+autocmd FileType tex inoremap ;dh {\dh}
+autocmd FileType tex inoremap ;yogh {\textyogh}
+autocmd FileType tex inoremap ;j {\textdyoghlig}
+autocmd FileType tex inoremap ;uh {\textschwa}
+autocmd FileType tex inoremap ;eps {\textepsilon}
+autocmd FileType tex inoremap ;gam {\textgamma}
+autocmd FileType tex inoremap ;I {\textsci}
+autocmd FileType tex inoremap ;sh {\textesh}
+autocmd FileType tex inoremap ;th {\texttheta}
+autocmd FileType tex inoremap ;Th {\textthorn}
+autocmd FileType tex inoremap ;TH {\textthorn}
+autocmd FileType tex inoremap ;ups {\textupsilon}
+autocmd FileType tex inoremap ;ph {\textphi}
+autocmd FileType tex inoremap ;om {\textomega}
+autocmd FileType tex inoremap ;sig {\textsigma}
+autocmd FileType tex inoremap ;oe {\oe}
+autocmd FileType tex inoremap ;ng {\ng}
+autocmd FileType tex inoremap ;au {\textopeno}
+autocmd FileType tex inoremap ;O {\textopeno}
+autocmd FileType tex inoremap ;glot {\textglotstop}
+autocmd FileType tex inoremap ;ch {\textteshlig}
+"""END
+
+"""HTML
+autocmd FileType html inoremap ;b <++>FbT>i
+autocmd FileType html inoremap ;i <++>FeT>i
+autocmd FileType html inoremap ;1 <++>2kf<++>2kf<++>2kf
<++>02kf>a
+autocmd FileType html inoremap ;a href=""><++><++>F"i
+autocmd FileType html inoremap ;ul <++>03kfoF>a
+autocmd FileType html inoremap ;ol
<++>03kfauthor="<++>",year="<++>",title="<++>",journal="<++>",volume="<++>",pages="<++>",}<++>8kA,i
+autocmd FileType bib inoremap ;b @book{author="<++>",year="<++>",title="<++>",publisher="<++>",}<++>6kA,i
+autocmd FileType bib inoremap ;c @incollection{author="<++>",title="<++>",booktitle="<++>",editor="<++>",year="<++>",publisher="<++>",}<++>8kA,i
+"""END
+
+let g:instant_markdown_autostart = 0
+
+autocmd Filetype markdown inoremap ;n ---
+autocmd Filetype markdown inoremap ;b ****<++>F*hi
+autocmd Filetype markdown inoremap ;s ~~~~<++>F~hi
+autocmd Filetype markdown inoremap ;e **<++>F*i
+autocmd Filetype markdown inoremap ;h ====<++>F=hi
+autocmd Filetype markdown inoremap ;i <++>F[a
+autocmd Filetype markdown inoremap ;a [](<++>)<++>F[a
+autocmd Filetype markdown inoremap ;1 #<++>kA
+autocmd Filetype markdown inoremap ;2 ##<++>kA
+autocmd Filetype markdown inoremap ;3 ###<++>kA
+autocmd Filetype markdown inoremap ;l --------
+autocmd Filetype markdown nnoremap :!pandoc-tbeamer-s%-o%pdf-Vtheme:Copenhagen--latex-engine=xelatex
+
+
+syntax on
+
+"au BufWinLeave * mkview
+"au BufWinEnter * silent loadview
+
+colorscheme slate
+
+let g:vimwiki_ext2syntax = {'.md': 'markdown', '.markdown': 'markdown', '.mdown': 'markdown'}
+"Spell-check set to F6
+map :setlocal spell! spelllang=en_us
+map :Goyo
+
+set wildmode=longest,list,full
+set wildmenu
diff --git a/.config/Scripts/wall_looper.sh b/.config/Scripts/wall_looper.sh
new file mode 100644
index 00000000..a1a300b5
--- /dev/null
+++ b/.config/Scripts/wall_looper.sh
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+#Feed this script two arguments, $1 for a folder directory and $2 for a time interval in seconds.
+
+#Will loop thru the files in the directory, using feh to change the background every $2 seconds.
+
+while [ 1==1 ]
+
+do
+for file in $1*;
+do
+sleep $2
+feh --bg-scale "$file"
+cp "$file" ~/.config/wall.png
+done
+done
diff --git a/.config/calcurse/conf b/.config/calcurse/conf
new file mode 100644
index 00000000..e829390b
--- /dev/null
+++ b/.config/calcurse/conf
@@ -0,0 +1,25 @@
+appearance.calendarview=monthly
+appearance.compactpanels=no
+appearance.defaultpanel=calendar
+appearance.layout=1
+appearance.notifybar=yes
+appearance.sidebarwidth=1
+appearance.theme=red on default
+appearance.todoview=show-completed
+daemon.enable=no
+daemon.log=no
+format.inputdate=1
+format.notifydate=%a %F
+format.notifytime=%T
+format.outputdate=%D
+general.autogc=no
+general.autosave=yes
+general.confirmdelete=yes
+general.confirmquit=no
+general.firstdayofweek=sunday
+general.periodicsave=0
+general.progressbar=yes
+general.systemdialogs=no
+notification.command=printf '\a'
+notification.notifyall=flagged-only
+notification.warning=300
diff --git a/.config/calcurse/keys b/.config/calcurse/keys
new file mode 100644
index 00000000..f188702a
--- /dev/null
+++ b/.config/calcurse/keys
@@ -0,0 +1,70 @@
+#
+# Calcurse keys configuration file
+#
+# This file sets the keybindings used by Calcurse.
+# Lines beginning with "#" are comments, and ignored by Calcurse.
+# To assign a keybinding to an action, this file must contain a line
+# with the following syntax:
+#
+# ACTION KEY1 KEY2 ... KEYn
+#
+# Where ACTION is what will be performed when KEY1, KEY2, ..., or KEYn
+# will be pressed.
+#
+# To define bindings which use the CONTROL key, prefix the key with 'C-'.
+# The escape, space bar and horizontal Tab key can be specified using
+# the 'ESC', 'SPC' and 'TAB' keyword, respectively.
+# Arrow keys can also be specified with the UP, DWN, LFT, RGT keywords.
+# Last, Home and End keys can be assigned using 'KEY_HOME' and 'KEY_END'
+# keywords.
+#
+# A description of what each ACTION keyword is used for is available
+# from calcurse online configuration menu.
+
+generic-cancel ESC
+generic-select SPC
+generic-credits @
+generic-help ?
+generic-quit q Q
+generic-save s S ^S
+generic-reload R
+generic-copy c
+generic-paste p ^V
+generic-change-view TAB
+generic-import i I
+generic-export x X
+generic-goto g G
+generic-other-cmd o O
+generic-config-menu C
+generic-redraw ^R
+generic-add-appt ^A
+generic-add-todo ^T
+generic-prev-day T ^H
+generic-next-day t ^L
+generic-prev-week W ^K
+generic-next-week w ^J
+generic-prev-month M
+generic-next-month m
+generic-prev-year Y
+generic-next-year y
+generic-scroll-down ^N
+generic-scroll-up ^P
+generic-goto-today ^G
+generic-command :
+move-right l L RGT
+move-left h H LFT
+move-down j J DWN
+move-up k K UP
+start-of-week 0
+end-of-week $
+add-item a A
+del-item d D
+edit-item e E
+view-item v V
+pipe-item |
+flag-item !
+repeat r
+edit-note n N
+view-note >
+raise-priority +
+lower-priority -
diff --git a/.config/compton.conf b/.config/compton.conf
new file mode 100644
index 00000000..8ad5733c
--- /dev/null
+++ b/.config/compton.conf
@@ -0,0 +1,56 @@
+# vim: filetype=conf
+backend = "glx";
+paint-on-overlay = true;
+glx-no-stencil = true;
+glx-no-rebind-pixmap = true;
+vsync = "opengl-swc";
+
+# These are important. The first one enables the opengl backend. The last one is the vsync method. Depending on the driver you might need to use a different method.
+# The other options are smaller performance tweaks that work well in most cases.
+# You can find the rest of the options here: https://github.com/chjj/compton/wiki/perf-guide, and here: https://github.com/chjj/compton/wiki/vsync-guide
+
+
+# Shadow
+shadow = true; # Enabled client-side shadows on windows.
+no-dock-shadow = true; # Avoid drawing shadows on dock/panel windows.
+no-dnd-shadow = true; # Don't draw shadows on DND windows.
+clear-shadow = true; # Zero the part of the shadow's mask behind the window (experimental).
+shadow-radius = 7; # The blur radius for shadows. (default 12)
+shadow-offset-x = -7; # The left offset for shadows. (default -15)
+shadow-offset-y = -7; # The top offset for shadows. (default -15)
+shadow-exclude = [
+ "! name~=''",
+ "n:e:Notification",
+ "n:e:Plank",
+ "n:e:Docky",
+ "g:e:Synapse",
+ "g:e:Kupfer",
+ "g:e:WC_Cronograph",
+ "g:e:Conky",
+ "n:w:*Firefox*",
+ "n:w:*Chrome*",
+ "n:w:*Chromium*",
+ "class_g ?= 'Notify-osd'",
+ "class_g ?= 'Cairo-dock'",
+ "class_g ?= 'Xfce4-notifyd'",
+ "class_g ?= 'Xfce4-power-manager'"
+];
+
+# The shadow exclude options are helpful if you have shadows enabled. Due to the way compton draws its shadows, certain applications will have visual glitches
+# (most applications are fine, only apps that do weird things with xshapes or argb are affected).
+# This list includes all the affected apps I found in my testing. The "! name~=''" part excludes shadows on any "Unknown" windows, this prevents a visual glitch with the XFWM alt tab switcher.
+
+# Fading
+fading = false; # Fade windows during opacity changes.
+fade-delta = 4; # The time between steps in a fade in milliseconds. (default 10).
+fade-in-step = 0.03; # Opacity change between steps while fading in. (default 0.028).
+fade-out-step = 0.03; # Opacity change between steps while fading out. (default 0.03).
+#no-fading-openclose = true; # Fade windows in/out when opening/closing
+
+detect-client-opacity = true; # This prevents opacity being ignored for some apps. For example without this enabled my xfce4-notifyd is 100% opacity no matter what.
+
+# Window type settings
+wintypes:
+{
+ tooltip = { fade = true; shadow = false; };
+};
diff --git a/.config/feh/keys b/.config/feh/keys
new file mode 100755
index 00000000..b5e38725
--- /dev/null
+++ b/.config/feh/keys
@@ -0,0 +1,12 @@
+zoom_in K XF86Forward
+zoom_out J XF86Back
+next_img L space
+prev_img BackSpace H
+
+scroll_up k Up
+scroll_down j Down
+scroll_right l Right
+scroll_left h Left
+
+toggle_fullscreen f
+save_filelist F
diff --git a/.config/fontconfig/fonts.conf b/.config/fontconfig/fonts.conf
new file mode 100755
index 00000000..759bba48
--- /dev/null
+++ b/.config/fontconfig/fonts.conf
@@ -0,0 +1,24 @@
+
+
+
+
+
+ serif
+ Tinos
+
+
+ sans-serif
+ Arimo
+
+
+ sans
+ Arimo
+
+
+ monospace
+ Tamsyn
+
+
+
+
+
diff --git a/.config/i3/bar/.codeclimate.yml b/.config/i3/bar/.codeclimate.yml
new file mode 100644
index 00000000..ec808744
--- /dev/null
+++ b/.config/i3/bar/.codeclimate.yml
@@ -0,0 +1,15 @@
+engines:
+ duplication:
+ enabled: true
+ config:
+ languages:
+ - python
+ fixme:
+ enabled: true
+ radon:
+ enabled: true
+ratings:
+ paths:
+ - "**.py"
+exclude_paths:
+- tests/
diff --git a/.config/i3/bar/.coveragerc b/.config/i3/bar/.coveragerc
new file mode 100644
index 00000000..e8097148
--- /dev/null
+++ b/.config/i3/bar/.coveragerc
@@ -0,0 +1,10 @@
+[run]
+omit =
+ tests/*
+ *mock*
+ *funcsigs*
+ *pbr*
+ *six*
+ /usr/lib*
+
+[report]
diff --git a/.config/i3/bar/.gitignore b/.config/i3/bar/.gitignore
new file mode 100644
index 00000000..d7d9fd4a
--- /dev/null
+++ b/.config/i3/bar/.gitignore
@@ -0,0 +1,91 @@
+*swp
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# IPython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
diff --git a/.config/i3/bar/.travis.yml b/.config/i3/bar/.travis.yml
new file mode 100644
index 00000000..0e198adb
--- /dev/null
+++ b/.config/i3/bar/.travis.yml
@@ -0,0 +1,18 @@
+language: python
+python:
+ - "2.7"
+ - "3.3"
+ - "3.4"
+ - "3.5"
+ - "3.6"
+install:
+ - pip install psutil
+ - pip install netifaces
+ - pip install -U coverage==4.3
+ - pip install codeclimate-test-reporter
+script:
+ - nosetests -v --with-coverage --cover-erase tests/
+ - CODECLIMATE_REPO_TOKEN=40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf codeclimate-test-reporter
+addons:
+ code_climate:
+ repo_token: 40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf
diff --git a/.config/i3/bar/LICENSE b/.config/i3/bar/LICENSE
new file mode 100644
index 00000000..b472f2e8
--- /dev/null
+++ b/.config/i3/bar/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 tobi-wan-kenobi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/.config/i3/bar/README.md b/.config/i3/bar/README.md
new file mode 100644
index 00000000..3570c337
--- /dev/null
+++ b/.config/i3/bar/README.md
@@ -0,0 +1,134 @@
+# bumblebee-status
+
+[](https://travis-ci.org/tobi-wan-kenobi/bumblebee-status)
+[](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
+[](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/coverage)
+[](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
+
+bumblebee-status is a modular, theme-able status line generator for the [i3 window manager](https://i3wm.org/).
+
+Focus is on:
+* Ease of use (no configuration files!)
+* Theme support
+* Extensibility (of course...)
+
+I hope you like it and appreciate any kind of feedback: Bug reports, Feature requests, etc. :)
+
+Thanks a lot!
+
+Supported Python versions: 2.7, 3.3, 3.4, 3.5, 3.6
+
+Explicitly unsupported Python versions: 3.2 (missing unicode literals)
+
+# Documentation
+See [the wiki](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki) for documentation.
+
+Other resources:
+
+* A list of [available modules](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/Available-Modules)
+* [How to write a theme](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-theme)
+* [How to write a module](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-module)
+
+# Installation
+```
+$ git clone git://github.com/tobi-wan-kenobi/bumblebee-status
+```
+
+# Usage
+## Normal usage
+Next, open your i3wm configuration and modify the *status_command* for your i3bar like this:
+
+```
+bar {
+ status_command = -m -p -t
+}
+```
+
+You can retrieve a list of modules and themes by entering:
+```
+$ cd bumblebee-status
+$ ./bumblebee-status -l themes
+$ ./bumblebee-status -l modules
+```
+
+To change the update interval, use:
+```
+$ ./bumblebee-status -m -p interval=
+```
+
+As a simple example, this is what my i3 configuration looks like:
+
+```
+bar {
+ font pango:Inconsolata 10
+ position top
+ tray_output none
+ status_command ~/.i3/bumblebee-status/bumblebee-status -m nic disk:root cpu memory battery date time pasink pasource dnf -p root.path=/ time.format="%H:%M CW %V" date.format="%a, %b %d %Y" -t solarized-powerline
+}
+
+```
+
+Restart i3wm and - that's it!
+
+## Errors
+If errors occur, you should see them in the i3bar itself. If that does not work, or you need more information for troubleshooting, you can activate a debug log using the `-d` or `--debug` switch:
+
+```
+$ ./bumblebee-status -d -m
+```
+
+This will create a file called `debug.log` in the same directory as the executable `bumblebee-status`.
+
+# Required Modules
+
+Modules and commandline utilities are only required for modules, the core itself has no external dependencies at all.
+
+* psutil (for the modules 'cpu', 'memory', 'traffic')
+* netifaces (for the module 'nic', 'traffic')
+* requests (for the module 'weather')
+
+# Required commandline utilities
+
+* xbacklight (for the module 'brightness')
+* xset (for the module 'caffeine')
+* notify-send (for the module 'caffeine')
+* cmus-remote (for the module 'cmus')
+* dnf (for the module 'dnf')
+* gpmdp-remote (for the module 'gpmdp')
+* setxkbmap (for the module 'layout')
+* fakeroot (for the module 'pacman')
+* pacman (for the module 'pacman')
+* pactl (for the module 'pulseaudio')
+* ping (for the module 'ping')
+* redshift (for the module 'redshift')
+* xrandr (for the module 'xrandr')
+* sensors (for the module 'sensors')
+
+# Examples
+Here are some screenshots for all themes that currently exist:
+
+:exclamation: Some themes (all 'Powerline' themes) require [Font Awesome](http://fontawesome.io/) and a powerline-compatible font ([powerline-fonts](https://github.com/powerline/fonts), for example) to display all icons correctly.
+
+Gruvbox Powerline (`-t gruvbox-powerline`) (contributed by [@paxy97](https://github.com/paxy97)):
+
+
+
+Solarized Powerline (`-t solarized-powerline`):
+
+
+
+Gruvbox (`-t gruvbox`):
+
+
+
+Solarized (`-t solarized`):
+
+
+
+Powerline (`-t powerline`):
+
+
+
+Default (nothing or `-t default`):
+
+
diff --git a/.config/i3/bar/bin/load-i3-bars.sh b/.config/i3/bar/bin/load-i3-bars.sh
new file mode 100755
index 00000000..cbe0e952
--- /dev/null
+++ b/.config/i3/bar/bin/load-i3-bars.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+if [ ! -f ~/.i3/config.template ]; then
+ cp ~/.i3/config ~/.i3/config.template
+else
+ cp ~/.i3/config.template ~/.i3/config
+fi
+
+screens=$(xrandr -q|grep ' connected'| grep -P '\d+x\d+' |cut -d' ' -f1)
+
+echo "screens: $screens"
+
+while read -r line; do
+ screen=$(echo $line | cut -d' ' -f1)
+ others=$(echo $screens|tr ' ' '\n'|grep -v $screen|tr '\n' '-'|sed 's/.$//')
+
+ if [ -f ~/.i3/config.$screen-$others ]; then
+ cat ~/.i3/config.$screen-$others >> ~/.i3/config
+ else
+ if [ -f ~/.i3/config.$screen ]; then
+ cat ~/.i3/config.$screen >> ~/.i3/config
+ fi
+ fi
+done <<< "$screens"
+
+i3-msg restart
diff --git a/.config/i3/bar/bin/pacman-updates b/.config/i3/bar/bin/pacman-updates
new file mode 100755
index 00000000..baf0b93b
--- /dev/null
+++ b/.config/i3/bar/bin/pacman-updates
@@ -0,0 +1,22 @@
+#!/usr/bin/bash
+
+if ! type -P fakeroot >/dev/null; then
+ error 'Cannot find the fakeroot binary.'
+ exit 1
+fi
+
+if [[ -z $CHECKUPDATES_DB ]]; then
+ CHECKUPDATES_DB="${TMPDIR:-/tmp}/checkup-db-${USER}/"
+fi
+
+trap 'rm -f $CHECKUPDATES_DB/db.lck' INT TERM EXIT
+
+DBPath="${DBPath:-/var/lib/pacman/}"
+eval $(awk -F' *= *' '$1 ~ /DBPath/ { print $1 "=" $2 }' /etc/pacman.conf)
+
+mkdir -p "$CHECKUPDATES_DB"
+ln -s "${DBPath}/local" "$CHECKUPDATES_DB" &> /dev/null
+fakeroot -- pacman -Sy --dbpath "$CHECKUPDATES_DB" --logfile /dev/null &> /dev/null
+fakeroot pacman -Su -p --dbpath "$CHECKUPDATES_DB"
+
+exit 0
diff --git a/.config/i3/bar/bin/toggle-display.sh b/.config/i3/bar/bin/toggle-display.sh
new file mode 100755
index 00000000..bd13a298
--- /dev/null
+++ b/.config/i3/bar/bin/toggle-display.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+echo $(dirname $(readlink -f "$0"))
+
+i3bar_update=$(dirname $(readlink -f "$0"))/load-i3-bars.sh
+
+xrandr "$@"
+
+if [ -f $i3bar_update ]; then
+ sleep 1
+ $i3bar_update
+fi
diff --git a/.config/i3/bar/bumblebee-status b/.config/i3/bar/bumblebee-status
new file mode 100755
index 00000000..582f3148
--- /dev/null
+++ b/.config/i3/bar/bumblebee-status
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import logging
+import bumblebee.theme
+import bumblebee.engine
+import bumblebee.config
+import bumblebee.output
+import bumblebee.input
+import bumblebee.modules.error
+
+def main():
+ config = bumblebee.config.Config(sys.argv[1:])
+
+ if config.debug():
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format="[%(asctime)s] %(levelname)-8s %(message)s",
+ filename=os.path.expanduser(config.logfile())
+ )
+ else:
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format="[%(asctime)s] %(levelname)-8s %(message)s",
+ stream=sys.stderr
+ )
+
+ theme = bumblebee.theme.Theme(config.theme())
+ output = bumblebee.output.I3BarOutput(theme=theme)
+ inp = bumblebee.input.I3BarInput()
+ engine = None
+
+ try:
+ engine = bumblebee.engine.Engine(
+ config=config,
+ output=output,
+ inp=inp,
+ )
+ engine.run()
+ except KeyboardInterrupt as error:
+ inp.stop()
+ sys.exit(0)
+ except BaseException as e:
+ logging.exception(e)
+ if output.started():
+ output.flush()
+ output.end()
+ else:
+ output.start()
+ import time
+ while True:
+ output.begin()
+ error = bumblebee.modules.error.Module(engine, config)
+ error.set("exception occurred: {}".format(e))
+ widget = error.widgets()[0]
+ widget.link_module(error)
+ output.draw(widget, error)
+ output.flush()
+ output.end()
+ time.sleep(1)
+
+if __name__ == "__main__":
+ main()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/__init__.py b/.config/i3/bar/bumblebee/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/.config/i3/bar/bumblebee/config.py b/.config/i3/bar/bumblebee/config.py
new file mode 100644
index 00000000..4e746bd6
--- /dev/null
+++ b/.config/i3/bar/bumblebee/config.py
@@ -0,0 +1,99 @@
+"""Configuration handling
+
+This module provides configuration information (loaded modules,
+module parameters, etc.) to all other components
+"""
+
+import os
+import sys
+import logging
+import argparse
+import textwrap
+import importlib
+import bumblebee.store
+
+MODULE_HELP = "Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use : to provide an alias in case you want to load the same module multiple times, but specify different parameters."
+THEME_HELP = "Specify the theme to use for drawing modules"
+PARAMETER_HELP = "Provide configuration parameters in the form of .="
+LIST_HELP = "Display a list of either available themes or available modules along with their parameters."
+DEBUG_HELP = "Enable debug log ('debug.log' located in the same directory as the bumblebee-status executable)"
+
+class print_usage(argparse.Action):
+ def __init__(self, option_strings, dest, nargs=None, **kwargs):
+ argparse.Action.__init__(self, option_strings, dest, nargs, **kwargs)
+ self._indent = " "*2
+
+ def __call__(self, parser, namespace, value, option_string=None):
+ if value == "modules":
+ self.print_modules()
+ elif value == "themes":
+ self.print_themes()
+ sys.exit(0)
+
+ def print_themes(self):
+ print(textwrap.fill(", ".join(bumblebee.theme.themes()),
+ 80, initial_indent = self._indent, subsequent_indent = self._indent
+ ))
+
+ def print_modules(self):
+ for m in bumblebee.engine.all_modules():
+ mod = importlib.import_module("bumblebee.modules.{}".format(m["name"]))
+ print(textwrap.fill("{}:".format(m["name"]), 80,
+ initial_indent=self._indent*2, subsequent_indent=self._indent*2))
+ for line in mod.__doc__.split("\n"):
+ print(textwrap.fill(line, 80,
+ initial_indent=self._indent*3, subsequent_indent=self._indent*6))
+
+def create_parser():
+ """Create the argument parser"""
+ parser = argparse.ArgumentParser(description="display system data in the i3bar")
+ parser.add_argument("-m", "--modules", nargs="+", default=[],
+ help=MODULE_HELP)
+ parser.add_argument("-t", "--theme", default="default", help=THEME_HELP)
+ parser.add_argument("-p", "--parameters", nargs="+", default=[],
+ help=PARAMETER_HELP)
+ parser.add_argument("-l", "--list", choices=["modules", "themes"], action=print_usage,
+ help=LIST_HELP)
+ parser.add_argument("-d", "--debug", action="store_true",
+ help=DEBUG_HELP)
+ parser.add_argument("-f", "--logfile", default="~/bumblebee-status-debug.log",
+ help="Location of the debug log file")
+
+ return parser
+
+class Config(bumblebee.store.Store):
+ """Top-level configuration class
+
+ Parses commandline arguments and provides non-module
+ specific configuration information.
+ """
+ def __init__(self, args=None):
+ super(Config, self).__init__()
+ parser = create_parser()
+ self._args = parser.parse_args(args if args else [])
+
+ if not self._args.debug:
+ logger = logging.getLogger().disabled = True
+
+ for param in self._args.parameters:
+ key, value = param.split("=")
+ self.set(key, value)
+
+ def modules(self):
+ """Return a list of all activated modules"""
+ return [{
+ "module": x.split(":")[0],
+ "name": x if not ":" in x else x.split(":")[1],
+ } for x in self._args.modules]
+
+ def theme(self):
+ """Return the name of the selected theme"""
+ return self._args.theme
+
+ def debug(self):
+ return self._args.debug
+
+ def logfile(self):
+ return self._args.logfile
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/engine.py b/.config/i3/bar/bumblebee/engine.py
new file mode 100644
index 00000000..c0ea9cc6
--- /dev/null
+++ b/.config/i3/bar/bumblebee/engine.py
@@ -0,0 +1,166 @@
+"""Core application engine"""
+
+import os
+import time
+import pkgutil
+import importlib
+import bumblebee.error
+import bumblebee.modules
+
+def all_modules():
+ """Return a list of available modules"""
+ result = []
+ path = os.path.dirname(bumblebee.modules.__file__)
+ for mod in [name for _, name, _ in pkgutil.iter_modules([path])]:
+ result.append({
+ "name": mod
+ })
+ return result
+
+class Module(object):
+ """Module instance base class
+
+ Objects of this type represent the modules that
+ the user configures. Concrete module implementations
+ (e.g. CPU utilization, disk usage, etc.) derive from
+ this base class.
+ """
+ def __init__(self, engine, config={}, widgets=[]):
+ self.name = config.get("name", self.__module__.split(".")[-1])
+ self._config = config
+ self.id = self.name
+ self._widgets = []
+ if widgets:
+ self._widgets = widgets if isinstance(widgets, list) else [widgets]
+
+ def widgets(self):
+ """Return the widgets to draw for this module"""
+ return self._widgets
+
+ def widget(self, name):
+ for widget in self._widgets:
+ if widget.name == name:
+ return widget
+
+ def widget_by_id(self, uid):
+ for widget in self._widgets:
+ if widget.id == uid:
+ return widget
+ return None
+
+ def update(self, widgets):
+ """By default, update() is a NOP"""
+ pass
+
+ def update_all(self):
+ self.update(self._widgets)
+
+ def parameter(self, name, default=None):
+ """Return the config parameter 'name' for this module"""
+ name = "{}.{}".format(self.name, name)
+ return self._config["config"].get(name, default)
+
+ def threshold_state(self, value, warn, crit):
+ if value > float(self.parameter("critical", crit)):
+ return "critical"
+ if value > float(self.parameter("warning", warn)):
+ return "warning"
+ return None
+
+class Engine(object):
+ """Engine for driving the application
+
+ This class connects input/output, instantiates all
+ required modules and drives the "event loop"
+ """
+ def __init__(self, config, output=None, inp=None):
+ self._output = output
+ self._config = config
+ self._running = True
+ self._modules = []
+ self.input = inp
+ self._aliases = self._read_aliases()
+ self.load_modules(config.modules())
+
+ self.input.register_callback(None, bumblebee.input.WHEEL_UP,
+ "i3-msg workspace prev_on_output")
+ self.input.register_callback(None, bumblebee.input.WHEEL_DOWN,
+ "i3-msg workspace next_on_output")
+
+ self.input.start()
+
+ def modules(self):
+ return self._modules
+
+ def load_modules(self, modules):
+ """Load specified modules and return them as list"""
+ for module in modules:
+ mod = self._load_module(module["module"], module["name"])
+ self._modules.append(mod)
+ self._register_module_callbacks(mod)
+ return self._modules
+
+ def _register_module_callbacks(self, module):
+ buttons = [
+ { "name": "left-click", "id": bumblebee.input.LEFT_MOUSE },
+ { "name": "middle-click", "id": bumblebee.input.MIDDLE_MOUSE },
+ { "name": "right-click", "id": bumblebee.input.RIGHT_MOUSE },
+ { "name": "wheel-up", "id": bumblebee.input.WHEEL_UP },
+ { "name": "wheel-down", "id": bumblebee.input.WHEEL_DOWN },
+ ]
+ for button in buttons:
+ if module.parameter(button["name"], None):
+ self.input.register_callback(obj=module,
+ button=button["id"], cmd=module.parameter(button["name"]))
+
+ def _read_aliases(self):
+ result = {}
+ for module in all_modules():
+ mod = importlib.import_module("bumblebee.modules.{}".format(module["name"]))
+ for alias in getattr(mod, "ALIASES", []):
+ result[alias] = module["name"]
+ return result
+
+ def _load_module(self, module_name, config_name=None):
+ """Load specified module and return it as object"""
+ if module_name in self._aliases:
+ config_name is config_name if config_name else module_name
+ module_name = self._aliases[module_name]
+ if config_name is None:
+ config_name = module_name
+ try:
+ module = importlib.import_module("bumblebee.modules.{}".format(module_name))
+ except ImportError as error:
+ raise bumblebee.error.ModuleLoadError(error)
+ return getattr(module, "Module")(self, {
+ "name": config_name,
+ "config": self._config
+ })
+
+ def running(self):
+ """Check whether the event loop is running"""
+ return self._running
+
+ def stop(self):
+ """Stop the event loop"""
+ self._running = False
+
+ def run(self):
+ """Start the event loop"""
+ self._output.start()
+ while self.running():
+ self._output.begin()
+ for module in self._modules:
+ module.update(module.widgets())
+ for widget in module.widgets():
+ widget.link_module(module)
+ self._output.draw(widget=widget, module=module, engine=self)
+ self._output.flush()
+ self._output.end()
+ if self.running():
+ self.input.wait(float(self._config.get("interval", 1)))
+
+ self._output.stop()
+ self.input.stop()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/error.py b/.config/i3/bar/bumblebee/error.py
new file mode 100644
index 00000000..129f02d7
--- /dev/null
+++ b/.config/i3/bar/bumblebee/error.py
@@ -0,0 +1,15 @@
+"""Collection of all exceptions raised by this tool"""
+
+class BaseError(Exception):
+ """Base class for all exceptions generated by this tool"""
+ pass
+
+class ModuleLoadError(BaseError):
+ """Raised whenever loading a module fails"""
+ pass
+
+class ThemeLoadError(BaseError):
+ """Raised whenever loading a theme fails"""
+ pass
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/input.py b/.config/i3/bar/bumblebee/input.py
new file mode 100644
index 00000000..1678b2ef
--- /dev/null
+++ b/.config/i3/bar/bumblebee/input.py
@@ -0,0 +1,134 @@
+"""Input classes"""
+
+import sys
+import json
+import uuid
+import time
+import select
+import threading
+import bumblebee.util
+
+LEFT_MOUSE = 1
+MIDDLE_MOUSE = 2
+RIGHT_MOUSE = 3
+WHEEL_UP = 4
+WHEEL_DOWN = 5
+
+def is_terminated():
+ for thread in threading.enumerate():
+ if thread.name == "MainThread" and not thread.is_alive():
+ return True
+ return False
+
+def read_input(inp):
+ """Read i3bar input and execute callbacks"""
+ epoll = select.epoll()
+ epoll.register(sys.stdin.fileno(), select.EPOLLIN)
+ while inp.running:
+ if is_terminated():
+ return
+
+ events = epoll.poll(1)
+ for fileno, event in events:
+ line = "["
+ while "[" in line:
+ line = sys.stdin.readline().strip(",").strip()
+ inp.has_event = True
+ try:
+ event = json.loads(line)
+ if "instance" in event:
+ inp.callback(event)
+ inp.redraw()
+ except ValueError:
+ pass
+ epoll.unregister(sys.stdin.fileno())
+ epoll.close()
+ inp.has_event = True
+ inp.clean_exit = True
+
+class I3BarInput(object):
+ """Process incoming events from the i3bar"""
+ def __init__(self):
+ self.running = True
+ self._callbacks = {}
+ self.clean_exit = False
+ self.global_id = str(uuid.uuid4())
+ self.need_event = False
+ self.has_event = False
+ self._condition = threading.Condition()
+
+ def start(self):
+ """Start asynchronous input processing"""
+ self.has_event = False
+ self.running = True
+ self._condition.acquire()
+ self._thread = threading.Thread(target=read_input, args=(self,))
+ self._thread.start()
+
+ def redraw(self):
+ self._condition.acquire()
+ self._condition.notify()
+ self._condition.release()
+
+ def alive(self):
+ """Check whether the input processing is still active"""
+ return self._thread.is_alive()
+
+ def wait(self, timeout):
+ self._condition.wait(timeout)
+
+ def _wait(self):
+ while not self.has_event:
+ time.sleep(0.1)
+ self.has_event = False
+
+ def stop(self):
+ """Stop asynchronous input processing"""
+ self._condition.release()
+ if self.need_event:
+ self._wait()
+ self.running = False
+ self._thread.join()
+ return self.clean_exit
+
+ def _uuidstr(self, name, button):
+ return "{}::{}".format(name, button)
+
+ def _uid(self, obj, button):
+ uid = self.global_id
+ if obj:
+ uid = obj.id
+ return self._uuidstr(uid, button)
+
+ def deregister_callbacks(self, obj):
+ to_delete = []
+ uid = obj.id if obj else self.global_id
+ for key in self._callbacks:
+ if uid in key:
+ to_delete.append(key)
+ for key in to_delete:
+ del self._callbacks[key]
+
+ def register_callback(self, obj, button, cmd):
+ """Register a callback function or system call"""
+ uid = self._uid(obj, button)
+ if uid not in self._callbacks:
+ self._callbacks[uid] = {}
+ self._callbacks[uid] = cmd
+
+ def callback(self, event):
+ """Execute callback action for an incoming event"""
+ button = event["button"]
+
+ cmd = self._callbacks.get(self._uuidstr(self.global_id, button), None)
+ cmd = self._callbacks.get(self._uuidstr(event["name"], button), cmd)
+ cmd = self._callbacks.get(self._uuidstr(event["instance"], button), cmd)
+
+ if cmd is None:
+ return
+ if callable(cmd):
+ cmd(event)
+ else:
+ bumblebee.util.execute(cmd, False)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/__init__.py b/.config/i3/bar/bumblebee/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/.config/i3/bar/bumblebee/modules/amixer.py b/.config/i3/bar/bumblebee/modules/amixer.py
new file mode 100644
index 00000000..fa08ec33
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/amixer.py
@@ -0,0 +1,36 @@
+"""get volume level
+
+"""
+import re
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.volume)
+ )
+ self._level = "0"
+ self._muted = True
+ device = self.parameter("device", "Master,0")
+ self._cmdString = "amixer get {}".format(device)
+
+ def volume(self, widget):
+ m = re.search(r'([\d]+)\%', self._level)
+ self._muted = True
+ if m:
+ if m.group(1) != "0":
+ self._muted = False
+ return "{}%".format(m.group(1))
+ else:
+ return "0%"
+
+ def update(self, widgets):
+ self._level = bumblebee.util.execute(self._cmdString)
+
+ def state(self, widget):
+ if self._muted:
+ return [ "warning", "muted" ]
+ return [ "unmuted" ]
diff --git a/.config/i3/bar/bumblebee/modules/battery.py b/.config/i3/bar/bumblebee/modules/battery.py
new file mode 100644
index 00000000..f00c603a
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/battery.py
@@ -0,0 +1,82 @@
+# pylint: disable=C0111,R0903
+
+"""Displays battery status, remaining percentage and charging information.
+
+Parameters:
+ * battery.device : The device to read information from (defaults to BAT0)
+ * battery.warning : Warning threshold in % of remaining charge (defaults to 20)
+ * battery.critical: Critical threshold in % of remaining charge (defaults to 10)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.capacity)
+ )
+ battery = self.parameter("device", "BAT0")
+ self._path = "/sys/class/power_supply/{}".format(battery)
+ self._capacity = 100
+ self._ac = False
+
+ def capacity(self, widget):
+ if self._ac:
+ return "ac"
+ if self._capacity == -1:
+ return "n/a"
+ return "{:03d}%".format(self._capacity)
+
+ def update(self, widgets):
+ widget = widgets[0]
+ self._ac = False
+ if not os.path.exists(self._path):
+ self._ac = True
+ self._capacity = 100
+ return
+
+ try:
+ with open(self._path + "/capacity") as f:
+ self._capacity = int(f.read())
+ except IOError:
+ self._capacity = -1
+ self._capacity = self._capacity if self._capacity < 100 else 100
+
+ def state(self, widget):
+ state = []
+
+ if self._capacity < 0:
+ return ["critical", "unknown"]
+
+ if self._capacity < int(self.parameter("critical", 10)):
+ state.append("critical")
+ elif self._capacity > int(self.parameter("great", 90)):
+ state.append("great")
+ elif self._capacity > int(self.parameter("good", 80)):
+ state.append("good")
+ elif self._capacity < int(self.parameter("warning", 20)):
+ state.append("warning")
+ elif self._capacity < int(self.parameter("mid", 80)):
+ state.append("mid")
+
+ if self._ac:
+ state.append("AC")
+ else:
+ charge = ""
+ with open(self._path + "/status") as f:
+ charge = f.read().strip()
+ if charge == "Discharging":
+ state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity))))
+ else:
+ if self._capacity > 95:
+ state.append("charged")
+ else:
+ state.append("charging")
+
+ return state
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/battery.py_ori b/.config/i3/bar/bumblebee/modules/battery.py_ori
new file mode 100644
index 00000000..5384716d
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/battery.py_ori
@@ -0,0 +1,75 @@
+# pylint: disable=C0111,R0903
+
+"""Displays battery status, remaining percentage and charging information.
+
+Parameters:
+ * battery.device : The device to read information from (defaults to BAT0)
+ * battery.warning : Warning threshold in % of remaining charge (defaults to 20)
+ * battery.critical: Critical threshold in % of remaining charge (defaults to 10)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.capacity)
+ )
+ battery = self.parameter("device", "BAT0")
+ self._path = "/sys/class/power_supply/{}".format(battery)
+ self._capacity = 100
+ self._ac = False
+
+ def capacity(self, widget):
+ if self._ac:
+ return "ac"
+ if self._capacity == -1:
+ return "n/a"
+ return "{:03d}%".format(self._capacity)
+
+ def update(self, widgets):
+ self._ac = False
+ if not os.path.exists(self._path):
+ self._ac = True
+ self._capacity = 100
+ return
+
+ try:
+ with open(self._path + "/capacity") as f:
+ self._capacity = int(f.read())
+ except IOError:
+ self._capacity = -1
+ self._capacity = self._capacity if self._capacity < 100 else 100
+
+ def state(self, widget):
+ state = []
+
+ if self._capacity < 0:
+ return ["critical", "unknown"]
+
+ if self._capacity < int(self.parameter("critical", 10)):
+ state.append("critical")
+ elif self._capacity < int(self.parameter("warning", 20)):
+ state.append("warning")
+
+ if self._ac:
+ state.append("AC")
+ else:
+ charge = ""
+ with open(self._path + "/status") as f:
+ charge = f.read().strip()
+ if charge == "Discharging":
+ state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity))))
+ else:
+ if self._capacity > 95:
+ state.append("charged")
+ else:
+ state.append("charging")
+
+ return state
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/brightness.py b/.config/i3/bar/bumblebee/modules/brightness.py
new file mode 100644
index 00000000..716a734f
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/brightness.py
@@ -0,0 +1,37 @@
+# pylint: disable=C0111,R0903
+
+"""Displays the brightness of a display
+
+Requires the following executable:
+ * xbacklight
+
+Parameters:
+ * brightness.step: The amount of increase/decrease on scroll in % (defaults to 2)
+
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.brightness)
+ )
+ self._brightness = 0
+
+ step = self.parameter("step", 2)
+
+ engine.input.register_callback(self, button=bumblebee.input.WHEEL_UP,
+ cmd="xbacklight +{}%".format(step))
+ engine.input.register_callback(self, button=bumblebee.input.WHEEL_DOWN,
+ cmd="xbacklight -{}%".format(step))
+
+ def brightness(self, widget):
+ return "{:03.0f}%".format(self._brightness)
+
+ def update(self, widgets):
+ self._brightness = float(bumblebee.util.execute("xbacklight -get"))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/caffeine.py b/.config/i3/bar/bumblebee/modules/caffeine.py
new file mode 100644
index 00000000..2354ed0f
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/caffeine.py
@@ -0,0 +1,48 @@
+# pylint: disable=C0111,R0903
+
+"""Enable/disable automatic screen locking.
+
+Requires the following executables:
+ * xset
+ * notify-send
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.caffeine)
+ )
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd=self._toggle
+ )
+
+ def caffeine(self, widget):
+ return ""
+
+ def state(self, widget):
+ if self._active():
+ return "activated"
+ return "deactivated"
+
+ def _active(self):
+ for line in bumblebee.util.execute("xset q").split("\n"):
+ if "timeout" in line:
+ timeout = int(line.split(" ")[4])
+ if timeout == 0:
+ return True
+ return False
+ return False
+
+ def _toggle(self, widget):
+ if self._active():
+ bumblebee.util.execute("xset s default")
+ bumblebee.util.execute("notify-send \"Out of coffee\"")
+ else:
+ bumblebee.util.execute("xset s off")
+ bumblebee.util.execute("notify-send \"Consuming caffeine\"")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/cmus.py b/.config/i3/bar/bumblebee/modules/cmus.py
new file mode 100644
index 00000000..4bc0e56e
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/cmus.py
@@ -0,0 +1,92 @@
+# pylint: disable=C0111,R0903
+# -*- coding: utf-8 -*-
+
+"""Displays information about the current song in cmus.
+
+Requires the following executable:
+ * cmus-remote
+
+Parameters:
+ * cmus.format: Format string for the song information. Tag values can be put in curly brackets (i.e. {artist})
+"""
+
+from collections import defaultdict
+
+import string
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+from bumblebee.output import scrollable
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = [
+ bumblebee.output.Widget(name="cmus.prev"),
+ bumblebee.output.Widget(name="cmus.main", full_text=self.description),
+ bumblebee.output.Widget(name="cmus.next"),
+ bumblebee.output.Widget(name="cmus.shuffle"),
+ bumblebee.output.Widget(name="cmus.repeat"),
+ ]
+ super(Module, self).__init__(engine, config, widgets)
+
+ engine.input.register_callback(widgets[0], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -r")
+ engine.input.register_callback(widgets[1], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -u")
+ engine.input.register_callback(widgets[2], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -n")
+ engine.input.register_callback(widgets[3], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -S")
+ engine.input.register_callback(widgets[4], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -R")
+
+ self._fmt = self.parameter("format", "{artist} - {title} {position}/{duration}")
+ self._status = None
+ self._shuffle = False
+ self._repeat = False
+ self._tags = defaultdict(lambda: '')
+
+ @scrollable
+ def description(self, widget):
+ return string.Formatter().vformat(self._fmt, (), self._tags)
+
+ def update(self, widgets):
+ self._load_song()
+
+ def state(self, widget):
+ if widget.name == "cmus.shuffle":
+ return "shuffle-on" if self._shuffle else "shuffle-off"
+ if widget.name == "cmus.repeat":
+ return "repeat-on" if self._repeat else "repeat-off"
+ if widget.name == "cmus.prev":
+ return "prev"
+ if widget.name == "cmus.next":
+ return "next"
+ return self._status
+
+ def _load_song(self):
+ info = ""
+ try:
+ info = bumblebee.util.execute("cmus-remote -Q")
+ except RuntimeError:
+ pass
+ self._tags = defaultdict(lambda: '')
+ for line in info.split("\n"):
+ if line.startswith("status"):
+ self._status = line.split(" ", 2)[1]
+ if line.startswith("tag"):
+ key, value = line.split(" ", 2)[1:]
+ self._tags.update({ key: value })
+ for key in ["duration", "position"]:
+ if line.startswith(key):
+ dur = int(line.split(" ")[1])
+ self._tags.update({key:bumblebee.util.durationfmt(dur)})
+ if line.startswith("set repeat "):
+ self._repeat = False if "false" in line else True
+ if line.startswith("set shuffle "):
+ self._shuffle = False if "false" in line else True
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/cpu.py b/.config/i3/bar/bumblebee/modules/cpu.py
new file mode 100644
index 00000000..6a24bdc9
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/cpu.py
@@ -0,0 +1,37 @@
+# pylint: disable=C0111,R0903
+
+"""Displays CPU utilization across all CPUs.
+
+Parameters:
+ * cpu.warning : Warning threshold in % of CPU usage (defaults to 70%)
+ * cpu.critical: Critical threshold in % of CPU usage (defaults to 80%)
+"""
+
+try:
+ import psutil
+except ImportError:
+ pass
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.utilization)
+ )
+ self._utilization = psutil.cpu_percent(percpu=False)
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="gnome-system-monitor")
+
+ def utilization(self, widget):
+ return "{:06.02f}%".format(self._utilization)
+
+ def update(self, widgets):
+ self._utilization = psutil.cpu_percent(percpu=False)
+
+ def state(self, widget):
+ return self.threshold_state(self._utilization, 70, 80)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/datetime.py b/.config/i3/bar/bumblebee/modules/datetime.py
new file mode 100644
index 00000000..4141a53f
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/datetime.py
@@ -0,0 +1,35 @@
+# pylint: disable=C0111,R0903
+
+"""Displays the current date and time.
+
+Parameters:
+ * datetime.format: strftime()-compatible formatting string
+ * date.format : alias for datetime.format
+ * time.format : alias for datetime.format
+"""
+
+from __future__ import absolute_import
+import datetime
+import bumblebee.engine
+
+ALIASES = [ "date", "time" ]
+
+def default_format(module):
+ default = "%x %X"
+ if module == "date":
+ default = "%x"
+ if module == "time":
+ default = "%X"
+ return default
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.get_time)
+ )
+ self._fmt = self.parameter("format", default_format(self.name))
+
+ def get_time(self, widget):
+ return datetime.datetime.now().strftime(self._fmt)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/disk.py b/.config/i3/bar/bumblebee/modules/disk.py
new file mode 100644
index 00000000..1f53bda3
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/disk.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0111,R0903
+
+"""Shows free diskspace, total diskspace and the percentage of free disk space.
+
+Parameters:
+ * disk.warning: Warning threshold in % of disk space (defaults to 80%)
+ * disk.critical: Critical threshold in % of disk space (defaults ot 90%)
+ * disk.path: Path to calculate disk usage from (defaults to /)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.diskspace)
+ )
+ self._path = self.parameter("path", "/")
+ self._perc = 0
+ self._used = 0
+ self._size = 0
+
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="termite -e ranger {}".format(self._path))
+
+ def diskspace(self, widget):
+ #return "{} {}/{} ({:05.02f}%)".format(self._path,
+ #bumblebee.util.bytefmt(self._used),
+ #bumblebee.util.bytefmt(self._size), self._perc
+ #)
+ #return "{} {:05.02f}%".format(self._path, self._perc)
+ return "{}".format(bumblebee.util.bytefmt(self._size - self._used))
+
+ def update(self, widgets):
+ st = os.statvfs(self._path)
+ self._size = st.f_frsize*st.f_blocks
+ self._used = self._size - st.f_frsize*st.f_bavail
+ self._perc = 100.0*self._used/self._size
+
+ def state(self, widget):
+ return self.threshold_state(self._perc, 90, 95)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/disk.py_ori b/.config/i3/bar/bumblebee/modules/disk.py_ori
new file mode 100644
index 00000000..aee40ac5
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/disk.py_ori
@@ -0,0 +1,45 @@
+# pylint: disable=C0111,R0903
+
+"""Shows free diskspace, total diskspace and the percentage of free disk space.
+
+Parameters:
+ * disk.warning: Warning threshold in % of disk space (defaults to 80%)
+ * disk.critical: Critical threshold in % of disk space (defaults ot 90%)
+ * disk.path: Path to calculate disk usage from (defaults to /)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.diskspace)
+ )
+ self._path = self.parameter("path", "/")
+ self._perc = 0
+ self._used = 0
+ self._size = 0
+
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="nautilus {}".format(self._path))
+
+ def diskspace(self, widget):
+ return "{} {}/{} ({:05.02f}%)".format(self._path,
+ bumblebee.util.bytefmt(self._used),
+ bumblebee.util.bytefmt(self._size), self._perc
+ )
+
+ def update(self, widgets):
+ st = os.statvfs(self._path)
+ self._size = st.f_blocks * st.f_frsize
+ self._used = (st.f_blocks - st.f_bfree) * st.f_frsize
+ self._perc = 100.0*self._used/self._size
+
+ def state(self, widget):
+ return self.threshold_state(self._perc, 80, 90)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/dnf.py b/.config/i3/bar/bumblebee/modules/dnf.py
new file mode 100644
index 00000000..f36dfa9a
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/dnf.py
@@ -0,0 +1,80 @@
+# pylint: disable=C0111,R0903
+
+"""Displays DNF package update information (///)
+
+Requires the following executable:
+ * dnf
+
+Parameters:
+ * dnf.interval: Time in seconds between two consecutive update checks (defaulst to 1800)
+
+"""
+
+import time
+import threading
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+def get_dnf_info(widget):
+ try:
+ res = bumblebee.util.execute("dnf updateinfo")
+ except RuntimeError:
+ pass
+
+ security = 0
+ bugfixes = 0
+ enhancements = 0
+ other = 0
+ for line in res.split("\n"):
+
+ if not line.startswith(" "): continue
+ elif "ecurity" in line:
+ for s in line.split():
+ if s.isdigit(): security += int(s)
+ elif "ugfix" in line:
+ for s in line.split():
+ if s.isdigit(): bugfixes += int(s)
+ elif "hancement" in line:
+ for s in line.split():
+ if s.isdigit(): enhancements += int(s)
+ else:
+ for s in line.split():
+ if s.isdigit(): other += int(s)
+
+ widget.set("security", security)
+ widget.set("bugfixes", bugfixes)
+ widget.set("enhancements", enhancements)
+ widget.set("other", other)
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widget = bumblebee.output.Widget(full_text=self.updates)
+ super(Module, self).__init__(engine, config, widget)
+ self._next_check = 0
+
+ def updates(self, widget):
+ result = []
+ for t in ["security", "bugfixes", "enhancements", "other"]:
+ result.append(str(widget.get(t, 0)))
+ return "/".join(result)
+
+ def update(self, widgets):
+ if int(time.time()) < self._next_check:
+ return
+ thread = threading.Thread(target=get_dnf_info, args=(widgets[0],))
+ thread.start()
+ self._next_check = int(time.time()) + self.parameter("interval", 30*60)
+
+ def state(self, widget):
+ cnt = 0
+ for t in ["security", "bugfixes", "enhancements", "other"]:
+ cnt += widget.get(t, 0)
+ if cnt == 0:
+ return "good"
+ if cnt > 50 or widget.get("security", 0) > 0:
+ return "critical"
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/error.py b/.config/i3/bar/bumblebee/modules/error.py
new file mode 100644
index 00000000..16d3834f
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/error.py
@@ -0,0 +1,26 @@
+# pylint: disable=C0111,R0903
+
+"""Draws an error widget.
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.text)
+ )
+ self._text = ""
+
+ def set(self, text):
+ self._text = text
+
+ def text(self, widget):
+ return self._text
+
+ def state(self, widget):
+ return ["critical"]
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/gpmdp.py b/.config/i3/bar/bumblebee/modules/gpmdp.py
new file mode 100644
index 00000000..36988e86
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/gpmdp.py
@@ -0,0 +1,58 @@
+# pylint: disable=C0111,R0903
+
+"""Displays information about the current song in Google Play music player.
+
+Requires the following executable:
+ * gpmdp-remote
+"""
+
+import string
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = [
+ bumblebee.output.Widget(name="gpmdp.prev"),
+ bumblebee.output.Widget(name="gpmdp.main", full_text=self.description),
+ bumblebee.output.Widget(name="gpmdp.next"),
+ ]
+ super(Module, self).__init__(engine, config, widgets)
+
+ engine.input.register_callback(widgets[0], button=bumblebee.input.LEFT_MOUSE,
+ cmd="playerctl previous")
+ engine.input.register_callback(widgets[1], button=bumblebee.input.LEFT_MOUSE,
+ cmd="playerctl play-pause")
+ engine.input.register_callback(widgets[2], button=bumblebee.input.LEFT_MOUSE,
+ cmd="playerctl next")
+
+ self._status = None
+ self._tags = None
+
+ def description(self, widget):
+ return self._tags if self._tags else "n/a"
+
+ def update(self, widgets):
+ self._load_song()
+
+ def state(self, widget):
+ if widget.name == "gpmdp.prev":
+ return "prev"
+ if widget.name == "gpmdp.next":
+ return "next"
+ return self._status
+
+ def _load_song(self):
+ info = ""
+ try:
+ info = bumblebee.util.execute("gpmdp-remote current")
+ status = bumblebee.util.execute("gpmdp-remote status")
+ except RuntimeError:
+ pass
+ self._status = status.split("\n")[0].lower()
+ self._tags = info.split("\n")[0]
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/kernel.py b/.config/i3/bar/bumblebee/modules/kernel.py
new file mode 100644
index 00000000..761fa8c0
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/kernel.py
@@ -0,0 +1,21 @@
+# pylint: disable=C0111,R0903
+
+"""Shows Linux kernel version information"""
+
+import platform
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.output)
+ )
+ self._release_name = platform.release()
+
+ def output(self, widget):
+ return self._release_name
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/layout.py b/.config/i3/bar/bumblebee/modules/layout.py
new file mode 100644
index 00000000..d9daadbb
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/layout.py
@@ -0,0 +1,74 @@
+# pylint: disable=C0111,R0903
+
+"""Displays and changes the current keyboard layout
+
+Requires the following executable:
+ * setxkbmap
+
+Parameters:
+ * layout.lang: pipe-separated list of languages to cycle through (e.g. us|rs|de). Default: en
+"""
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.layout)
+ )
+ self._languages = self.parameter("lang", "us").split("|")
+ self._idx = 0
+
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd=self._next_keymap)
+ engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE,
+ cmd=self._prev_keymap)
+
+ def _next_keymap(self, event):
+ self._idx = (self._idx + 1) % len(self._languages)
+ self._set_keymap()
+
+ def _prev_keymap(self, event):
+ self._idx = self._idx - 1 if self._idx > 0 else len(self._languages) - 1
+ self._set_keymap()
+
+ def _set_keymap(self):
+ tmp = self._languages[self._idx].split(":")
+ layout = tmp[0]
+ variant = ""
+ if len(tmp) > 1:
+ variant = "-variant {}".format(tmp[1])
+ try:
+ bumblebee.util.execute("setxkbmap -layout {} {}".format(layout, variant))
+ except RuntimeError:
+ pass
+
+ def layout(self, widget):
+ try:
+ res = bumblebee.util.execute("setxkbmap -query")
+ except RuntimeError:
+ return "n/a"
+ layout = ""
+ variant = None
+ for line in res.split("\n"):
+ if not line:
+ continue
+ if "layout" in line:
+ layout = line.split(":")[1].strip()
+ if "variant" in line:
+ variant = line.split(":")[1].strip()
+ if variant:
+ layout += ":" + variant
+
+ if layout in self._languages:
+ self._idx = self._languages.index(layout)
+ else:
+ self._languages.append(layout)
+ self._idx = len(self._languages) - 1
+
+ return layout
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/load.py b/.config/i3/bar/bumblebee/modules/load.py
new file mode 100644
index 00000000..4d94ee18
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/load.py
@@ -0,0 +1,41 @@
+# pylint: disable=C0111,R0903
+
+"""Displays system load.
+
+Parameters:
+ * load.warning : Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs)
+ * load.critical: Critical threshold for the one-minute load average (defaults to 80% of the number of CPUs)
+"""
+
+import os
+import multiprocessing
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.load)
+ )
+ self._load = [0, 0, 0]
+ try:
+ self._cpus = multiprocessing.cpu_count()
+ except NotImplementedError as e:
+ self._cpus = 1
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="gnome-system-monitor")
+
+ def load(self, widget):
+ return "{:.02f}/{:.02f}/{:.02f}".format(
+ self._load[0], self._load[1], self._load[2]
+ )
+
+ def update(self, widgets):
+ self._load = os.getloadavg()
+
+ def state(self, widget):
+ return self.threshold_state(self._load[0], self._cpus*0.7, self._cpus*0.8)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/memory.py b/.config/i3/bar/bumblebee/modules/memory.py
new file mode 100644
index 00000000..0534f7da
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/memory.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0111,R0903
+
+"""Displays available RAM, total amount of RAM and percentage available.
+
+Parameters:
+ * cpu.warning : Warning threshold in % of memory used (defaults to 80%)
+ * cpu.critical: Critical threshold in % of memory used (defaults to 90%)
+"""
+
+try:
+ import psutil
+except ImportError:
+ pass
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.memory_usage)
+ )
+ self._mem = psutil.virtual_memory()
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="gnome-system-monitor")
+
+ def memory_usage(self, widget):
+ used = self._mem.total - self._mem.available
+ return "{}/{} ({:05.02f}%)".format(
+ bumblebee.util.bytefmt(used),
+ bumblebee.util.bytefmt(self._mem.total),
+ self._mem.percent
+ )
+
+ def update(self, widgets):
+ self._mem = psutil.virtual_memory()
+
+ def state(self, widget):
+ if self._mem.percent > float(self.parameter("critical", 90)):
+ return "critical"
+ if self._mem.percent > float(self.parameter("warning", 80)):
+ return "warning"
+ return None
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/nic.py b/.config/i3/bar/bumblebee/modules/nic.py
new file mode 100644
index 00000000..4fd8e550
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/nic.py
@@ -0,0 +1,83 @@
+#pylint: disable=C0111,R0903
+
+"""Displays the name, IP address(es) and status of each available network interface.
+
+Parameters:
+ * nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth")
+"""
+
+import netifaces
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ super(Module, self).__init__(engine, config, widgets)
+ self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
+ self._update_widgets(widgets)
+
+ def update(self, widgets):
+ self._update_widgets(widgets)
+
+ def state(self, widget):
+ states = []
+
+ if widget.get("state") == "down":
+ states.append("critical")
+ elif widget.get("state") != "up":
+ states.append("warning")
+ else:
+ states.append("good")
+
+ intf = widget.get("intf")
+ iftype = "wireless" if self._iswlan(intf) else "wired"
+ iftype = "tunnel" if self._istunnel(intf) else iftype
+
+ states.append("{}-{}".format(iftype, widget.get("state")))
+
+ return states
+
+ def _iswlan(self, intf):
+ # wifi, wlan, wlp, seems to work for me
+ if intf.startswith("w"): return True
+ return False
+
+ def _istunnel(self, intf):
+ return intf.startswith("tun")
+
+ def _update_widgets(self, widgets):
+ interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ]
+
+ for widget in widgets:
+ widget.set("visited", False)
+
+ for intf in interfaces:
+ addr = []
+ state = "down"
+ try:
+ if netifaces.AF_INET in netifaces.ifaddresses(intf):
+ for ip in netifaces.ifaddresses(intf)[netifaces.AF_INET]:
+ if "addr" in ip and ip["addr"] != "":
+ addr.append(ip["addr"])
+ state = "up"
+ except Exception as e:
+ addr = []
+ widget = self.widget(intf)
+ if not widget:
+ widget = bumblebee.output.Widget(name=intf)
+ widgets.append(widget)
+ widget.full_text("{}".format(",".join(addr)))
+ #widget.full_text("{} {} {}".format(intf, state, ", ".join(addr)))
+ widget.set("intf", intf)
+ widget.set("state", state)
+ widget.set("visited", True)
+
+ for widget in widgets:
+ if widget.get("visited") == False:
+ widgets.remove(widget)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/nic.py_ori b/.config/i3/bar/bumblebee/modules/nic.py_ori
new file mode 100644
index 00000000..2ca87a7e
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/nic.py_ori
@@ -0,0 +1,88 @@
+#pylint: disable=C0111,R0903
+
+"""Displays the name, IP address(es) and status of each available network interface.
+
+Parameters:
+ * nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth")
+"""
+
+try:
+ import netifaces
+except ImportError:
+ pass
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ super(Module, self).__init__(engine, config, widgets)
+ self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
+ self._update_widgets(widgets)
+
+ def update(self, widgets):
+ self._update_widgets(widgets)
+
+ def state(self, widget):
+ states = []
+
+ if widget.get("state") == "down":
+ states.append("critical")
+ elif widget.get("state") != "up":
+ states.append("warning")
+
+ intf = widget.get("intf")
+ iftype = "wireless" if self._iswlan(intf) else "wired"
+ iftype = "tunnel" if self._istunnel(intf) else iftype
+
+ states.append("{}-{}".format(iftype, widget.get("state")))
+
+ return states
+
+ def _iswlan(self, intf):
+ # wifi, wlan, wlp, seems to work for me
+ if intf.startswith("w"): return True
+ return False
+
+ def _istunnel(self, intf):
+ return intf.startswith("tun")
+
+ def get_addresses(self, intf):
+ retval = []
+ try:
+ for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []):
+ if ip.get("addr", "") != "":
+ retval.append(ip.get("addr"))
+ except Exception:
+ return []
+ return retval
+
+ def _update_widgets(self, widgets):
+ interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ]
+
+ for widget in widgets:
+ widget.set("visited", False)
+
+ for intf in interfaces:
+ addr = []
+ state = "down"
+ for ip in self.get_addresses(intf):
+ addr.append(ip)
+ state = "up"
+ widget = self.widget(intf)
+ if not widget:
+ widget = bumblebee.output.Widget(name=intf)
+ widgets.append(widget)
+ widget.full_text("{} {} {}".format(intf, state, ", ".join(addr)))
+ widget.set("intf", intf)
+ widget.set("state", state)
+ widget.set("visited", True)
+
+ for widget in widgets:
+ if widget.get("visited") == False:
+ widgets.remove(widget)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/pacman.py b/.config/i3/bar/bumblebee/modules/pacman.py
new file mode 100644
index 00000000..a7780a8b
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/pacman.py
@@ -0,0 +1,70 @@
+# pylint: disable=C0111,R0903
+
+"""Displays update information per repository for pacman."
+
+Requires the following executables:
+ * fakeroot
+ * pacman
+"""
+
+import os
+import threading
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+#list of repositories.
+#the last one sould always be other
+repos = ["core", "extra", "community", "multilib", "testing", "other"]
+
+def get_pacman_info(widget, path):
+ try:
+ result = bumblebee.util.execute("{}/../../bin/pacman-updates".format(path))
+ except:
+ pass
+
+ count = len(repos)*[0]
+
+ for line in result.splitlines():
+ if line.startswith(("http", "rsync")):
+ for i in range(len(repos)-1):
+ if "/" + repos[i] + "/" in line:
+ count[i] += 1
+ break
+ else:
+ result[-1] += 1
+
+ for i in range(len(repos)):
+ widget.set(repos[i], count[i])
+
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.updates)
+ )
+ self._count = 0
+
+ def updates(self, widget):
+ return '/'.join(map(lambda x: str(widget.get(x,0)), repos))
+
+ def update(self, widgets):
+ path = os.path.dirname(os.path.abspath(__file__))
+ if self._count == 0:
+ thread = threading.Thread(target=get_pacman_info, args=(widgets[0],path))
+ thread.start()
+
+ # TODO: improve this waiting mechanism a bit
+ self._count += 1
+ self._count = 0 if self._count > 300 else self._count
+
+ def state(self, widget):
+ weightedCount = sum(map(lambda x: (len(repos)-x[0]) * widget.get(x[1],0), enumerate(repos)))
+
+ if weightedCount < 10:
+ return "good"
+
+ return self.threshold_state(weightedCount, 100, 150)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/ping.py b/.config/i3/bar/bumblebee/modules/ping.py
new file mode 100644
index 00000000..469871ae
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/ping.py
@@ -0,0 +1,77 @@
+# pylint: disable=C0111,R0903
+
+"""Periodically checks the RTT of a configurable host using ICMP echos
+
+Requires the following executable:
+ * ping
+
+Parameters:
+ * ping.interval: Time in seconds between two RTT checks (defaults to 60)
+ * ping.address : IP address to check
+ * ping.timeout : Timeout for waiting for a reply (defaults to 5.0)
+ * ping.probes : Number of probes to send (defaults to 5)
+ * ping.warning : Threshold for warning state, in seconds (defaults to 1.0)
+ * ping.critical: Threshold for critical state, in seconds (defaults to 2.0)
+"""
+
+import re
+import time
+import threading
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+def get_rtt(module, widget):
+ try:
+ widget.set("rtt-unreachable", False)
+ res = bumblebee.util.execute("ping -n -q -c {} -W {} {}".format(
+ widget.get("rtt-probes"), widget.get("rtt-timeout"), widget.get("address")
+ ))
+
+ for line in res.split("\n"):
+ if not line.startswith("rtt"): continue
+ m = re.search(r'([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+)\s+(\S+)', line)
+
+ widget.set("rtt-min", float(m.group(1)))
+ widget.set("rtt-avg", float(m.group(2)))
+ widget.set("rtt-max", float(m.group(3)))
+ widget.set("rtt-unit", m.group(5))
+ except Exception as e:
+ widget.set("rtt-unreachable", True)
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widget = bumblebee.output.Widget(full_text=self.rtt)
+ super(Module, self).__init__(engine, config, widget)
+
+ widget.set("address", self.parameter("address", "8.8.8.8"))
+ widget.set("interval", self.parameter("interval", 60))
+ widget.set("rtt-probes", self.parameter("probes", 5))
+ widget.set("rtt-timeout", self.parameter("timeout", 5.0))
+ widget.set("rtt-avg", 0.0)
+ widget.set("rtt-unit", "")
+
+ self._next_check = 0
+
+ def rtt(self, widget):
+ if widget.get("rtt-unreachable"):
+ return "{}: unreachable".format(widget.get("address"))
+ return "{}: {:.1f}{}".format(
+ widget.get("address"),
+ widget.get("rtt-avg"),
+ widget.get("rtt-unit")
+ )
+
+ def state(self, widget):
+ if widget.get("rtt-unreachable"): return ["critical"]
+ return self.threshold_state(widget.get("rtt-avg"), 1000.0, 2000.0)
+
+ def update(self, widgets):
+ if int(time.time()) < self._next_check:
+ return
+ thread = threading.Thread(target=get_rtt, args=(self,widgets[0],))
+ thread.start()
+ self._next_check = int(time.time()) + int(widgets[0].get("interval"))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/pulseaudio.py b/.config/i3/bar/bumblebee/modules/pulseaudio.py
new file mode 100644
index 00000000..0bf88f4c
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/pulseaudio.py
@@ -0,0 +1,107 @@
+# pylint: disable=C0111,R0903
+
+"""Displays volume and mute status of PulseAudio devices.
+
+Aliases: pasink, pasource
+
+Requires the following executable:
+ * pactl
+"""
+
+import re
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+ALIASES = [ "pasink", "pasource" ]
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.volume)
+ )
+
+ self._left = 0
+ self._right = 0
+ self._mono = 0
+ self._mute = False
+ channel = "sink" if self.name == "pasink" else "source"
+
+ self._patterns = [
+ { "expr": "Name:", "callback": (lambda line: False) },
+ { "expr": "Mute:", "callback": (lambda line: self.mute(False if " no" in line.lower() else True)) },
+ { "expr": "Volume:", "callback": self.getvolume },
+ ]
+
+ engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd="pavucontrol")
+
+ events = [
+ { "type": "mute", "action": "toggle", "button": bumblebee.input.LEFT_MOUSE },
+ { "type": "volume", "action": "+2%", "button": bumblebee.input.WHEEL_UP },
+ { "type": "volume", "action": "-2%", "button": bumblebee.input.WHEEL_DOWN },
+ ]
+
+ for event in events:
+ engine.input.register_callback(self, button=event["button"],
+ cmd="pactl set-{}-{} @DEFAULT_{}@ {}".format(channel, event["type"],
+ channel.upper(), event["action"]))
+
+ def mute(self, value):
+ self._mute = value
+
+ def getvolume(self, line):
+ if "mono" in line:
+ m = re.search(r'mono:.*\s*\/\s*(\d+)%', line)
+ if m:
+ self._mono = m.group(1)
+ else:
+ m = re.search(r'left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%', line)
+ if m:
+ self._left = m.group(1)
+ self._right = m.group(2)
+ return True
+
+ def _default_device(self):
+ output = bumblebee.util.execute("pactl info")
+ pattern = "Default Sink: " if self.name == "pasink" else "Default Source: "
+ for line in output.split("\n"):
+ if line.startswith(pattern):
+ return line.replace(pattern, "")
+ return "n/a"
+
+ def volume(self, widget):
+ if int(self._mono) > 0:
+ return "{}%".format(self._mono)
+ elif self._left == self._right:
+ return "{}%".format(self._left)
+ else:
+ return "{}%/{}%".format(self._left, self._right)
+ return "n/a"
+
+ def update(self, widgets):
+ channel = "sinks" if self.name == "pasink" else "sources"
+ device = self._default_device()
+
+ result = bumblebee.util.execute("pactl list {}".format(channel))
+ found = False
+
+ for line in result.split("\n"):
+ if device in line:
+ found = True
+ continue
+ if found == False:
+ continue
+ for pattern in self._patterns:
+ if not pattern["expr"] in line:
+ continue
+ if pattern["callback"](line) == False and found == True:
+ return
+
+ def state(self, widget):
+ if self._mute:
+ return [ "warning", "muted" ]
+ return [ "unmuted" ]
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/redshift.py b/.config/i3/bar/bumblebee/modules/redshift.py
new file mode 100644
index 00000000..2fd1dfbb
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/redshift.py
@@ -0,0 +1,46 @@
+# pylint: disable=C0111,R0903
+
+"""Displays the current color temperature of redshift
+
+Requires the following executable:
+ * redshift
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.text)
+ )
+ self._text = ""
+ self._state = "transition"
+
+ def text(self, widget):
+ return "{}".format(self._text)
+
+ def update(self, widgets):
+ result = bumblebee.util.execute("redshift -p")
+
+ temp = ""
+ transition = ""
+ for line in result.split("\n"):
+ if "temperature" in line.lower():
+ temp = line.split(" ")[2]
+ if "period" in line.lower():
+ state = line.split(" ")[1].lower()
+ if "day" in state:
+ self._state = "day"
+ elif "night" in state:
+ self._state = "night"
+ else:
+ self._state = "transition"
+ transition = " ".join(line.split(" ")[2:])
+ self._text = "{} {}".format(temp, transition)
+
+ def state(self, widget):
+ return self._state
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/sensors.py b/.config/i3/bar/bumblebee/modules/sensors.py
new file mode 100644
index 00000000..36dabbd7
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/sensors.py
@@ -0,0 +1,45 @@
+"""Displays sensor temperature
+
+Requires the following executable:
+ * sensors
+
+Parameters:
+ * sensors.match: What line in the output of `sensors -u` should be matched against (default: temp1_input)
+ * sensors.match_number: which of the matches you want (default -1: last match).
+"""
+
+import os
+import re
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.temperature)
+ )
+ self._temperature = "unknown"
+ pattern = self.parameter("match", "temp1_input")
+ pattern_string = r"^\s*{}:\s*([\d.]+)$".format(pattern)
+ self._match_number = int(self.parameter("match_number", "-1"))
+ self._pattern = re.compile(pattern_string, re.MULTILINE)
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd="xsensors")
+
+ def get_temp(self):
+ temperatures = bumblebee.util.execute("sensors -u")
+ matching_temp = self._pattern.findall(temperatures)
+ temperature = "unknown"
+ if matching_temp:
+ temperature = int(float(matching_temp[self._match_number]))
+
+ return temperature
+
+ def temperature(self, widget):
+ return self._temperature
+
+ def update(self, widgets):
+ self._temperature = self.get_temp()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/spacer.py b/.config/i3/bar/bumblebee/modules/spacer.py
new file mode 100644
index 00000000..b89c2334
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/spacer.py
@@ -0,0 +1,23 @@
+# pylint: disable=C0111,R0903
+
+"""Draws a widget with configurable text content.
+
+Parameters:
+ * spacer.text: Widget contents (defaults to empty string)
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.text)
+ )
+ self._text = self.parameter("text", "")
+
+ def text(self, widget):
+ return self._text
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/stock.py b/.config/i3/bar/bumblebee/modules/stock.py
new file mode 100644
index 00000000..2297c3a8
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/stock.py
@@ -0,0 +1,62 @@
+# -*- coding: UTF-8 -*-
+# pylint: disable=C0111,R0903
+
+"""Display a stock quote from yahoo finance.
+
+Requires the following python packages:
+ * requests
+
+Parameters:
+ * stock.symbols : Comma-separated list of symbols to fetch
+ * stock.change : Should we fetch change in stock value (defaults to True)
+ * stock.currencies : List of symbols to go with the values (default $)
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+import requests
+from requests.exceptions import RequestException
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.value)
+ )
+ self._symbols = self.parameter("symbols", "")
+ self._change = self.parameter("change", True)
+ self._currencies = self.parameter('currencies', None)
+ self._baseurl = 'http://download.finance.yahoo.com/d/quotes.csv'
+ self._value = self.fetch()
+
+ if not self._currencies:
+ self._currencies = '$' * len(self._symbols)
+
+ # The currencies could be unicode, like the € symbol. Convert to a unicode object.
+ if hasattr(self._currencies, "decode"):
+ self._currencies = self._currencies.decode("utf-8", "ignore")
+
+ def value(self, widget):
+ results = []
+ for i, val in enumerate(self._value.split('\n')):
+ try:
+ currency_symbol = self._currencies[i]
+ except:
+ currency_symbol = '$'
+ results.append('%s%s' % (currency_symbol, val))
+ return u' '.join(results)
+
+ def fetch(self):
+ if self._symbols:
+ url = self._baseurl
+ url += '?s=%s&f=l1' % self._symbols
+ if self._change:
+ url += 'c1'
+ return requests.get(url).text.strip()
+ else:
+ return ''
+
+ def update(self, widgets):
+ self._value = self.fetch()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/test.py b/.config/i3/bar/bumblebee/modules/test.py
new file mode 100644
index 00000000..e4099c65
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/test.py
@@ -0,0 +1,16 @@
+# pylint: disable=C0111,R0903
+
+"""Test module
+"""
+
+import bumblebee.engine
+
+ALIASES = [ "test-alias" ]
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text="test")
+ )
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/traffic.py b/.config/i3/bar/bumblebee/modules/traffic.py
new file mode 100644
index 00000000..0da9fd1f
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/traffic.py
@@ -0,0 +1,72 @@
+# pylint: disable=C0111,R0903
+
+"""Displays network IO for interfaces.
+
+Parameters:
+ * traffic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth")
+"""
+
+import re
+import psutil
+import netifaces
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ super(Module, self).__init__(engine, config, widgets)
+ self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
+ self._update_widgets(widgets)
+ self._status = ""
+
+ def state(self, widget):
+ if "traffic.rx" in widget.name:
+ return "rx"
+ if "traffic.tx" in widget.name:
+ return "tx"
+ return self._status
+
+ def update(self, widgets):
+ self._update_widgets(widgets)
+
+ def create_widget(self, widgets, name, txt=None, attributes={}):
+ widget = self.widget(name)
+ if widget: return widget
+
+ widget = bumblebee.output.Widget(name=name)
+ widget.full_text(txt)
+ widgets.append(widget)
+
+ for key in attributes:
+ widget.set(key, attributes[key])
+
+ return widget
+
+ def _update_widgets(self, widgets):
+ interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ]
+
+ counters = psutil.net_io_counters(pernic=True)
+ for interface in interfaces:
+ if not interface: interface = "lo"
+ data = {
+ "rx": counters[interface].bytes_recv,
+ "tx": counters[interface].bytes_sent,
+ }
+
+ name = "traffic-{}".format(interface)
+
+ self.create_widget(widgets, name, interface)
+
+ for direction in ["rx", "tx"]:
+ name = "traffic.{}-{}".format(direction, interface)
+ widget = self.create_widget(widgets, name, attributes={"theme.minwidth": "1000.00MB"})
+ prev = widget.get(direction, 0)
+ speed = bumblebee.util.bytefmt(int(data[direction]) - int(prev))
+ widget.full_text(speed)
+ widget.set(direction, data[direction])
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/weather.py b/.config/i3/bar/bumblebee/modules/weather.py
new file mode 100644
index 00000000..683f61df
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/weather.py
@@ -0,0 +1,74 @@
+# -*- coding: UTF-8 -*-
+# pylint: disable=C0111,R0903
+
+"""Displays the temperature on the current location based on the ip
+
+Requires the following python packages:
+ * requests
+
+Parameters:
+ * weather.interval: Interval (in minutes) for updating weather information
+ * weather.location: Set location (ISO 3166 country code), defaults to 'auto' for getting location from http://ipinfo.io
+ * weather.unit: metric (default), kelvin, imperial
+ * weather.apikey: API key from http://api.openweathermap.org
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+import json
+import time
+try:
+ import requests
+ from requests.exceptions import RequestException
+except ImportError:
+ pass
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.temperature)
+ )
+ self._temperature = 0
+ self._apikey = self.parameter("apikey", "af7bfe22287c652d032a3064ffa44088")
+ self._location = self.parameter("location", "auto")
+ self._interval = int(self.parameter("interval", "15"))
+ self._unit = self.parameter("unit", "metric")
+ self._nextcheck = 0
+ self._valid = False
+
+ def _unit_suffix(self):
+ if self._unit == "metric":
+ return "C"
+ if self._unit == "kelvin":
+ return "K"
+ if self._unit == "imperial":
+ return "F"
+ return ""
+
+ def temperature(self, widget):
+ if not self._valid:
+ return u"?"
+ return u"{}°{}".format(self._temperature, self._unit_suffix())
+
+ def update(self, widgets):
+ timestamp = int(time.time())
+ if self._nextcheck < int(time.time()):
+ try:
+ self._nextcheck = int(time.time()) + self._interval*60
+ weather_url = "http://api.openweathermap.org/data/2.5/weather?appid={}".format(self._apikey)
+ weather_url = "{}&units={}".format(weather_url, self._unit)
+ if self._location == "auto":
+ location_url = "http://ipinfo.io/json"
+ location = json.loads(requests.get(location_url).text)
+ coord = location["loc"].split(",")
+ weather_url = "{url}&lat={lat}&lon={lon}".format(url=weather_url, lat=coord[0], lon=coord[1])
+ else:
+ weather_url = "{url}&q={city}".format(url=weather_url, city=self._location)
+ weather = json.loads(requests.get(weather_url).text)
+ self._temperature = int(weather['main']['temp'])
+ self._valid = True
+ except RequestException:
+ self._valid = False
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/modules/xrandr.py b/.config/i3/bar/bumblebee/modules/xrandr.py
new file mode 100644
index 00000000..a1db224f
--- /dev/null
+++ b/.config/i3/bar/bumblebee/modules/xrandr.py
@@ -0,0 +1,74 @@
+# pylint: disable=C0111,R0903
+
+"""Shows a widget for each connected screen and allows the user to enable/disable screens.
+
+Requires the following executable:
+ * xrandr
+"""
+
+import os
+import re
+import sys
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ self._engine = engine
+ super(Module, self).__init__(engine, config, widgets)
+ self.update_widgets(widgets)
+
+ def update_widgets(self, widgets):
+ new_widgets = []
+ for line in bumblebee.util.execute("xrandr -q").split("\n"):
+ if not " connected" in line:
+ continue
+ display = line.split(" ", 2)[0]
+ m = re.search(r'\d+x\d+\+(\d+)\+\d+', line)
+
+ widget = self.widget(display)
+ if not widget:
+ widget = bumblebee.output.Widget(full_text=display, name=display)
+ self._engine.input.register_callback(widget, button=1, cmd=self._toggle)
+ self._engine.input.register_callback(widget, button=3, cmd=self._toggle)
+ new_widgets.append(widget)
+ widget.set("state", "on" if m else "off")
+ widget.set("pos", int(m.group(1)) if m else sys.maxsize)
+
+ while len(widgets) > 0:
+ del widgets[0]
+ for widget in new_widgets:
+ widgets.append(widget)
+
+ def update(self, widgets):
+ self.update_widgets(widgets)
+
+ def state(self, widget):
+ return widget.get("state", "off")
+
+ def _toggle(self, event):
+ path = os.path.dirname(os.path.abspath(__file__))
+ toggle_cmd = "{}/../../bin/toggle-display.sh".format(path)
+
+ widget = self.widget_by_id(event["instance"])
+
+ if widget.get("state") == "on":
+ bumblebee.util.execute("{} --output {} --off".format(toggle_cmd, widget.name))
+ else:
+ first_neighbor = next((widget for widget in self.widgets() if widget.get("state") == "on"), None)
+ last_neighbor = next((widget for widget in reversed(self.widgets()) if widget.get("state") == "on"), None)
+
+ neighbor = first_neighbor if event["button"] == bumblebee.input.LEFT_MOUSE else last_neighbor
+
+ if neighbor == None:
+ bumblebee.util.execute("{} --output {} --auto".format(toggle_cmd, widget.name))
+ else:
+ bumblebee.util.execute("{} --output {} --auto --{}-of {}".format(toggle_cmd, widget.name,
+ "left" if event.get("button") == bumblebee.input.LEFT_MOUSE else "right",
+ neighbor.name))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/output.py b/.config/i3/bar/bumblebee/output.py
new file mode 100644
index 00000000..992eec6e
--- /dev/null
+++ b/.config/i3/bar/bumblebee/output.py
@@ -0,0 +1,140 @@
+# pylint: disable=R0201
+
+"""Output classes"""
+
+import sys
+import json
+import uuid
+
+import bumblebee.store
+
+def scrollable(func):
+ def wrapper(module, widget):
+ text = func(module, widget)
+ width = widget.get("theme.width", module.parameter("width", 30))
+ widget.set("theme.minwidth", "A"*width)
+ if len(text) <= width:
+ return text
+ # we need to shorten
+ start = widget.get("scrolling.start", -1)
+ direction = widget.get("scrolling.direction", "right")
+ start += 1 if direction == "right" else -1
+ widget.set("scrolling.start", start)
+ if width + start >= len(text):
+ widget.set("scrolling.direction", "left")
+ if start <= 0:
+ widget.set("scrolling.direction", "right")
+ text = text[start:width+start]
+
+ return text
+ return wrapper
+
+class Widget(bumblebee.store.Store):
+ """Represents a single visible block in the status bar"""
+ def __init__(self, full_text="", name=""):
+ super(Widget, self).__init__()
+ self._full_text = full_text
+ self.module = None
+ self._module = None
+ self.name = name
+ self.id = str(uuid.uuid4())
+
+ def link_module(self, module):
+ """Set the module that spawned this widget
+
+ This is done outside the constructor to avoid having to
+ pass in the module name in every concrete module implementation"""
+ self.module = module.name
+ self._module = module
+
+ def cls(self):
+ if not self._module:
+ return None
+ return self._module.__module__.replace("bumblebee.modules.", "")
+
+ def state(self):
+ """Return the widget's state"""
+ if self._module and hasattr(self._module, "state"):
+ states = self._module.state(self)
+ if not isinstance(states, list):
+ return [states]
+ return states
+ return []
+
+ def full_text(self, value=None):
+ """Set or retrieve the full text to display in the widget"""
+ if value:
+ self._full_text = value
+ else:
+ if callable(self._full_text):
+ return self._full_text(self)
+ else:
+ return self._full_text
+
+class I3BarOutput(object):
+ """Manage output according to the i3bar protocol"""
+ def __init__(self, theme):
+ self._theme = theme
+ self._widgets = []
+ self._started = False
+
+ def started(self):
+ return self._started
+
+ def start(self):
+ """Print start preamble for i3bar protocol"""
+ self._started = True
+ sys.stdout.write(json.dumps({"version": 1, "click_events": True}) + "\n[\n")
+
+ def stop(self):
+ """Finish i3bar protocol"""
+ sys.stdout.write("]\n")
+
+ def draw(self, widget, module=None, engine=None):
+ """Draw a single widget"""
+ full_text = widget.full_text()
+ padding = self._theme.padding(widget)
+ prefix = self._theme.prefix(widget, padding)
+ suffix = self._theme.suffix(widget, padding)
+ minwidth = self._theme.minwidth(widget)
+ if prefix:
+ full_text = u"{}{}".format(prefix, full_text)
+ if suffix:
+ full_text = u"{}{}".format(full_text, suffix)
+ separator = self._theme.separator(widget)
+ if separator:
+ self._widgets.append({
+ u"full_text": separator,
+ "separator": False,
+ "color": self._theme.separator_fg(widget),
+ "background": self._theme.separator_bg(widget),
+ "separator_block_width": self._theme.separator_block_width(widget),
+ })
+ width = self._theme.minwidth(widget)
+ self._widgets.append({
+ u"full_text": full_text,
+ "color": self._theme.fg(widget),
+ "background": self._theme.bg(widget),
+ "separator_block_width": self._theme.separator_block_width(widget),
+ "separator": True if separator is None else False,
+ "min_width": width + "A"*(len(prefix) + len(suffix)) if width else None,
+ "align": self._theme.align(widget),
+ "instance": widget.id,
+ "name": module.id,
+ })
+
+ def begin(self):
+ """Start one output iteration"""
+ self._widgets = []
+ self._theme.reset()
+
+ def flush(self):
+ """Flushes output"""
+ sys.stdout.write(json.dumps(self._widgets))
+
+ def end(self):
+ """Finalizes output"""
+ sys.stdout.write(",\n")
+ sys.stdout.flush()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/store.py b/.config/i3/bar/bumblebee/store.py
new file mode 100644
index 00000000..8fac71ba
--- /dev/null
+++ b/.config/i3/bar/bumblebee/store.py
@@ -0,0 +1,21 @@
+"""Store interface
+
+Allows arbitrary classes to offer a simple get/set
+store interface by deriving from the Store class in
+this module
+"""
+
+class Store(object):
+ """Interface for storing and retrieving simple values"""
+ def __init__(self):
+ self._data = {}
+
+ def set(self, key, value):
+ """Set 'key' to 'value', overwriting 'key' if it exists already"""
+ self._data[key] = value
+
+ def get(self, key, default=None):
+ """Return the current value of 'key', or 'default' if 'key' is not set"""
+ return self._data.get(key, default)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/theme.py b/.config/i3/bar/bumblebee/theme.py
new file mode 100644
index 00000000..e16fbb65
--- /dev/null
+++ b/.config/i3/bar/bumblebee/theme.py
@@ -0,0 +1,180 @@
+# pylint: disable=C0103
+
+"""Theme support"""
+
+import os
+import glob
+import copy
+import json
+import io
+
+import bumblebee.error
+
+def theme_path():
+ """Return the path of the theme directory"""
+ return os.path.dirname("{}/../themes/".format(os.path.dirname(os.path.realpath(__file__))))
+
+def themes():
+ result = []
+
+ for filename in glob.iglob("{}/*.json".format(theme_path())):
+ if "test" not in filename:
+ result.append(os.path.basename(filename).replace(".json", ""))
+ return result
+
+class Theme(object):
+ """Represents a collection of icons and colors"""
+ def __init__(self, name):
+ self._init(self.load(name))
+ self._widget = None
+ self._cycle_idx = 0
+ self._cycle = {}
+ self._prevbg = None
+
+ def _init(self, data):
+ """Initialize theme from data structure"""
+ for iconset in data.get("icons", []):
+ self._merge(data, self._load_icons(iconset))
+ self._theme = data
+ self._defaults = data.get("defaults", {})
+ self._cycles = self._theme.get("cycle", [])
+ self.reset()
+
+ def data(self):
+ """Return the raw theme data"""
+ return self._theme
+
+ def reset(self):
+ """Reset theme to initial state"""
+ self._cycle = self._cycles[0] if len(self._cycles) > 0 else {}
+ self._cycle_idx = 0
+ self._widget = None
+ self._prevbg = None
+
+ def padding(self, widget):
+ """Return padding for widget"""
+ return self._get(widget, "padding", "")
+
+ def prefix(self, widget, default=None):
+ """Return the theme prefix for a widget's full text"""
+ padding = self.padding(widget)
+ pre = self._get(widget, "prefix", None)
+ return u"{}{}{}".format(padding, pre, padding) if pre else default
+
+ def suffix(self, widget, default=None):
+ """Return the theme suffix for a widget's full text"""
+ padding = self._get(widget, "padding", "")
+ suf = self._get(widget, "suffix", None)
+ return u"{}{}{}".format(padding, suf, padding) if suf else default
+
+ def fg(self, widget):
+ """Return the foreground color for this widget"""
+ return self._get(widget, "fg", None)
+
+ def bg(self, widget):
+ """Return the background color for this widget"""
+ return self._get(widget, "bg", None)
+
+ def align(self, widget):
+ """Return the widget alignment"""
+ return self._get(widget, "align", None)
+
+ def minwidth(self, widget):
+ """Return the minimum width string for this widget"""
+ return self._get(widget, "minwidth", "")
+
+ def separator(self, widget):
+ """Return the separator between widgets"""
+ return self._get(widget, "separator", None)
+
+ def separator_fg(self, widget):
+ """Return the separator's foreground/text color"""
+ return self.bg(widget)
+
+ def separator_bg(self, widget):
+ """Return the separator's background color"""
+ return self._prevbg
+
+ def separator_block_width(self, widget):
+ """Return the SBW"""
+ return self._get(widget, "separator-block-width", None)
+
+ def _load_icons(self, name):
+ """Load icons for a theme"""
+ path = "{}/icons/".format(theme_path())
+ return self.load(name, path=path)
+
+ def load(self, name, path=theme_path()):
+ """Load and parse a theme file"""
+ themefile = "{}/{}.json".format(path, name)
+
+ if os.path.isfile(themefile):
+ try:
+ with io.open(themefile,encoding="utf-8") as data:
+ return json.load(data)
+ except ValueError as exception:
+ raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception))
+ else:
+ raise bumblebee.error.ThemeLoadError("no such theme: {}".format(name))
+
+ def _get(self, widget, name, default=None):
+ """Return the config value 'name' for 'widget'"""
+
+ if not self._widget:
+ self._widget = widget
+
+ if self._widget != widget:
+ self._prevbg = self.bg(self._widget)
+ self._widget = widget
+ if len(self._cycles) > 0:
+ self._cycle_idx = (self._cycle_idx + 1) % len(self._cycles)
+ self._cycle = self._cycles[self._cycle_idx]
+
+ module_theme = self._theme.get(widget.module, {})
+ class_theme = self._theme.get(widget.cls(), {})
+
+ state_themes = []
+ # avoid infinite recursion
+ states = widget.state()
+ if name not in states:
+ for state in states:
+ state_themes.append(self._get(widget, state, {}))
+
+ value = self._defaults.get(name, default)
+ value = widget.get("theme.{}".format(name), value)
+ value = self._cycle.get(name, value)
+ value = class_theme.get(name, value)
+ value = module_theme.get(name, value)
+
+ for theme in state_themes:
+ value = theme.get(name, value)
+
+ if isinstance(value, list):
+ key = "{}-idx".format(name)
+ idx = widget.get(key, 0)
+ widget.set(key, (idx + 1) % len(value))
+ value = value[idx]
+
+ return value
+
+ # algorithm copied from
+ # http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts
+ # nicely done :)
+ def _merge(self, target, *args):
+ """Merge two arbitrarily nested data structures"""
+ if len(args) > 1:
+ for item in args:
+ self._merge(item)
+ return target
+
+ item = args[0]
+ if not isinstance(item, dict):
+ return item
+ for key, value in item.items():
+ if key in target and isinstance(target[key], dict):
+ self._merge(target[key], value)
+ else:
+ target[key] = copy.deepcopy(value)
+ return target
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/bumblebee/util.py b/.config/i3/bar/bumblebee/util.py
new file mode 100644
index 00000000..9efa146f
--- /dev/null
+++ b/.config/i3/bar/bumblebee/util.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+
+import shlex
+import logging
+import subprocess
+
+try:
+ from exceptions import RuntimeError
+except ImportError:
+ # Python3 doesn't require this anymore
+ pass
+
+def execute(cmd, wait=True):
+ logging.info("executing command '{}'".format(cmd))
+ args = shlex.split(cmd)
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ rv = None
+
+ if wait:
+ out, _ = proc.communicate()
+ if proc.returncode != 0:
+ raise RuntimeError("{} exited with {}".format(cmd, proc.returncode))
+
+ if hasattr(out, "decode"):
+ rv = out.decode("utf-8")
+ else:
+ rv = out
+
+ logging.info(u"command returned '{}'".format("" if not rv else rv))
+ return rv
+
+def bytefmt(num):
+ for unit in [ "", "Ki", "Mi", "Gi" ]:
+ if num < 1024.0:
+ return "{:.2f}{}B".format(num, unit)
+ num /= 1024.0
+ return "{:.2f}GiB".format(num*1024.0)
+
+def durationfmt(duration):
+ minutes, seconds = divmod(duration, 60)
+ hours, minutes = divmod(minutes, 60)
+ res = "{:02d}:{:02d}".format(minutes, seconds)
+ if hours > 0: res = "{:02d}:{}".format(hours, res)
+
+ return res
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/runlint.sh b/.config/i3/bar/runlint.sh
new file mode 100755
index 00000000..6902ce9e
--- /dev/null
+++ b/.config/i3/bar/runlint.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+find . -name "*.py"|xargs pylint --disable=R0903,R0201,C0330
diff --git a/.config/i3/bar/runtests.sh b/.config/i3/bar/runtests.sh
new file mode 100755
index 00000000..60f058eb
--- /dev/null
+++ b/.config/i3/bar/runtests.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+echo "testing with $(python2 -V 2>&1)"
+python2 $(which nosetests) --rednose -v --with-coverage --cover-erase tests/
+
+if [ $? == 0 ]; then
+ echo
+
+ echo "testing with $(python3 -V 2>&1)"
+ python3 $(which nosetests-3) --rednose -v --with-coverage --cover-erase tests/
+fi
diff --git a/.config/i3/bar/screenshots/amixer.png b/.config/i3/bar/screenshots/amixer.png
new file mode 100644
index 00000000..0da57c50
Binary files /dev/null and b/.config/i3/bar/screenshots/amixer.png differ
diff --git a/.config/i3/bar/screenshots/battery.png b/.config/i3/bar/screenshots/battery.png
new file mode 100644
index 00000000..bdbafee4
Binary files /dev/null and b/.config/i3/bar/screenshots/battery.png differ
diff --git a/.config/i3/bar/screenshots/brightness.png b/.config/i3/bar/screenshots/brightness.png
new file mode 100644
index 00000000..298abf39
Binary files /dev/null and b/.config/i3/bar/screenshots/brightness.png differ
diff --git a/.config/i3/bar/screenshots/caffeine.png b/.config/i3/bar/screenshots/caffeine.png
new file mode 100644
index 00000000..54447779
Binary files /dev/null and b/.config/i3/bar/screenshots/caffeine.png differ
diff --git a/.config/i3/bar/screenshots/cmus.png b/.config/i3/bar/screenshots/cmus.png
new file mode 100644
index 00000000..52c0b515
Binary files /dev/null and b/.config/i3/bar/screenshots/cmus.png differ
diff --git a/.config/i3/bar/screenshots/cpu.png b/.config/i3/bar/screenshots/cpu.png
new file mode 100644
index 00000000..0b5527c8
Binary files /dev/null and b/.config/i3/bar/screenshots/cpu.png differ
diff --git a/.config/i3/bar/screenshots/date.png b/.config/i3/bar/screenshots/date.png
new file mode 100644
index 00000000..36621e74
Binary files /dev/null and b/.config/i3/bar/screenshots/date.png differ
diff --git a/.config/i3/bar/screenshots/datetime.png b/.config/i3/bar/screenshots/datetime.png
new file mode 100644
index 00000000..0c158432
Binary files /dev/null and b/.config/i3/bar/screenshots/datetime.png differ
diff --git a/.config/i3/bar/screenshots/disk.png b/.config/i3/bar/screenshots/disk.png
new file mode 100644
index 00000000..7a659b8f
Binary files /dev/null and b/.config/i3/bar/screenshots/disk.png differ
diff --git a/.config/i3/bar/screenshots/dnf.png b/.config/i3/bar/screenshots/dnf.png
new file mode 100644
index 00000000..49924168
Binary files /dev/null and b/.config/i3/bar/screenshots/dnf.png differ
diff --git a/.config/i3/bar/screenshots/kernel.png b/.config/i3/bar/screenshots/kernel.png
new file mode 100644
index 00000000..9d20cdc9
Binary files /dev/null and b/.config/i3/bar/screenshots/kernel.png differ
diff --git a/.config/i3/bar/screenshots/layout.png b/.config/i3/bar/screenshots/layout.png
new file mode 100644
index 00000000..d09250e9
Binary files /dev/null and b/.config/i3/bar/screenshots/layout.png differ
diff --git a/.config/i3/bar/screenshots/load.png b/.config/i3/bar/screenshots/load.png
new file mode 100644
index 00000000..e136e7e9
Binary files /dev/null and b/.config/i3/bar/screenshots/load.png differ
diff --git a/.config/i3/bar/screenshots/memory.png b/.config/i3/bar/screenshots/memory.png
new file mode 100644
index 00000000..db0c59be
Binary files /dev/null and b/.config/i3/bar/screenshots/memory.png differ
diff --git a/.config/i3/bar/screenshots/nic.png b/.config/i3/bar/screenshots/nic.png
new file mode 100644
index 00000000..d1512c07
Binary files /dev/null and b/.config/i3/bar/screenshots/nic.png differ
diff --git a/.config/i3/bar/screenshots/pacman.png b/.config/i3/bar/screenshots/pacman.png
new file mode 100644
index 00000000..8f4169e3
Binary files /dev/null and b/.config/i3/bar/screenshots/pacman.png differ
diff --git a/.config/i3/bar/screenshots/pasink.png b/.config/i3/bar/screenshots/pasink.png
new file mode 100644
index 00000000..2fd63595
Binary files /dev/null and b/.config/i3/bar/screenshots/pasink.png differ
diff --git a/.config/i3/bar/screenshots/pasource.png b/.config/i3/bar/screenshots/pasource.png
new file mode 100644
index 00000000..ae2ee772
Binary files /dev/null and b/.config/i3/bar/screenshots/pasource.png differ
diff --git a/.config/i3/bar/screenshots/ping.png b/.config/i3/bar/screenshots/ping.png
new file mode 100644
index 00000000..1c8bf2be
Binary files /dev/null and b/.config/i3/bar/screenshots/ping.png differ
diff --git a/.config/i3/bar/screenshots/pulseaudio.png b/.config/i3/bar/screenshots/pulseaudio.png
new file mode 100644
index 00000000..b38a4aa3
Binary files /dev/null and b/.config/i3/bar/screenshots/pulseaudio.png differ
diff --git a/.config/i3/bar/screenshots/redshift.png b/.config/i3/bar/screenshots/redshift.png
new file mode 100644
index 00000000..999328f6
Binary files /dev/null and b/.config/i3/bar/screenshots/redshift.png differ
diff --git a/.config/i3/bar/screenshots/sensors.png b/.config/i3/bar/screenshots/sensors.png
new file mode 100644
index 00000000..69d25542
Binary files /dev/null and b/.config/i3/bar/screenshots/sensors.png differ
diff --git a/.config/i3/bar/screenshots/spacer.png b/.config/i3/bar/screenshots/spacer.png
new file mode 100644
index 00000000..5200d0fc
Binary files /dev/null and b/.config/i3/bar/screenshots/spacer.png differ
diff --git a/.config/i3/bar/screenshots/stock.png b/.config/i3/bar/screenshots/stock.png
new file mode 100644
index 00000000..427f7cf8
Binary files /dev/null and b/.config/i3/bar/screenshots/stock.png differ
diff --git a/.config/i3/bar/screenshots/themes/default.png b/.config/i3/bar/screenshots/themes/default.png
new file mode 100644
index 00000000..c2680bd1
Binary files /dev/null and b/.config/i3/bar/screenshots/themes/default.png differ
diff --git a/.config/i3/bar/screenshots/themes/gruvbox.png b/.config/i3/bar/screenshots/themes/gruvbox.png
new file mode 100644
index 00000000..2f2c3859
Binary files /dev/null and b/.config/i3/bar/screenshots/themes/gruvbox.png differ
diff --git a/.config/i3/bar/screenshots/themes/powerline-gruvbox.png b/.config/i3/bar/screenshots/themes/powerline-gruvbox.png
new file mode 100644
index 00000000..5b80ff33
Binary files /dev/null and b/.config/i3/bar/screenshots/themes/powerline-gruvbox.png differ
diff --git a/.config/i3/bar/screenshots/themes/powerline-solarized.png b/.config/i3/bar/screenshots/themes/powerline-solarized.png
new file mode 100644
index 00000000..5be0b16a
Binary files /dev/null and b/.config/i3/bar/screenshots/themes/powerline-solarized.png differ
diff --git a/.config/i3/bar/screenshots/themes/powerline.png b/.config/i3/bar/screenshots/themes/powerline.png
new file mode 100644
index 00000000..1bd551ad
Binary files /dev/null and b/.config/i3/bar/screenshots/themes/powerline.png differ
diff --git a/.config/i3/bar/screenshots/themes/solarized.png b/.config/i3/bar/screenshots/themes/solarized.png
new file mode 100644
index 00000000..a15d08ce
Binary files /dev/null and b/.config/i3/bar/screenshots/themes/solarized.png differ
diff --git a/.config/i3/bar/screenshots/time.png b/.config/i3/bar/screenshots/time.png
new file mode 100644
index 00000000..39d5210f
Binary files /dev/null and b/.config/i3/bar/screenshots/time.png differ
diff --git a/.config/i3/bar/screenshots/traffic.png b/.config/i3/bar/screenshots/traffic.png
new file mode 100644
index 00000000..bcb27dad
Binary files /dev/null and b/.config/i3/bar/screenshots/traffic.png differ
diff --git a/.config/i3/bar/screenshots/weather.png b/.config/i3/bar/screenshots/weather.png
new file mode 100644
index 00000000..1efee833
Binary files /dev/null and b/.config/i3/bar/screenshots/weather.png differ
diff --git a/.config/i3/bar/screenshots/xrandr.png b/.config/i3/bar/screenshots/xrandr.png
new file mode 100644
index 00000000..ec1c6de1
Binary files /dev/null and b/.config/i3/bar/screenshots/xrandr.png differ
diff --git a/.config/i3/bar/testjson.sh b/.config/i3/bar/testjson.sh
new file mode 100755
index 00000000..d4df9f90
--- /dev/null
+++ b/.config/i3/bar/testjson.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+find themes/ -name "*.json"|grep -v invalid|xargs cat|json_verify -s
diff --git a/.config/i3/bar/tests/__init__.py b/.config/i3/bar/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/.config/i3/bar/tests/mocks.py b/.config/i3/bar/tests/mocks.py
new file mode 100644
index 00000000..f5be753b
--- /dev/null
+++ b/.config/i3/bar/tests/mocks.py
@@ -0,0 +1,141 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import json
+import shlex
+import random
+import subprocess
+
+from bumblebee.input import I3BarInput
+from bumblebee.output import Widget
+from bumblebee.config import Config
+
+def rand(cnt):
+ return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for i in range(cnt))
+
+def setup_test(test, Module):
+ test._stdin, test._select, test.stdin, test.select = epoll_mock("bumblebee.input")
+
+ test.popen = MockPopen()
+
+ test.config = Config()
+ test.input = I3BarInput()
+ test.engine = mock.Mock()
+ test.engine.input = test.input
+ test.input.need_event = True
+ test.module = Module(engine=test.engine, config={ "config": test.config })
+ for widget in test.module.widgets():
+ widget.link_module(test.module)
+ test.anyWidget = widget
+
+def teardown_test(test):
+ test._stdin.stop()
+ test._select.stop()
+ test.popen.cleanup()
+
+def epoll_mock(module=""):
+ if len(module) > 0: module = "{}.".format(module)
+
+ stdin = mock.patch("{}sys.stdin".format(module))
+ select = mock.patch("{}select".format(module))
+ epoll = mock.Mock()
+
+ stdin_mock = stdin.start()
+ select_mock = select.start()
+
+ stdin_mock.fileno.return_value = 1
+ select_mock.epoll.return_value = epoll
+ epoll.poll.return_value = [(stdin_mock.fileno.return_value, 100)]
+
+ return stdin, select, stdin_mock, select_mock
+
+def mouseEvent(stdin, button, inp, module=None, instance=None):
+ stdin.readline.return_value = json.dumps({
+ "name": module.id if module else rand(10),
+ "button": button,
+ "instance": instance
+ })
+ inp.start()
+ inp.stop()
+ stdin.readline.assert_any_call()
+
+class MockPopen(object):
+ def __init__(self, module=""):
+ if len(module) > 0: module = "{}.".format(module)
+ self._patch = mock.patch("{}subprocess.Popen".format(module))
+ self._popen = self._patch.start()
+ self.mock = mock.Mock()
+ # for a nicer, more uniform interface
+ self.mock.popen = self._popen
+ # for easier command execution checks
+ self.mock.popen.assert_call = self.assert_call
+ self._popen.return_value = self.mock
+
+ self.mock.communicate.return_value = [ "", None ]
+ self.mock.returncode = 0
+
+ def assert_call(self, cmd):
+ self.mock.popen.assert_any_call(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ def cleanup(self):
+ self._patch.stop()
+
+class MockInput(object):
+ def __init__(self):
+ self._callbacks = {}
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def get_callback(self, uid):
+ return self._callbacks.get(uid, None)
+
+ def register_callback(self, obj, button, cmd):
+ if not obj:
+ return
+ self._callbacks[obj.id] = {
+ "button": button,
+ "command": cmd,
+ }
+
+class MockOutput(object):
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def draw(self, widget, engine, module):
+ engine.stop()
+
+ def begin(self):
+ pass
+
+ def flush(self):
+ pass
+
+ def end(self):
+ pass
+
+class MockEngine(object):
+ def __init__(self):
+ self.input = MockInput()
+
+class MockWidget(Widget):
+ def __init__(self, text):
+ super(MockWidget, self).__init__(text)
+ self.module = None
+ self.attr_state = ["state-default"]
+ self.id = rand(10)
+
+ self.full_text(text)
+
+# def state(self):
+# return self.attr_state
+
+ def update(self, widgets):
+ pass
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/__init__.py b/.config/i3/bar/tests/modules/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/.config/i3/bar/tests/modules/test_battery.py b/.config/i3/bar/tests/modules/test_battery.py
new file mode 100644
index 00000000..0c86cc9f
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_battery.py
@@ -0,0 +1,108 @@
+# pylint: disable=C0103,C0111
+
+import sys
+import mock
+import unittest
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.modules.battery import Module
+from bumblebee.config import Config
+
+class TestBatteryModule(unittest.TestCase):
+ def setUp(self):
+ self._stdout = mock.patch("sys.stdout", new_callable=StringIO)
+ self._exists = mock.patch("bumblebee.modules.battery.os.path.exists")
+ self._open = mock.patch("bumblebee.modules.battery.open", create=True)
+
+ self.stdout = self._stdout.start()
+ self.exists = self._exists.start()
+ self.open = self._open.start()
+ self.file = mock.Mock()
+ self.file.__enter__ = lambda x: self.file
+ self.file.__exit__ = lambda x, a, b, c: ""
+ self.open.return_value = self.file
+
+ self.exists.return_value = True
+ self.engine = mock.Mock()
+ self.config = Config()
+ self.module = Module(engine=self.engine, config={"config":self.config})
+
+ self.config.set("battery.critical", "20")
+ self.config.set("battery.warning", "25")
+ self.criticalValue = "19"
+ self.warningValue = "21"
+ self.normalValue = "26"
+ self.chargedValue = "96"
+
+ for widget in self.module.widgets():
+ widget.link_module(self.module)
+ self.anyWidget = widget
+
+ def tearDown(self):
+ self._stdout.stop()
+ self._exists.stop()
+ self._open.stop()
+
+ def test_format(self):
+ for widget in self.module.widgets():
+ self.assertEquals(len(widget.full_text()), len("100%"))
+
+ def test_critical(self):
+ self.file.read.return_value = self.criticalValue
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+ def test_warning(self):
+ self.file.read.return_value = self.warningValue
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_normal(self):
+ self.file.read.return_value = self.normalValue
+ self.module.update_all()
+ self.assertTrue(not "warning" in self.module.state(self.anyWidget))
+ self.assertTrue(not "critical" in self.module.state(self.anyWidget))
+
+ def test_overload(self):
+ self.file.read.return_value = "120"
+ self.module.update_all()
+ self.assertTrue(not "warning" in self.module.state(self.anyWidget))
+ self.assertTrue(not "critical" in self.module.state(self.anyWidget))
+ self.assertEquals(self.module.capacity(self.anyWidget), "100%")
+
+ def test_ac(self):
+ self.exists.return_value = False
+ self.module.update_all()
+ self.assertEquals(self.module.capacity(self.anyWidget), "ac")
+ self.assertTrue("AC" in self.module.state(self.anyWidget))
+
+ def test_error(self):
+ self.file.read.side_effect = IOError("failed to read")
+ self.module.update_all()
+ self.assertEquals(self.module.capacity(self.anyWidget), "n/a")
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+ self.assertTrue("unknown" in self.module.state(self.anyWidget))
+
+ def test_charging(self):
+ self.file.read.return_value = self.chargedValue
+ self.module.update_all()
+ self.assertTrue("charged" in self.module.state(self.anyWidget))
+ self.file.read.return_value = self.normalValue
+ self.module.update_all()
+ self.assertTrue("charging" in self.module.state(self.anyWidget))
+
+ def test_discharging(self):
+ for limit in [ 10, 25, 50, 80, 100 ]:
+ value = limit - 1
+ self.file.read.return_value = str(value)
+ self.module.update_all()
+ self.file.read.return_value = "Discharging"
+ self.assertTrue("discharging-{}".format(limit) in self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_brightness.py b/.config/i3/bar/tests/modules/test_brightness.py
new file mode 100644
index 00000000..b636d443
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_brightness.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.input import WHEEL_UP, WHEEL_DOWN
+from bumblebee.modules.brightness import Module
+
+class TestBrightnessModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_format(self):
+ for widget in self.module.widgets():
+ self.assertEquals(len(widget.full_text()), len("100%"))
+
+ def test_wheel_up(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module)
+ self.popen.assert_call("xbacklight +2%")
+
+ def test_wheel_down(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module)
+ self.popen.assert_call("xbacklight -2%")
+
+ def test_custom_step(self):
+ self.config.set("brightness.step", "10")
+ module = Module(engine=self.engine, config={"config": self.config})
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=module)
+ self.popen.assert_call("xbacklight -10%")
+
+ def test_update(self):
+ self.popen.mock.communicate.return_value = ("20.0", None)
+ self.module.update_all()
+ self.assertEquals(self.module.brightness(self.anyWidget), "020%")
+
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_caffeine.py b/.config/i3/bar/tests/modules/test_caffeine.py
new file mode 100644
index 00000000..e95e2ac8
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_caffeine.py
@@ -0,0 +1,53 @@
+# pylint: disable=C0103,C0111
+
+import json
+import unittest
+import mock
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.config import Config
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.caffeine import Module
+
+class TestCaffeineModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ self.xset_active = " timeout: 0 cycle: 123"
+ self.xset_inactive = " timeout: 600 cycle: 123"
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_text(self):
+ self.assertEquals(self.module.caffeine(self.anyWidget), "")
+
+ def test_active(self):
+ self.popen.mock.communicate.return_value = (self.xset_active, None)
+ self.assertTrue(not "deactivated" in self.module.state(self.anyWidget))
+ self.assertTrue("activated" in self.module.state(self.anyWidget))
+
+ def test_inactive(self):
+ self.popen.mock.communicate.return_value = (self.xset_inactive, None)
+ self.assertTrue("deactivated" in self.module.state(self.anyWidget))
+ self.popen.mock.communicate.return_value = ("no text", None)
+ self.assertTrue("deactivated" in self.module.state(self.anyWidget))
+
+ def test_toggle(self):
+ self.popen.mock.communicate.return_value = (self.xset_active, None)
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("xset s default")
+ self.popen.assert_call("notify-send \"Out of coffee\"")
+
+ self.popen.mock.communicate.return_value = (self.xset_inactive, None)
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("xset s off")
+ self.popen.assert_call("notify-send \"Consuming caffeine\"")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_cmus.py b/.config/i3/bar/tests/modules/test_cmus.py
new file mode 100644
index 00000000..ce1f77c9
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_cmus.py
@@ -0,0 +1,114 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.cmus import Module
+
+class TestCmusModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ self.songTemplate = """
+status {status}
+file /path/to/file
+duration {duration}
+position {position}
+tag title {title}
+tag artist {artist}
+tag album {album}
+tag tracknumber 1
+tag date 1984
+tag comment comment
+ """
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_read_song(self):
+ self.popen.mock.communicate.return_value = ("song", None)
+ self.module.update_all()
+ self.popen.assert_call("cmus-remote -Q")
+
+ def test_handle_runtimeerror(self):
+ self.popen.mock.communicate.side_effect = RuntimeError("error loading song")
+ self.module.update_all()
+ self.assertEquals(self.module.description(self.anyWidget), " - /")
+
+ def test_format(self):
+ self.popen.mock.communicate.return_value = (self.songTemplate.format(
+ artist="an artist", title="a title", duration="100", position="20",
+ album="an album", status="irrelevant"
+ ), None)
+ self.module.update_all()
+ self.anyWidget.set("theme.width", 1000)
+ self.assertEquals(self.module.description(self.anyWidget),
+ "an artist - a title 00:20/01:40"
+ )
+
+ def test_scrollable_format(self):
+ self.popen.mock.communicate.return_value = (self.songTemplate.format(
+ artist="an artist", title="a title", duration="100", position="20",
+ album="an album", status="irrelevant"
+ ), None)
+ self.module.update_all()
+ self.anyWidget.set("theme.width", 10)
+ self.assertEquals(self.module.description(self.anyWidget),
+ "an artist - a title 00:20/01:40"[:10]
+ )
+
+ def test_repeat(self):
+ self.popen.mock.communicate.return_value = ("set repeat false", None)
+ self.module.update_all()
+ self.assertTrue("repeat-off" in self.module.state(self.module.widget("cmus.repeat")))
+ self.popen.mock.communicate.return_value = ("set repeat true", None)
+ self.module.update_all()
+ self.assertTrue("repeat-on" in self.module.state(self.module.widget("cmus.repeat")))
+
+ def test_shuffle(self):
+ self.popen.mock.communicate.return_value = ("set shuffle false", None)
+ self.module.update_all()
+ self.assertTrue("shuffle-off" in self.module.state(self.module.widget("cmus.shuffle")))
+ self.popen.mock.communicate.return_value = ("set shuffle true", None)
+ self.module.update_all()
+ self.assertTrue("shuffle-on" in self.module.state(self.module.widget("cmus.shuffle")))
+
+ def test_prevnext(self):
+ self.assertTrue("prev" in self.module.state(self.module.widget("cmus.prev")))
+ self.assertTrue("next" in self.module.state(self.module.widget("cmus.next")))
+
+ def test_main(self):
+ self.popen.mock.communicate.return_value = ("status paused", None)
+ self.module.update_all()
+ self.assertTrue("paused" in self.module.state(self.module.widget("cmus.main")))
+
+ self.popen.mock.communicate.return_value = ("status playing", None)
+ self.module.update_all()
+ self.assertTrue("playing" in self.module.state(self.module.widget("cmus.main")))
+
+ self.popen.mock.communicate.return_value = ("status stopped", None)
+ self.module.update_all()
+ self.assertTrue("stopped" in self.module.state(self.module.widget("cmus.main")))
+
+ def test_widget(self):
+ self.assertEquals(len(self.module.widgets()), 5)
+
+ for idx, val in enumerate(["prev", "main", "next", "shuffle", "repeat"]):
+ self.assertEquals(self.module.widgets()[idx].name, "cmus.{}".format(val))
+
+ def test_interaction(self):
+ events = [
+ {"widget": "cmus.shuffle", "action": "cmus-remote -S"},
+ {"widget": "cmus.repeat", "action": "cmus-remote -R"},
+ {"widget": "cmus.next", "action": "cmus-remote -n"},
+ {"widget": "cmus.prev", "action": "cmus-remote -r"},
+ {"widget": "cmus.main", "action": "cmus-remote -u"},
+ ]
+ for event in events:
+ mocks.mouseEvent(stdin=self.stdin, inp=self.input, module=self.module, instance=self.module.widget(event["widget"]).id, button=LEFT_MOUSE)
+ self.popen.assert_call(event["action"])
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_cpu.py b/.config/i3/bar/tests/modules/test_cpu.py
new file mode 100644
index 00000000..cd5ce899
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_cpu.py
@@ -0,0 +1,45 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.cpu import Module
+
+class TestCPUModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+ self._psutil = mock.patch("bumblebee.modules.cpu.psutil")
+ self.psutil = self._psutil.start()
+
+ def tearDown(self):
+ self._psutil.stop()
+ mocks.teardown_test(self)
+
+ def test_format(self):
+ self.psutil.cpu_percent.return_value = 21.0
+ self.module.update_all()
+ for widget in self.module.widgets():
+ self.assertEquals(len(widget.full_text()), len("100.00%"))
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("gnome-system-monitor")
+
+ def test_warning(self):
+ self.config.set("cpu.critical", "20")
+ self.config.set("cpu.warning", "18")
+ self.psutil.cpu_percent.return_value = 19.0
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("cpu.critical", "20")
+ self.config.set("cpu.warning", "19")
+ self.psutil.cpu_percent.return_value = 21.0
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_disk.py b/.config/i3/bar/tests/modules/test_disk.py
new file mode 100644
index 00000000..16f31c41
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_disk.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.disk import Module
+
+class MockVFS(object):
+ def __init__(self, perc):
+ self.f_blocks = 1024*1024
+ self.f_frsize = 1
+ self.f_bfree = self.f_blocks*(1.0 - perc/100.0)
+
+class TestDiskModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+ self._os = mock.patch("bumblebee.modules.disk.os")
+ self.os = self._os.start()
+ self.config.set("disk.path", "somepath")
+
+ def tearDown(self):
+ self._os.stop()
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ module = Module(engine=self.engine, config={"config":self.config})
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=module)
+ self.popen.assert_call("nautilus {}".format(self.module.parameter("path")))
+
+ def test_warning(self):
+ self.config.set("disk.critical", "80")
+ self.config.set("disk.warning", "70")
+ self.os.statvfs.return_value = MockVFS(75.0)
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("disk.critical", "80")
+ self.config.set("disk.warning", "70")
+ self.os.statvfs.return_value = MockVFS(85.0)
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_load.py b/.config/i3/bar/tests/modules/test_load.py
new file mode 100644
index 00000000..e57bc2b0
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_load.py
@@ -0,0 +1,57 @@
+# pylint: disable=C0103,C0111
+
+import json
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.load import Module
+
+class TestLoadModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ self._mp = mock.patch("bumblebee.modules.load.multiprocessing")
+ self._os = mock.patch("bumblebee.modules.load.os")
+
+ self.mp = self._mp.start()
+ self.os = self._os.start()
+
+ self.mp.cpu_count.return_value = 1
+
+ def tearDown(self):
+ self._mp.stop()
+ self._os.stop()
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("gnome-system-monitor")
+
+ def test_load_format(self):
+ self.os.getloadavg.return_value = [ 5.9, 1.2, 0.8 ]
+ self.module.update_all()
+ self.assertEquals(self.module.load(self.anyWidget), "5.90/1.20/0.80")
+
+ def test_warning(self):
+ self.config.set("load.critical", "1")
+ self.config.set("load.warning", "0.8")
+ self.os.getloadavg.return_value = [ 0.9, 0, 0 ]
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("load.critical", "1")
+ self.config.set("load.warning", "0.8")
+ self.os.getloadavg.return_value = [ 1.1, 0, 0 ]
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+ def test_assume_single_core(self):
+ self.mp.cpu_count.side_effect = NotImplementedError
+ module = Module(engine=self.engine, config={"config": mock.Mock() })
+ self.assertEquals(1, module._cpus)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_memory.py b/.config/i3/bar/tests/modules/test_memory.py
new file mode 100644
index 00000000..94c61f99
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_memory.py
@@ -0,0 +1,52 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.memory import Module
+
+class VirtualMemory(object):
+ def __init__(self, percent):
+ self.percent = percent
+
+class TestMemoryModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+ self._psutil = mock.patch("bumblebee.modules.memory.psutil")
+ self.psutil = self._psutil.start()
+
+ def tearDown(self):
+ self._psutil.stop()
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("gnome-system-monitor")
+
+ def test_warning(self):
+ self.config.set("memory.critical", "80")
+ self.config.set("memory.warning", "70")
+ self.psutil.virtual_memory.return_value = VirtualMemory(75)
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("memory.critical", "80")
+ self.config.set("memory.warning", "70")
+ self.psutil.virtual_memory.return_value = VirtualMemory(81)
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+ def test_usage(self):
+ rv = VirtualMemory(50)
+ rv.total = 1000
+ rv.available = 500
+ self.psutil.virtual_memory.return_value = rv
+ self.module.update_all()
+ self.assertEquals("500.00B/1000.00B (50.00%)", self.module.memory_usage(self.anyWidget))
+ self.assertEquals(None, self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/modules/test_pulseaudio.py b/.config/i3/bar/tests/modules/test_pulseaudio.py
new file mode 100644
index 00000000..f8754c68
--- /dev/null
+++ b/.config/i3/bar/tests/modules/test_pulseaudio.py
@@ -0,0 +1,34 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE, RIGHT_MOUSE, WHEEL_UP, WHEEL_DOWN
+from bumblebee.modules.pulseaudio import Module
+
+class TestPulseAudioModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("pactl set-source-mute @DEFAULT_SOURCE@ toggle")
+
+ def test_rightclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=RIGHT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("pavucontrol")
+
+ def test_wheelup(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module)
+ self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ +2%")
+
+ def test_wheeldown(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module)
+ self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ -2%")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_config.py b/.config/i3/bar/tests/test_config.py
new file mode 100644
index 00000000..395dcb71
--- /dev/null
+++ b/.config/i3/bar/tests/test_config.py
@@ -0,0 +1,77 @@
+# pylint: disable=C0103,C0111
+
+import unittest
+import mock
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+from bumblebee.config import Config
+from bumblebee.theme import themes
+from bumblebee.engine import all_modules
+
+class TestConfig(unittest.TestCase):
+ def setUp(self):
+ self._stdout = mock.patch("bumblebee.config.sys.stdout", new_callable=StringIO)
+ self._stderr = mock.patch("bumblebee.config.sys.stderr", new_callable=StringIO)
+
+ self.stdout = self._stdout.start()
+ self.stderr = self._stderr.start()
+
+ self.defaultConfig = Config()
+ self.someSimpleModules = ["foo", "bar", "baz"]
+ self.someAliasModules = ["foo:a", "bar:b", "baz:c"]
+ self.someTheme = "some-theme"
+
+ def tearDown(self):
+ self._stdout.stop()
+ self._stderr.stop()
+
+ def test_no_modules_by_default(self):
+ self.assertEquals(self.defaultConfig.modules(), [])
+
+ def test_simple_modules(self):
+ cfg = Config(["-m"] + self.someSimpleModules)
+ self.assertEquals(cfg.modules(), [{
+ "name": x, "module": x
+ } for x in self.someSimpleModules])
+
+ def test_alias_modules(self):
+ cfg = Config(["-m"] + self.someAliasModules)
+ self.assertEquals(cfg.modules(), [{
+ "module": x.split(":")[0],
+ "name": x.split(":")[1],
+ } for x in self.someAliasModules])
+
+ def test_parameters(self):
+ cfg = Config(["-m", "module", "-p", "module.key=value"])
+ self.assertEquals(cfg.get("module.key"), "value")
+
+ def test_theme(self):
+ cfg = Config(["-t", self.someTheme])
+ self.assertEquals(cfg.theme(), self.someTheme)
+
+ def test_notheme(self):
+ self.assertEquals(self.defaultConfig.theme(), "default")
+
+ def test_list_themes(self):
+ with self.assertRaises(SystemExit):
+ cfg = Config(["-l", "themes"])
+ result = self.stdout.getvalue()
+ for theme in themes():
+ self.assertTrue(theme in result)
+
+ def test_list_modules(self):
+ with self.assertRaises(SystemExit):
+ cfg = Config(["-l", "modules"])
+ result = self.stdout.getvalue()
+ for module in all_modules():
+ self.assertTrue(module["name"] in result)
+
+ def test_invalid_list(self):
+ with self.assertRaises(SystemExit):
+ cfg = Config(["-l", "invalid"])
+ self.assertTrue("invalid choice" in "".join(self.stderr.getvalue()))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_engine.py b/.config/i3/bar/tests/test_engine.py
new file mode 100644
index 00000000..946f6049
--- /dev/null
+++ b/.config/i3/bar/tests/test_engine.py
@@ -0,0 +1,90 @@
+# pylint: disable=C0103,C0111,W0703,W0212
+
+import shlex
+import unittest
+
+from bumblebee.error import ModuleLoadError
+from bumblebee.engine import Engine
+from bumblebee.config import Config
+import bumblebee.input
+
+from tests.mocks import MockOutput, MockInput
+
+class TestEngine(unittest.TestCase):
+ def setUp(self):
+ self.engine = Engine(config=Config(), output=MockOutput(), inp=MockInput())
+ self.testModule = "test"
+ self.testAlias = "test-alias"
+ self.singleWidgetModule = [{"module": self.testModule, "name": "a"}]
+ self.singleWidgetAlias = [{"module": self.testAlias, "name": "a" }]
+ self.invalidModule = "no-such-module"
+ self.testModuleSpec = "bumblebee.modules.{}".format(self.testModule)
+ self.testModules = [
+ {"module": "test", "name": "a"},
+ {"module": "test", "name": "b"},
+ ]
+
+ def test_stop(self):
+ self.assertTrue(self.engine.running())
+ self.engine.stop()
+ self.assertFalse(self.engine.running())
+
+ def test_load_module(self):
+ module = self.engine._load_module(self.testModule)
+ self.assertEquals(module.__module__, self.testModuleSpec)
+
+ def test_load_invalid_module(self):
+ with self.assertRaises(ModuleLoadError):
+ self.engine._load_module(self.invalidModule)
+
+ def test_load_none(self):
+ with self.assertRaises(ModuleLoadError):
+ self.engine._load_module(None)
+
+ def test_load_modules(self):
+ modules = self.engine.load_modules(self.testModules)
+ self.assertEquals(len(modules), len(self.testModules))
+ self.assertEquals(
+ [module.__module__ for module in modules],
+ [self.testModuleSpec for module in modules]
+ )
+
+ def test_run(self):
+ self.engine.load_modules(self.singleWidgetModule)
+ try:
+ self.engine.run()
+ except Exception as e:
+ self.fail(e)
+
+ def test_aliases(self):
+ modules = self.engine.load_modules(self.singleWidgetAlias)
+ self.assertEquals(len(modules), 1)
+ self.assertEquals(modules[0].__module__, self.testModuleSpec)
+
+ def test_custom_cmd(self):
+ testmodules = [
+ { "name": "test", "button": "test.left-click", "action": "echo" },
+ { "name": "test:alias", "button": "alias.right-click", "action": "echo2" },
+ ]
+ cmd = "-m"
+ for test in testmodules:
+ cmd += " " + test["name"]
+ cmd += " -p"
+ for test in testmodules:
+ cmd += " " + test["button"] + "=" + test["action"]
+ cfg = Config(shlex.split(cmd))
+ inp = MockInput()
+ engine = Engine(config=cfg, output=MockOutput(), inp=inp)
+
+ i = 0
+ for module in engine.modules():
+ callback = inp.get_callback(module.id)
+ self.assertTrue(callback is not None)
+ self.assertEquals(callback["command"], testmodules[i]["action"])
+ if "left" in testmodules[i]["button"]:
+ self.assertTrue(callback["button"], bumblebee.input.LEFT_MOUSE)
+ if "right" in testmodules[i]["button"]:
+ self.assertTrue(callback["button"], bumblebee.input.RIGHT_MOUSE)
+ i += 1
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_i3barinput.py b/.config/i3/bar/tests/test_i3barinput.py
new file mode 100644
index 00000000..6852f97b
--- /dev/null
+++ b/.config/i3/bar/tests/test_i3barinput.py
@@ -0,0 +1,110 @@
+# pylint: disable=C0103,C0111
+
+import json
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import I3BarInput, LEFT_MOUSE, RIGHT_MOUSE
+
+class TestI3BarInput(unittest.TestCase):
+ def setUp(self):
+ self.input = I3BarInput()
+ self.input.need_event = True
+
+ self._stdin = mock.patch("bumblebee.input.sys.stdin")
+ self.stdin = self._stdin.start()
+ self._select = mock.patch("bumblebee.input.select")
+ self.select = self._select.start()
+ self.popen = mocks.MockPopen()
+
+ self.stdin.fileno.return_value = 1
+ epoll = mock.Mock()
+ self.select.epoll.return_value = epoll
+
+ epoll.poll.return_value = [(self.stdin.fileno.return_value, 2)]
+
+ self.anyModule = mock.Mock()
+ self.anyModule.id = mocks.rand(10)
+ self.anotherModule = mock.Mock()
+ self.anotherModule.id = mocks.rand(10)
+ self.anyWidget = mocks.MockWidget("some-widget")
+ self.anotherWidget = mocks.MockWidget("another-widget")
+ self.anyData = self.invalidData = "any data"
+ self.invalidEvent = json.dumps({"name": None, "instance": None, "button": 1})
+ self.incompleteEvent = json.dumps({"button": 1})
+ self.anyCommand = "this is a command with arguments"
+
+ self._called = 0
+
+ def tearDown(self):
+ self._stdin.stop()
+ self._select.stop()
+ self.popen.cleanup()
+
+ def callback(self, event):
+ self._called += 1
+
+ def calls(self):
+ rv = self._called
+ self._called = 0
+ return rv
+
+ def test_read_event(self):
+ self.stdin.readline.return_value = self.anyData
+ self.input.start()
+ self.input.stop()
+ self.stdin.readline.assert_any_call()
+
+ def test_ignore_invalid_input(self):
+ for data in [ self.invalidData, self.incompleteEvent, self.invalidEvent ]:
+ self.stdin.readline.return_value = data
+ self.input.start()
+ self.assertEquals(self.input.alive(), True)
+ self.assertEquals(self.input.stop(), True)
+ self.stdin.readline.assert_any_call()
+
+ def test_global_callback(self):
+ self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin)
+ self.assertTrue(self.calls() > 0)
+
+ def test_remove_global_callback(self):
+ self.test_global_callback()
+ self.input.deregister_callbacks(None)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin)
+ self.assertTrue(self.calls() == 0)
+
+ def test_global_callback_wrong_button(self):
+ self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=RIGHT_MOUSE, inp=self.input, stdin=self.stdin)
+ self.assertTrue(self.calls() == 0)
+
+ def test_module_callback(self):
+ self.input.register_callback(self.anyModule, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule)
+ self.assertTrue(self.calls() > 0)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherModule)
+ self.assertTrue(self.calls() == 0)
+
+ def test_remove_module_callback(self):
+ self.test_module_callback()
+ self.input.deregister_callbacks(self.anyModule)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule)
+ self.assertTrue(self.calls() == 0)
+
+ def test_widget_callback(self):
+ self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget)
+ self.assertTrue(self.calls() > 0)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherWidget)
+ self.assertTrue(self.calls() == 0)
+
+ def test_widget_cmd_callback(self):
+ self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.anyCommand)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget)
+ self.popen.assert_call(self.anyCommand)
+
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_i3baroutput.py b/.config/i3/bar/tests/test_i3baroutput.py
new file mode 100644
index 00000000..8c566efb
--- /dev/null
+++ b/.config/i3/bar/tests/test_i3baroutput.py
@@ -0,0 +1,133 @@
+# pylint: disable=C0103,C0111
+
+import json
+import mock
+import unittest
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.output import I3BarOutput
+
+class TestI3BarOutput(unittest.TestCase):
+ def setUp(self):
+ self.theme = mock.Mock()
+ self.theme.separator_fg.return_value = "#123456"
+ self.theme.separator_bg.return_value = "#000000"
+ self.theme.separator.return_value = ""
+ self.theme.prefix.return_value = ""
+ self.theme.suffix.return_value = ""
+ self.theme.separator_block_width.return_value = 1
+ self.theme.fg.return_value = "#ababab"
+ self.theme.bg.return_value = "#ababab"
+ self.theme.align.return_value = None
+ self.theme.minwidth.return_value = ""
+ self.output = I3BarOutput(self.theme)
+
+ self._stdout = mock.patch("bumblebee.output.sys.stdout", new_callable=StringIO)
+ self.stdout = self._stdout.start()
+
+ self.anyWidget = mocks.MockWidget("some text")
+ self.anyModule = mock.Mock()
+ self.anyModule.id = mocks.rand(10)
+ self.anyModule.name = mocks.rand(10)
+
+ self.expectedStart = json.dumps({"version": 1, "click_events": True}) + "\n[\n"
+ self.expectedStop = "]\n"
+
+ self.anyColor = "#ffffff"
+ self.anotherColor = "#cdcdcd"
+
+ def tearDown(self):
+ self._stdout.stop()
+
+ def test_start(self):
+ self.output.start()
+ self.assertEquals(self.expectedStart, self.stdout.getvalue())
+
+ def test_stop(self):
+ self.output.stop()
+ self.assertEquals(self.expectedStop, self.stdout.getvalue())
+
+ def test_draw_single_widget(self):
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], self.anyWidget.full_text())
+
+ def test_draw_multiple_widgets(self):
+ for i in range(4):
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())
+ for res in result:
+ self.assertEquals(res["full_text"], self.anyWidget.full_text())
+
+ def test_begin(self):
+ self.output.begin()
+ self.assertEquals("", self.stdout.getvalue())
+
+ def test_end(self):
+ self.output.end()
+ self.assertEquals(",\n", self.stdout.getvalue())
+
+ def test_prefix(self):
+ self.theme.prefix.return_value = " - "
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], " - {}".format(self.anyWidget.full_text()))
+
+ def test_suffix(self):
+ self.theme.suffix.return_value = " - "
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], "{} - ".format(self.anyWidget.full_text()))
+
+ def test_bothfix(self):
+ self.theme.prefix.return_value = "*"
+ self.theme.suffix.return_value = " - "
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], "*{} - ".format(self.anyWidget.full_text()))
+
+ def test_colors(self):
+ self.theme.fg.return_value = self.anyColor
+ self.theme.bg.return_value = self.anotherColor
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["color"], self.anyColor)
+ self.assertEquals(result["background"], self.anotherColor)
+
+ def test_widget_link(self):
+ self.anyWidget.link_module(self.anyModule)
+ self.assertEquals(self.anyWidget._module, self.anyModule)
+ self.assertEquals(self.anyWidget.module, self.anyModule.name)
+
+ def test_unlinked_widget_state(self):
+ state = self.anyWidget.state()
+ self.assertTrue(type(state) == list)
+
+ def test_linked_widget_state(self):
+ self.anyWidget.link_module(self.anyModule)
+ for lst in [ "samplestate", ["a", "b", "c"], [] ]:
+ self.anyModule.state.return_value = lst
+ state = self.anyWidget.state()
+ self.assertEquals(type(state), list)
+ if type(lst) is not list: lst = [lst]
+ self.assertEquals(state, lst)
+
+ def test_widget_fulltext(self):
+ self.anyWidget.full_text("some text")
+ self.assertEquals(self.anyWidget.full_text(), "some text")
+ self.anyWidget.full_text(lambda x: "callable fulltext")
+ self.assertEquals(self.anyWidget.full_text(), "callable fulltext")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_module.py b/.config/i3/bar/tests/test_module.py
new file mode 100644
index 00000000..cf663dcb
--- /dev/null
+++ b/.config/i3/bar/tests/test_module.py
@@ -0,0 +1,89 @@
+# pylint: disable=C0103,C0111,W0703
+
+import unittest
+
+from bumblebee.engine import Module
+from bumblebee.config import Config
+from tests.mocks import MockWidget
+
+class TestModule(unittest.TestCase):
+ def setUp(self):
+ self.widgetName = "foo"
+ self.widget = MockWidget(self.widgetName)
+ self.config = Config()
+ self.anyWidgetName = "random-widget-name"
+ self.noSuchModule = "this-module-does-not-exist"
+ self.moduleWithoutWidgets = Module(engine=None, widgets=None)
+ self.moduleWithOneWidget = Module(engine=None, widgets=self.widget, config={"config": self.config})
+ self.moduleWithMultipleWidgets = Module(engine=None,
+ widgets=[self.widget, self.widget, self.widget]
+ )
+
+ self.anyConfigName = "cfg"
+ self.anotherConfigName = "cfg2"
+ self.anyModule = Module(engine=None, widgets=self.widget, config={
+ "name": self.anyConfigName, "config": self.config
+ })
+ self.anotherModule = Module(engine=None, widgets=self.widget, config={
+ "name": self.anotherConfigName, "config": self.config
+ })
+ self.anyKey = "some-parameter"
+ self.anyValue = "value"
+ self.anotherValue = "another-value"
+ self.emptyKey = "i-do-not-exist"
+ self.config.set("{}.{}".format(self.anyConfigName, self.anyKey), self.anyValue)
+ self.config.set("{}.{}".format(self.anotherConfigName, self.anyKey), self.anotherValue)
+
+ def test_empty_widgets(self):
+ self.assertEquals(self.moduleWithoutWidgets.widgets(), [])
+
+ def test_single_widget(self):
+ self.assertEquals(self.moduleWithOneWidget.widgets(), [self.widget])
+
+ def test_multiple_widgets(self):
+ for widget in self.moduleWithMultipleWidgets.widgets():
+ self.assertEquals(widget, self.widget)
+
+ def test_retrieve_widget_by_name(self):
+ widget = MockWidget(self.anyWidgetName)
+ widget.name = self.anyWidgetName
+ module = Module(engine=None, widgets=[self.widget, widget, self.widget])
+ retrievedWidget = module.widget(self.anyWidgetName)
+ self.assertEquals(retrievedWidget, widget)
+
+ def test_retrieve_widget_by_id(self):
+ widget = MockWidget(self.anyWidgetName)
+ widget.id = self.anyWidgetName
+ module = Module(engine=None, widgets=[self.widget, widget, self.widget])
+ retrievedWidget = module.widget_by_id(self.anyWidgetName)
+ self.assertEquals(retrievedWidget, widget)
+
+ def test_retrieve_missing_widget(self):
+ module = self.moduleWithMultipleWidgets
+
+ widget = module.widget(self.noSuchModule)
+ self.assertEquals(widget, None)
+
+ widget = module.widget_by_id(self.noSuchModule)
+ self.assertEquals(widget, None)
+
+ def test_threshold(self):
+ module = self.moduleWithOneWidget
+ module.name = self.widgetName
+
+ self.config.set("{}.critical".format(self.widgetName), 10.0)
+ self.config.set("{}.warning".format(self.widgetName), 8.0)
+ self.assertEquals("critical", module.threshold_state(10.1, 0, 0))
+ self.assertEquals("warning", module.threshold_state(10.0, 0, 0))
+ self.assertEquals(None, module.threshold_state(8.0, 0, 0))
+ self.assertEquals(None, module.threshold_state(7.9, 0, 0))
+
+ def test_parameters(self):
+ self.assertEquals(self.anyModule.parameter(self.anyKey), self.anyValue)
+ self.assertEquals(self.anotherModule.parameter(self.anyKey), self.anotherValue)
+
+ def test_default_parameters(self):
+ self.assertEquals(self.anyModule.parameter(self.emptyKey), None)
+ self.assertEquals(self.anyModule.parameter(self.emptyKey, self.anyValue), self.anyValue)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_modules.py b/.config/i3/bar/tests/test_modules.py
new file mode 100644
index 00000000..4b280a32
--- /dev/null
+++ b/.config/i3/bar/tests/test_modules.py
@@ -0,0 +1,58 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+import importlib
+
+import tests.mocks as mocks
+
+from bumblebee.engine import all_modules
+from bumblebee.output import Widget
+from bumblebee.config import Config
+
+class TestGenericModules(unittest.TestCase):
+ def setUp(self):
+ engine = mock.Mock()
+ engine.input = mock.Mock()
+ config = Config()
+ self.objects = {}
+
+ self.popen = mocks.MockPopen()
+ self.popen.mock.communicate.return_value = (str.encode("1"), "error")
+ self.popen.mock.returncode = 0
+
+ self._platform = mock.patch("bumblebee.modules.kernel.platform")
+ self.platform = self._platform.start()
+ self.platform.release.return_value = "unknown linux v1"
+
+ for mod in all_modules():
+ name = "bumblebee.modules.{}".format(mod["name"])
+ cls = importlib.import_module(name)
+ self.objects[mod["name"]] = getattr(cls, "Module")(engine, {"config": config})
+ for widget in self.objects[mod["name"]].widgets():
+ self.assertEquals(widget.get("variable", None), None)
+
+ def tearDown(self):
+ self._platform.stop()
+ self.popen.cleanup()
+
+ def test_widgets(self):
+ for mod in self.objects:
+ widgets = self.objects[mod].widgets()
+ for widget in widgets:
+ widget.link_module(self.objects[mod])
+ self.assertEquals(widget.module, mod)
+ self.assertTrue(isinstance(widget, Widget))
+ self.assertTrue(hasattr(widget, "full_text"))
+ widget.set("variable", "value")
+ self.assertEquals(widget.get("variable", None), "value")
+ self.assertTrue(isinstance(widget.full_text(), str) or isinstance(widget.full_text(), unicode))
+
+ def test_update(self):
+ for mod in self.objects:
+ widgets = self.objects[mod].widgets()
+ self.objects[mod].update(widgets)
+ self.test_widgets()
+ self.assertEquals(widgets, self.objects[mod].widgets())
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_store.py b/.config/i3/bar/tests/test_store.py
new file mode 100644
index 00000000..faa1efb7
--- /dev/null
+++ b/.config/i3/bar/tests/test_store.py
@@ -0,0 +1,26 @@
+# pylint: disable=C0103,C0111,W0703
+
+import unittest
+
+from bumblebee.store import Store
+
+class TestStore(unittest.TestCase):
+ def setUp(self):
+ self.store = Store()
+ self.anyKey = "some-key"
+ self.anyValue = "some-value"
+ self.unsetKey = "invalid-key"
+
+ def test_set_value(self):
+ self.store.set(self.anyKey, self.anyValue)
+ self.assertEquals(self.store.get(self.anyKey), self.anyValue)
+
+ def test_get_invalid_value(self):
+ result = self.store.get(self.unsetKey)
+ self.assertEquals(result, None)
+
+ def test_get_invalid_with_default_value(self):
+ result = self.store.get(self.unsetKey, self.anyValue)
+ self.assertEquals(result, self.anyValue)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_theme.py b/.config/i3/bar/tests/test_theme.py
new file mode 100644
index 00000000..747d7498
--- /dev/null
+++ b/.config/i3/bar/tests/test_theme.py
@@ -0,0 +1,134 @@
+# pylint: disable=C0103,C0111,W0703
+
+import mock
+import unittest
+from bumblebee.theme import Theme
+from bumblebee.error import ThemeLoadError
+from tests.mocks import MockWidget
+
+class TestTheme(unittest.TestCase):
+ def setUp(self):
+ self.nonexistentThemeName = "no-such-theme"
+ self.invalidThemeName = "test_invalid"
+ self.validThemeName = "test"
+ self.validThemeSeparator = " * "
+ self.themedWidget = MockWidget("bla")
+ self.theme = Theme(self.validThemeName)
+ self.cycleTheme = Theme("test_cycle")
+ self.anyModule = mock.Mock()
+ self.anyWidget = MockWidget("bla")
+ self.anotherWidget = MockWidget("blub")
+
+ self.anyModule.state.return_value = "state-default"
+
+ self.anyWidget.link_module(self.anyModule)
+ self.themedWidget.link_module(self.anyModule)
+
+ data = self.theme.data()
+ self.widgetTheme = "test-widget"
+ self.themedWidget.module = self.widgetTheme
+ self.defaultColor = data["defaults"]["fg"]
+ self.defaultBgColor = data["defaults"]["bg"]
+ self.widgetColor = data[self.widgetTheme]["fg"]
+ self.widgetBgColor = data[self.widgetTheme]["bg"]
+ self.defaultPrefix = data["defaults"]["prefix"]
+ self.defaultSuffix = data["defaults"]["suffix"]
+ self.widgetPrefix = data[self.widgetTheme]["prefix"]
+ self.widgetSuffix = data[self.widgetTheme]["suffix"]
+
+ def test_load_valid_theme(self):
+ try:
+ Theme(self.validThemeName)
+ except Exception as e:
+ self.fail(e)
+
+ def test_load_nonexistent_theme(self):
+ with self.assertRaises(ThemeLoadError):
+ Theme(self.nonexistentThemeName)
+
+ def test_load_invalid_theme(self):
+ with self.assertRaises(ThemeLoadError):
+ Theme(self.invalidThemeName)
+
+ def test_default_prefix(self):
+ self.assertEquals(self.theme.prefix(self.anyWidget), self.defaultPrefix)
+
+ def test_default_suffix(self):
+ self.assertEquals(self.theme.suffix(self.anyWidget), self.defaultSuffix)
+
+ def test_widget_prefix(self):
+ self.assertEquals(self.theme.prefix(self.themedWidget), self.widgetPrefix)
+
+ def test_widget_fg(self):
+ self.assertEquals(self.theme.fg(self.anyWidget), self.defaultColor)
+ self.anyWidget.module = self.widgetTheme
+ self.assertEquals(self.theme.fg(self.anyWidget), self.widgetColor)
+
+ def test_widget_bg(self):
+ self.assertEquals(self.theme.bg(self.anyWidget), self.defaultBgColor)
+ self.anyWidget.module = self.widgetTheme
+ self.assertEquals(self.theme.bg(self.anyWidget), self.widgetBgColor)
+
+ def test_absent_cycle(self):
+ theme = self.theme
+ try:
+ theme.fg(self.anyWidget)
+ theme.fg(self.anotherWidget)
+ except Exception as e:
+ self.fail(e)
+
+ def test_reset(self):
+ theme = self.cycleTheme
+ data = theme.data()
+ theme.reset()
+ self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"])
+ self.assertEquals(theme.fg(self.anotherWidget), data["cycle"][1]["fg"])
+ theme.reset()
+ self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"])
+
+ def test_separator_block_width(self):
+ theme = self.theme
+ data = theme.data()
+
+ self.assertEquals(theme.separator_block_width(self.anyWidget),
+ data["defaults"]["separator-block-width"]
+ )
+
+ def test_separator(self):
+ for theme in [self.theme, self.cycleTheme]:
+ theme.reset()
+ prev_bg = theme.bg(self.anyWidget)
+ theme.bg(self.anotherWidget)
+
+ self.assertEquals(theme.separator_fg(self.anotherWidget), theme.bg(self.anotherWidget))
+ self.assertEquals(theme.separator_bg(self.anotherWidget), prev_bg)
+
+ def test_state(self):
+ theme = self.theme
+ data = theme.data()
+
+ self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["fg"])
+ self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["bg"])
+
+ self.anyModule.state.return_value = "critical"
+ self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["critical"]["fg"])
+ self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["critical"]["bg"])
+ self.assertEquals(theme.fg(self.themedWidget), data[self.widgetTheme]["critical"]["fg"])
+ # if elements are missing in the state theme, they are taken from the
+ # widget theme instead (i.e. no fallback to a more general state theme)
+ self.assertEquals(theme.bg(self.themedWidget), data[self.widgetTheme]["bg"])
+
+ def test_separator(self):
+ self.assertEquals(self.validThemeSeparator, self.theme.separator(self.anyWidget))
+
+ def test_list(self):
+ theme = self.theme
+ data = theme.data()[self.widgetTheme]["cycle-test"]["fg"]
+ self.anyModule.state.return_value = "cycle-test"
+ self.assertTrue(len(data) > 1)
+
+ for idx in range(0, len(data)):
+ self.assertEquals(theme.fg(self.themedWidget), data[idx])
+ self.assertEquals(theme.fg(self.themedWidget), data[0])
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/tests/test_util.py b/.config/i3/bar/tests/test_util.py
new file mode 100644
index 00000000..19887568
--- /dev/null
+++ b/.config/i3/bar/tests/test_util.py
@@ -0,0 +1,56 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.util import *
+
+class TestUtil(unittest.TestCase):
+ def setUp(self):
+ self.popen = mocks.MockPopen("bumblebee.util")
+ self.some_command_with_args = "sample-command -a -b -c"
+ self.some_utf8 = "some string".encode("utf-8")
+
+ def tearDown(self):
+ self.popen.cleanup()
+
+ def test_bytefmt(self):
+ self.assertEquals(bytefmt(10), "10.00B")
+ self.assertEquals(bytefmt(15*1024), "15.00KiB")
+ self.assertEquals(bytefmt(20*1024*1024), "20.00MiB")
+ self.assertEquals(bytefmt(22*1024*1024*1024), "22.00GiB")
+ self.assertEquals(bytefmt(35*1024*1024*1024*1024), "35840.00GiB")
+
+ def test_durationfmt(self):
+ self.assertEquals(durationfmt(00), "00:00")
+ self.assertEquals(durationfmt(25), "00:25")
+ self.assertEquals(durationfmt(60), "01:00")
+ self.assertEquals(durationfmt(119), "01:59")
+ self.assertEquals(durationfmt(3600), "01:00:00")
+ self.assertEquals(durationfmt(7265), "02:01:05")
+
+ def test_execute(self):
+ execute(self.some_command_with_args)
+ self.assertTrue(self.popen.mock.popen.called)
+ self.popen.mock.popen.assert_call(self.some_command_with_args)
+ self.assertTrue(self.popen.mock.communicate.called)
+
+ def test_execute_nowait(self):
+ execute(self.some_command_with_args, False)
+ self.assertTrue(self.popen.mock.popen.called)
+ self.popen.mock.popen.assert_call(self.some_command_with_args)
+ self.assertFalse(self.popen.mock.communicate.called)
+
+ def test_execute_utf8(self):
+ self.popen.mock.communicate.return_value = [ self.some_utf8, None ]
+ self.test_execute()
+
+ def test_execute_error(self):
+ self.popen.mock.returncode = 1
+
+ with self.assertRaises(RuntimeError):
+ execute(self.some_command_with_args)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/.config/i3/bar/themes/default.json b/.config/i3/bar/themes/default.json
new file mode 100644
index 00000000..ddda5fe9
--- /dev/null
+++ b/.config/i3/bar/themes/default.json
@@ -0,0 +1,7 @@
+{
+ "icons": [ "ascii" ],
+ "defaults": {
+ "urgent": true,
+ "fg": "#aabbcc"
+ }
+}
diff --git a/.config/i3/bar/themes/gruvbox-powerline.json b/.config/i3/bar/themes/gruvbox-powerline.json
new file mode 100644
index 00000000..5cbfdc0f
--- /dev/null
+++ b/.config/i3/bar/themes/gruvbox-powerline.json
@@ -0,0 +1,41 @@
+{
+ "icons": [ "paxy97", "awesome-fonts" ],
+ "defaults": {
+ "warning": {
+ "fg": "#1d2021",
+ "bg": "#d79921"
+ },
+ "critical": {
+ "fg": "#fbf1c7",
+ "bg": "#cc241d"
+ },
+ "default-separators": false,
+ "separator-block-width": 0
+ },
+ "cycle": [
+ {
+ "fg": "#ebdbb2",
+ "bg": "#1d2021"
+ },
+ {
+ "fg": "#fbf1c7",
+ "bg": "#282828"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ },
+ "AC": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ }
+ }
+}
diff --git a/.config/i3/bar/themes/gruvbox.json b/.config/i3/bar/themes/gruvbox.json
new file mode 100644
index 00000000..eee93c1d
--- /dev/null
+++ b/.config/i3/bar/themes/gruvbox.json
@@ -0,0 +1,41 @@
+{
+ "icons": [ "paxy97", "ascii" ],
+ "defaults": {
+ "warning": {
+ "fg": "#1d2021",
+ "bg": "#d79921"
+ },
+ "critical": {
+ "fg": "#fbf1c7",
+ "bg": "#cc241d"
+ },
+ "default-separators": false,
+ "separator-block-width": 0
+ },
+ "cycle": [
+ {
+ "fg": "#ebdbb2",
+ "bg": "#1d2021"
+ },
+ {
+ "fg": "#fbf1c7",
+ "bg": "#282828"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ },
+ "AC": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ }
+ }
+}
diff --git a/.config/i3/bar/themes/icons/ascii.json b/.config/i3/bar/themes/icons/ascii.json
new file mode 100644
index 00000000..9b03a462
--- /dev/null
+++ b/.config/i3/bar/themes/icons/ascii.json
@@ -0,0 +1,70 @@
+{
+ "defaults": {
+ "padding": " "
+ },
+ "memory": { "prefix": "ram" },
+ "cpu": { "prefix": "cpu" },
+ "disk": { "prefix": "hdd" },
+ "dnf": { "prefix": "dnf" },
+ "brightness": { "prefix": "o" },
+ "cmus": {
+ "playing": { "prefix": ">" },
+ "paused": { "prefix": "||" },
+ "stopped": { "prefix": "[]" },
+ "prev": { "prefix": "|<" },
+ "next": { "prefix": ">|" },
+ "shuffle-on": { "prefix": "S" },
+ "shuffle-off": { "prefix": "[s]" },
+ "repeat-on": { "prefix": "R" },
+ "repeat-off": { "prefix": "[r]" }
+ },
+ "pasink": {
+ "muted": { "prefix": "audio(mute)" },
+ "unmuted": { "prefix": "audio" }
+ },
+ "amixer": {
+ "muted": { "prefix": "audio(mute)" },
+ "unmuted": { "prefix": "audio" }
+ },
+ "pasource": {
+ "muted": { "prefix": "mic(mute)" },
+ "unmuted": { "prefix": "mic" }
+ },
+ "nic": {
+ "wireless-up": { "prefix": "wifi" },
+ "wireless-down": { "prefix": "wifi" },
+ "wired-up": { "prefix": "lan" },
+ "wired-down": { "prefix": "lan" },
+ "tunnel-up": { "prefix": "tun" },
+ "tunnel-down": { "prefix": "tun" }
+ },
+ "battery": {
+ "charged": { "suffix": "full" },
+ "charging": { "suffix": "chr" },
+ "AC": { "suffix": "ac" },
+ "discharging-10": {
+ "prefix": "!",
+ "suffix": "dis"
+ },
+ "discharging-25": { "suffix": "dis" },
+ "discharging-50": { "suffix": "dis" },
+ "discharging-80": { "suffix": "dis" },
+ "discharging-100": { "suffix": "dis" }
+ },
+ "caffeine": {
+ "activated": {"prefix": "caf-on" }, "deactivated": { "prefix": "caf-off " }
+ },
+ "xrandr": {
+ "on": { "prefix": " off "}, "off": { "prefix": " on "}
+ },
+ "redshift": {
+ "day": { "prefix": "day" }, "night": { "prefix": "night" }, "transition": { "prefix": "trans" }
+ },
+ "sensors": {
+ "prefix": "sensors"
+ },
+ "traffic": {
+ "rx": { "prefix": "down"},
+ "tx": { "prefix": "up"}
+ }
+}
diff --git a/.config/i3/bar/themes/icons/awesome-fonts.json b/.config/i3/bar/themes/icons/awesome-fonts.json
new file mode 100644
index 00000000..4263a19f
--- /dev/null
+++ b/.config/i3/bar/themes/icons/awesome-fonts.json
@@ -0,0 +1,86 @@
+{
+ "defaults": {
+ "separator": "", "padding": " ",
+ "unknown": { "prefix": "" }
+ },
+ "date": { "prefix": "" },
+ "time": { "prefix": "" },
+ "memory": { "prefix": "" },
+ "cpu": { "prefix": "" },
+ "disk": { "prefix": "" },
+ "dnf": { "prefix": "" },
+ "pacman": { "prefix": "" },
+ "brightness": { "prefix": "" },
+ "load": { "prefix": "" },
+ "layout": { "prefix": "" },
+ "cmus": {
+ "playing": { "prefix": "" },
+ "paused": { "prefix": "" },
+ "stopped": { "prefix": "" },
+ "prev": { "prefix": "" },
+ "next": { "prefix": "" },
+ "shuffle-on": { "prefix": "" },
+ "shuffle-off": { "prefix": "" },
+ "repeat-on": { "prefix": "" },
+ "repeat-off": { "prefix": "" }
+ },
+ "gpmdp": {
+ "playing": { "prefix": "" },
+ "paused": { "prefix": "" },
+ "stopped": { "prefix": "" },
+ "prev": { "prefix": "" },
+ "next": { "prefix": "" }
+ },
+ "pasink": {
+ "muted": { "prefix": "" },
+ "unmuted": { "prefix": "" }
+ },
+ "amixer": {
+ "muted": { "prefix": "" },
+ "unmuted": { "prefix": "" }
+ },
+ "pasource": {
+ "muted": { "prefix": "" },
+ "unmuted": { "prefix": "" }
+ },
+ "kernel": {
+ "prefix": "\uf17c"
+ },
+ "nic": {
+ "wireless-up": { "prefix": "" },
+ "wireless-down": { "prefix": "" },
+ "wired-up": { "prefix": "" },
+ "wired-down": { "prefix": "" },
+ "tunnel-up": { "prefix": "" },
+ "tunnel-down": { "prefix": "" }
+ },
+ "battery": {
+ "charged": { "prefix": "", "suffix": "" },
+ "AC": { "suffix": "" },
+ "charging": {
+ "prefix": [ "", "", "", "", "" ],
+ "suffix": ""
+ },
+ "discharging-10": { "prefix": "", "suffix": "" },
+ "discharging-25": { "prefix": "", "suffix": "" },
+ "discharging-50": { "prefix": "", "suffix": "" },
+ "discharging-80": { "prefix": "", "suffix": "" },
+ "discharging-100": { "prefix": "", "suffix": "" }
+ },
+ "caffeine": {
+ "activated": {"prefix": " " }, "deactivated": { "prefix": " " }
+ },
+ "xrandr": {
+ "on": { "prefix": " "}, "off": { "prefix": " " }
+ },
+ "redshift": {
+ "day": { "prefix": "" }, "night": { "prefix": "" }, "transition": { "prefix": "" }
+ },
+ "sensors": {
+ "prefix": "🌡"
+ },
+ "traffic":{
+ "rx": { "prefix": "" },
+ "tx": { "prefix": "" }
+ }
+}
diff --git a/.config/i3/bar/themes/icons/paxy97.json b/.config/i3/bar/themes/icons/paxy97.json
new file mode 100644
index 00000000..9a889ab7
--- /dev/null
+++ b/.config/i3/bar/themes/icons/paxy97.json
@@ -0,0 +1,5 @@
+{
+ "memory": {
+ "prefix": " "
+ }
+}
diff --git a/.config/i3/bar/themes/icons/test.json b/.config/i3/bar/themes/icons/test.json
new file mode 100644
index 00000000..ad17178b
--- /dev/null
+++ b/.config/i3/bar/themes/icons/test.json
@@ -0,0 +1,6 @@
+{
+ "test-widget": {
+ "prefix": "widget-prefix",
+ "suffix": "widget-suffix"
+ }
+}
diff --git a/.config/i3/bar/themes/kulade.json b/.config/i3/bar/themes/kulade.json
new file mode 100644
index 00000000..40f64e91
--- /dev/null
+++ b/.config/i3/bar/themes/kulade.json
@@ -0,0 +1,46 @@
+{
+ "icons": [ "awesome-fonts" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "warning": {
+ "fg": "#8C001A",
+ "bg": "#ffffff"
+ },
+ "critical": {
+ "fg": "#ffffff",
+ "bg": "#8C001A"
+ },
+ "good": {
+ "fg": "#000000",
+ "bg": "#008800"
+ },
+ "great": {
+ "fg": "#000000",
+ "bg": "#008080"
+ },
+ "mid": {
+ "fg": "#000000",
+ "bg": "#808000"
+ }
+ },
+ "cycle": [
+ { "fg": "#93a1a1", "bg": "#000000" },
+ { "fg": "#eee8d5", "bg": "#333333" }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#000000",
+ "bg": "#008080"
+ },
+ "AC": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ }
+}
diff --git a/.config/i3/bar/themes/powerline.json b/.config/i3/bar/themes/powerline.json
new file mode 100644
index 00000000..80be8cf1
--- /dev/null
+++ b/.config/i3/bar/themes/powerline.json
@@ -0,0 +1,41 @@
+{
+ "icons": [ "awesome-fonts" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "critical": {
+ "fg": "#ffffff",
+ "bg": "#ff0000"
+ },
+ "warning": {
+ "fg": "#d75f00",
+ "bg": "#ffd700"
+ },
+ "default_separators": false
+ },
+ "cycle": [
+ {
+ "fg": "#ffd700",
+ "bg": "#d75f00"
+ },
+ {
+ "fg": "#ffffff",
+ "bg": "#0087af"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#494949",
+ "bg": "#41db00"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#494949",
+ "bg": "#41db00"
+ },
+ "AC": {
+ "fg": "#494949",
+ "bg": "#41db00"
+ }
+ }
+}
diff --git a/.config/i3/bar/themes/solarized-powerline.json b/.config/i3/bar/themes/solarized-powerline.json
new file mode 100644
index 00000000..e1fbe1d0
--- /dev/null
+++ b/.config/i3/bar/themes/solarized-powerline.json
@@ -0,0 +1,40 @@
+{
+ "icons": [ "awesome-fonts" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "warning": {
+ "fg": "#002b36",
+ "bg": "#b58900"
+ },
+ "critical": {
+ "fg": "#002b36",
+ "bg": "#dc322f"
+ }
+ },
+ "cycle": [
+ { "fg": "#93a1a1", "bg": "#002b36" },
+ { "fg": "#eee8d5", "bg": "#586e75" }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "pacman": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ },
+ "AC": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ }
+}
diff --git a/.config/i3/bar/themes/solarized.json b/.config/i3/bar/themes/solarized.json
new file mode 100644
index 00000000..12a71ae7
--- /dev/null
+++ b/.config/i3/bar/themes/solarized.json
@@ -0,0 +1,42 @@
+{
+ "icons": [ "ascii" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "critical": {
+ "fg": "#002b36",
+ "bg": "#dc322f"
+ },
+ "warning": {
+ "fg": "#002b36",
+ "bg": "#b58900"
+ },
+ "default_separators": false,
+ "separator": ""
+ },
+ "cycle": [
+ {
+ "fg": "#93a1a1",
+ "bg": "#002b36"
+ },
+ {
+ "fg": "#eee8d5",
+ "bg": "#586e75"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ },
+ "AC": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ }
+}
diff --git a/.config/i3/bar/themes/test.json b/.config/i3/bar/themes/test.json
new file mode 100644
index 00000000..1d457a7e
--- /dev/null
+++ b/.config/i3/bar/themes/test.json
@@ -0,0 +1,25 @@
+{
+ "icons": [ "test" ],
+ "defaults": {
+ "prefix": "default-prefix",
+ "suffix": "default-suffix",
+ "fg": "#000000",
+ "bg": "#111111",
+ "separator": " * ",
+ "separator-block-width": 10,
+ "critical": {
+ "fg": "#ffffff",
+ "bg": "#010101"
+ }
+ },
+ "test-widget": {
+ "fg": "#ababab",
+ "bg": "#222222",
+ "critical": {
+ "fg": "#bababa"
+ },
+ "cycle-test": {
+ "fg": [ "#000000", "#111111" ]
+ }
+ }
+}
diff --git a/.config/i3/bar/themes/test_cycle.json b/.config/i3/bar/themes/test_cycle.json
new file mode 100644
index 00000000..5fd7e1ac
--- /dev/null
+++ b/.config/i3/bar/themes/test_cycle.json
@@ -0,0 +1,18 @@
+{
+ "icons": [ "test" ],
+ "defaults": {
+ "prefix": "default-prefix",
+ "suffix": "default-suffix",
+ "fg": "#000000",
+ "bg": "#111111"
+ },
+ "cycle": [
+ { "fg": "#aa0000" },
+ { "fg": "#00aa00" },
+ { "fg": "#0000aa" }
+ ],
+ "test-widget": {
+ "fg": "#ababab",
+ "bg": "#222222"
+ }
+}
diff --git a/.config/i3/bar/themes/test_invalid.json b/.config/i3/bar/themes/test_invalid.json
new file mode 100644
index 00000000..d510f27a
--- /dev/null
+++ b/.config/i3/bar/themes/test_invalid.json
@@ -0,0 +1 @@
+this is really not json
diff --git a/.config/i3/config b/.config/i3/config
new file mode 100755
index 00000000..1ae87b17
--- /dev/null
+++ b/.config/i3/config
@@ -0,0 +1,393 @@
+# vim: filetype=i3
+
+#exec --no-startup-id xrdb ~/.Xresources;
+#Basic settings
+set $mod Mod4
+floating_modifier $mod
+set $term urxvt
+set $screencast --no-startup-id bash ~/.config/Scripts/screencast.sh
+set $screencast_sys --no-startup-id bash ~/.config/Scripts/screencast_sys.sh
+set $video --no-startup-id bash ~/.config/Scripts/video.sh
+set $audio --no-startup-id bash ~/.config/Scripts/audio.sh
+set $stoprec --no-startup-id killall ffmpeg
+font pango:hack 9
+
+#The wifi manager applet:
+exec --no-startup-id nm-applet
+#For transparency and shadows:
+#exec --no-startup-id xcompmgr -slt
+exec --no-startup-id compton
+#This runs the script that renews the config shortcuts:
+exec_always --no-startup-id python ~/.config/Scripts/shortcuts.py
+
+exec --no-startup-id sudo mount -a
+
+#My background is always in ~/.config/wall.png. I have a shortcut in my file manager (ranger) that will automatically move a selected file here, so I can change by background on the go.
+exec_always --no-startup-id feh --bg-scale ~/.config/wall.png
+
+#Remaps the caps lock button to escape
+exec --no-startup-id setxkbmap -option caps:escape
+#Remaps the menu button (usually between the right Alt and Ctrl to super
+exec_always --no-startup-id xmodmap $HOME/.config/Scripts/remaps
+#Makes the mouse invisible after a brief period
+exec --no-startup-id unclutter
+#Disables touchpad; you might like like this one! (I only use the ThinkPad trackpoint).
+exec --no-startup-id exec synclient TouchpadOff=1
+
+#Needed for i3-gaps
+hide_edge_borders both
+for_window [class="^.*"] border pixel 0
+gaps inner 20
+gaps outer 20
+
+
+#MUSIC:
+##For moc:
+set $music mocp --config ~/.config/moc/config
+set $pause --no-startup-id mocp -G
+set $next --no-startup-id mocp -f
+set $prev --no-startup-id mocp -r
+set $lilfor --no-startup-id mocp -k 10
+set $bigfor --no-startup-id mocp -k 120
+set $lilbak --no-startup-id mocp -k -10
+set $bigbak --no-startup-id mocp -k -120
+set $beg --no-startup-id mocp -j 0%
+
+##For cmus:
+#set $music cmus
+#set $pause --no-startup-id cmus-remote -u
+#set $next --no-startup-id cmus-remote -n
+#set $prev --no-startup-id cmus-remote -r
+#set $lilfor --no-startup-id cmus-remote -k +10
+#set $bigfor --no-startup-id cmus-remote -k +120
+#set $lilbak --no-startup-id cmus-remote -k -10
+#set $bigbak --no-startup-id cmus-remote -k -120
+#set $beg --no-startup-id cmus-remote -k 00:00:00
+
+#For PULSEAUDIO/PAMIXER
+#set $inc --no-startup-id pamixer --allow-boost -i 5
+#set $biginc --no-startup-id pamixer --allow-boost -i 15
+#set $dec --no-startup-id pamixer --allow-boost -d 5
+#set $bigdec --no-startup-id pamixer --allow-boost -d 15
+#set $mute --no-startup-id pamixer --allow-boost -t
+#set $micmute --no-startup-id pamixer --allow-boost -t
+#set $truemute --no-startup-id pamixer -m
+
+#FOR ALSA/AMIXER
+set $inc --no-startup-id amixer sset Master 2%+
+set $biginc --no-startup-id amixer sset Master 5%+
+set $dec --no-startup-id amixer sset Master 2%-
+set $bigdec --no-startup-id amixer sset Master 5%-
+set $mute --no-startup-id amixer sset Master toggle
+set $truemute --no-startup-id amixer sset Master mute
+
+bar {
+colors {
+ focused_workspace #008080 #008080 #ffffff
+ inactive_workspace #000000 #000000 #005050
+ active_workspace #000000 #000000 #008080
+ urgent_workspace #8B0000 #8B0000 #000000
+ }
+ status_command ~/.config/i3/bar/bumblebee-status -m amixer weather battery nic disk datetime -p disk.path=/home datetime.format="%a, %b %d, %Y at %I:%M %p" -t kulade
+ #status_command ~/Downloads/bbs/bumblebee-status -m amixer battery nic disk datetime -p disk.path=/home datetime.format="%a, %b %d, %Y at %I:%M %p" -t kulade
+ position top
+ }
+
+set $flash --no-startup-id bash ~/.config/Scripts/flash_win.sh
+
+bindsym $mod+Return exec $term
+bindsym $mod+Shift+Return exec $term -e tmux -f ~/.config/tmux.conf
+
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+space focus mode_toggle
+
+bindsym $mod+Escape workspace prev
+bindsym $mod+Shift+Escape exec i3-msg exit
+
+bindsym $mod+BackSpace exec $flash
+bindsym $mod+Shift+BackSpace exec sudo reboot
+
+bindsym $mod+Tab workspace back_and_forth
+
+bindsym $mod+grave exec $flash
+#bindsym $mod+asciitilde
+
+#bindsym $mod+XF86Back
+#bindsym $mod+Shift+XF86Back
+
+#bindsym $mod+XF86Forward
+#bindsym $mod+Shift+XF86Forward
+#END
+
+#Letter Keys
+
+bindsym $mod+q kill
+bindsym $mod+Shift+q kill
+
+bindsym $mod+w exec --no-startup-id $BROWSER
+bindsym $mod+Shift+w exec --no-startup-id $BROWSER
+
+bindsym $mod+e exec $term -e mutt -F ~/.config/mutt/muttrc
+bindsym $mod+Shift+e exec $EMAIL
+
+bindsym $mod+r exec $term -e ranger
+bindsym $mod+Shift+r restart; for_window [instance="dropdown"] move position center; for_window [instance="math"] move position center
+
+bindsym $mod+t split toggle
+bindsym $mod+Shift+t gaps inner current set 15; gaps outer current set 15
+
+bindsym $mod+y exec $term -e calcurse -D $HOME/.config/calcurse/
+bindsym $mod+Shift+y resize shrink width 10 px or 10ppt
+
+for_window [instance="dropdown"] floating enable
+for_window [instance="dropdown"] resize set 625 400
+for_window [instance="dropdown"] move position center
+for_window [instance="dropdown"] move scratchpad
+
+
+exec --no-startup-id $term -name dropdown -e tmux -f ~/.config/Scripts/tmux.conf
+
+bindsym $mod+u [instance="dropdown"] scratchpad show
+bindsym $mod+Shift+u resize shrink height 10 px or 10 ppt
+
+bindsym $mod+i exec $term -e htop
+bindsym $mod+Shift+i resize grow height 10 px or 10 ppt
+
+bindsym $mod+o exec --no-startup-id $beg
+bindsym $mod+Shift+o resize grow width 10 px or 10 ppt
+
+bindsym $mod+p exec $pause
+bindsym $mod+Shift+p exec mypaint
+
+for_window [instance="math"] floating enable
+for_window [instance="math"] resize set 800 300
+for_window [instance="math"] move position center
+for_window [instance="math"] move scratchpad
+
+exec --no-startup-id $term -name math -e R -q
+
+bindsym $mod+a [instance="math"] scratchpad show
+bindsym $mod+Shift+a exec $term -e alsamixer
+
+bindsym $mod+s gaps inner current plus 5
+bindsym $mod+Shift+s gaps inner current minus 5
+
+bindsym $mod+d exec rofi -show run
+bindsym $mod+Shift+d gaps inner current set 0; gaps outer current set 0
+
+bindsym $mod+f fullscreen toggle
+bindsym $mod+Shift+f exec --no-startup-id syncthing -home="$HOME/.syncthing"
+
+bindsym $mod+g workspace prev
+bindsym $mod+Shift+g exec --no-startup-id gimp; workspace $ws5
+
+bindsym $mod+h focus left
+bindsym $mod+Shift+h move left 30
+
+bindsym $mod+j focus down
+bindsym $mod+Shift+j move down 30
+
+bindsym $mod+k focus up
+bindsym $mod+Shift+k move up 30
+
+bindsym $mod+l focus right
+bindsym $mod+Shift+l move right 30
+
+bindsym $mod+z gaps outer current plus 5
+bindsym $mod+Shift+z gaps outer current minus 5
+
+bindsym $mod+x exec --no-startup-id ~/.config/i3/lock.sh
+bindsym $mod+Shift+x exec sudo shutdown -h now
+
+bindsym $mod+c exec $term -e weechat -d ~/.config/weechat
+bindsym $mod+Shift+c exec --no-startup-id audacity
+
+bindsym $mod+v exec $term -e vis
+bindsym $mod+Shift+v exec --no-startup-id projectM-pulseaudio
+
+bindsym $mod+b exec $prev
+bindsym $mod+Shift+b exec --no-startup-id blender; workspace $ws6
+
+bindsym $mod+n exec $next
+bindsym $mod+Shift+n exec $term -e newsbeuter
+
+bindsym $mod+m exec $term -e $music
+bindsym $mod+Shift+m exec $mute
+
+bindsym $mod+semicolon workspace next
+bindsym $mod+apostrophe split horizontal ;; exec $term
+bindsym $mod+slash split vertical ;; exec $term
+bindsym $mod+Shift+slash kill
+bindsym $mod+backslash workspace back_and_forth
+#END
+
+#Workspaces
+
+set $ws1 "1: "
+set $ws2 "2: "
+set $ws3 "3: "
+set $ws4 "4: "
+set $ws5 "5: "
+set $ws6 "6: "
+set $ws7 "7: "
+set $ws8 "8: "
+set $ws9 "9: "
+set $ws10 "10: "
+
+# switch to workspace
+bindsym $mod+1 workspace $ws1
+bindsym $mod+2 workspace $ws2
+bindsym $mod+3 workspace $ws3
+bindsym $mod+4 workspace $ws4
+bindsym $mod+5 workspace $ws5
+bindsym $mod+6 workspace $ws6
+bindsym $mod+7 workspace $ws7
+bindsym $mod+8 workspace $ws8
+bindsym $mod+9 workspace $ws9
+bindsym $mod+0 workspace $ws10
+
+# move focused container to workspace
+bindsym $mod+Shift+1 move container to workspace $ws1
+bindsym $mod+Shift+2 move container to workspace $ws2
+bindsym $mod+Shift+3 move container to workspace $ws3
+bindsym $mod+Shift+4 move container to workspace $ws4
+bindsym $mod+Shift+5 move container to workspace $ws5
+bindsym $mod+Shift+6 move container to workspace $ws6
+bindsym $mod+Shift+7 move container to workspace $ws7
+bindsym $mod+Shift+8 move container to workspace $ws8
+bindsym $mod+Shift+9 move container to workspace $ws9
+bindsym $mod+Shift+0 move container to workspace $ws10
+
+for_window [class="Blender"] move to workspace $ws6
+for_window [class="Icedove"] move to workspace $ws9
+for_window [class="Thunderbird"] move to workspace $ws9
+for_window [class="marble"] move to workspace $ws7
+#for_window [class="Iceweasel"] move to workspace $ws2
+#for_window [class="Firefox"] move to workspace $ws2
+for_window [title="GIMP Startup"] move workspace $ws5
+for_window [class="Gimp"] move workspace $ws5
+for_window [class="VirtualBox"] floating enable
+for_window [window_role="gimp-dock"] floating disable; move left; resize shrink width 50 px or 50ppt
+for_window [window_role="gimp-toolbox"] floating disable; move right; resize grow width 30 px or 30ppt
+for_window [window_role="tr-main"] move to workspace $ws8
+#for_window [title="projectM"] exec --no-startup-id transset -a --dec .25
+for_window [window_role="GtkFileChooserDialog"] resize shrink height 10 px
+
+workspace $ws6 gaps inner 0
+workspace $ws6 gaps outer 0
+workspace $ws5 gaps inner 0
+workspace $ws5 gaps outer 0
+workspace $ws8 gaps inner 0
+workspace $ws8 gaps outer 0
+
+#Function Buttons
+#bindsym F1 nop
+#bindsym F2
+#bindsym F3
+#bindsym F4
+#bindsym F5
+#bindsym F6
+#bindsym F7
+#bindsym F8
+#bindsym F9
+#bindsym F10
+#bindsym F11 fullscreen toggle
+#bindsym F12
+
+bindsym $mod+F1 exec --no-startup-id mupdf ~/.config/Scripts/luke_guide.pdf
+bindsym $mod+F2 exec --no-startup-id python ~/.config/Scripts/shortcuts.py
+bindsym $mod+F3 exec --no-startup-id arandr
+bindsym $mod+F4 exec --no-startup-id sudo zzz
+bindsym $mod+F5 exec --no-startup-id sudo sv restart NetworkManager
+bindsym $mod+F6 exec --no-startup-id ~/.config/Scripts/transmission.sh
+#bindsym $mod+F6 exec --no-startup-id $term -e transmission-remote-cli
+bindsym $mod+F7 exec transset -a --dec .15
+bindsym $mod+F8 exec transset -a --inc .15
+bindsym $mod+F9 exec --no-startup-id sudo mount -a
+#bindsym $mod+F9 exec --no-startup-id bash ~/.config/i3/mount.sh
+bindsym $mod+F10 exec --no-startup-id bash ~/.config/i3/unmount.sh
+bindsym $mod+F11 exec $video
+bindsym $mod+F12 exec $screencast_sys
+
+#Arrow Keys
+bindsym $mod+Left focus left
+bindsym $mod+Down focus down
+bindsym $mod+Up focus up
+bindsym $mod+Right focus right
+bindsym $mod+Shift+Left move left
+bindsym $mod+Shift+Down move down
+bindsym $mod+Shift+Up move up
+bindsym $mod+Shift+Right move right
+#END
+
+#Quick Workspace Movement
+bindsym $mod+Home workspace $ws1
+bindsym $mod+Shift+Home move container to workspace $ws1
+bindsym $mod+End workspace $ws10
+bindsym $mod+Shift+End move container to workspace $ws10
+bindsym $mod+Prior workspace prev
+bindsym $mod+Shift+Prior move container to workspace prev
+bindsym $mod+Next workspace next
+bindsym $mod+Shift+Next move container to workspace next
+#END
+
+
+
+#Media
+bindsym $mod+plus exec $inc
+bindsym $mod+Shift+plus exec $biginc
+
+bindsym $mod+minus exec $dec
+bindsym $mod+Shift+minus exec $bigdec
+
+bindsym $mod+bracketleft exec $lilbak
+bindsym $mod+Shift+bracketleft exec $bigbak
+
+bindsym $mod+bracketright exec $lilfor
+bindsym $mod+Shift+bracketright exec $bigfor
+
+bindsym $mod+greater exec $inc
+bindsym $mod+Shift+greater exec $biginc
+
+bindsym $mod+less exec $dec
+bindsym $mod+Shift+less exec $bigdec
+
+bindsym Print exec --no-startup-id scrot
+bindsym Shift+Print exec --no-startup-id scrot -u
+
+bindsym Scroll_Lock exec $audio
+bindsym Num_Lock exec $video
+bindsym $mod+Scroll_Lock exec $stoprec
+
+bindsym Pause exec $screencast
+bindsym Shift+Pause exec $screencast_sys
+bindsym $mod+Pause exec $stoprec
+
+bindsym XF86Launch1 exec $stoprec
+bindsym $mod+XF86Launch1 exec --no-startup-id xset dpms force off
+
+bindsym XF86AudioPlay exec $pause
+bindsym XF86AudioPause exec $pause
+bindsym XF86AudioNext exec $next
+bindsym XF86AudioPrev exec $prev
+bindsym XF86AudioStop exec $pause
+
+bindsym XF86AudioRaiseVolume exec $inc
+bindsym XF86AudioLowerVolume exec $dec
+bindsym XF86AudioMute exec $mute
+bindsym XF86AudioMicMute exec $micmute
+#END
+
+bindsym XF86Mail exec $term -e mutt -F ~/.config/mutt/muttrc
+bindsym XF86MyComputer exec $term -e ranger /
+#bindsym $mod+XF86MyComputer exec
+bindsym XF86HomePage exec --no-startup-id $BROWSER lukesmith.xyz
+bindsym $mod+XF86HomePage exec --no-startup-id $BROWSER
+bindsym $mod+XF86WWW exec --no-startup-id $BROWSER
+
+bindsym XF86Calculator exec $term -e qalc
+
+bindsym XF86MonBrightnessUp exec --no-startup-id xbacklight -inc 15
+bindsym XF86MonBrightnessDown exec --no-startup-id xbacklight -dec 15
+
+for_window [instance="status"] move container to workspace $ws8
diff --git a/.config/i3/lock.png b/.config/i3/lock.png
new file mode 100644
index 00000000..cd8b2280
Binary files /dev/null and b/.config/i3/lock.png differ
diff --git a/.config/i3/lock.sh b/.config/i3/lock.sh
new file mode 100755
index 00000000..6537bef0
--- /dev/null
+++ b/.config/i3/lock.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+scrot /tmp/screen.png
+#convert /tmp/screen.png -scale 10% -scale 1000% /tmp/screen.png
+#convert /tmp/screen.png -paint 3 -modulate 80 /tmp/screen.png
+convert /tmp/screen.png -paint 3 /tmp/screen.png
+[[ -f ~/.config/i3/lock.png ]] && convert /tmp/screen.png ~/.config/i3/lock.png -gravity center -composite -matte /tmp/screen.png
+mocp -P
+i3lock -u -e -i /tmp/screen.png
diff --git a/.config/mimeapps.list b/.config/mimeapps.list
new file mode 100644
index 00000000..d450b3da
--- /dev/null
+++ b/.config/mimeapps.list
@@ -0,0 +1,26 @@
+[Default Applications]
+x-scheme-handler/http=userapp-Firefox-L5HJ0Y.desktop
+x-scheme-handler/https=userapp-Firefox-L5HJ0Y.desktop
+x-scheme-handler/ftp=userapp-Firefox-L5HJ0Y.desktop
+x-scheme-handler/chrome=userapp-Firefox-L5HJ0Y.desktop
+text/html=userapp-Firefox-L5HJ0Y.desktop
+application/x-extension-htm=userapp-Firefox-L5HJ0Y.desktop
+application/x-extension-html=userapp-Firefox-L5HJ0Y.desktop
+application/x-extension-shtml=userapp-Firefox-L5HJ0Y.desktop
+application/xhtml+xml=userapp-Firefox-L5HJ0Y.desktop
+application/x-extension-xhtml=userapp-Firefox-L5HJ0Y.desktop
+application/x-extension-xht=userapp-Firefox-L5HJ0Y.desktop
+application/pdf=mupdf.desktop
+
+[Added Associations]
+x-scheme-handler/http=userapp-Firefox-L5HJ0Y.desktop;
+x-scheme-handler/https=userapp-Firefox-L5HJ0Y.desktop;
+x-scheme-handler/ftp=userapp-Firefox-L5HJ0Y.desktop;
+x-scheme-handler/chrome=userapp-Firefox-L5HJ0Y.desktop;
+text/html=userapp-Firefox-L5HJ0Y.desktop;
+application/x-extension-htm=userapp-Firefox-L5HJ0Y.desktop;
+application/x-extension-html=userapp-Firefox-L5HJ0Y.desktop;
+application/x-extension-shtml=userapp-Firefox-L5HJ0Y.desktop;
+application/xhtml+xml=userapp-Firefox-L5HJ0Y.desktop;
+application/x-extension-xhtml=userapp-Firefox-L5HJ0Y.desktop;
+application/x-extension-xht=userapp-Firefox-L5HJ0Y.desktop;
diff --git a/.config/moc/config b/.config/moc/config
new file mode 100644
index 00000000..d5583927
--- /dev/null
+++ b/.config/moc/config
@@ -0,0 +1,13 @@
+Keymap = ${HOME}/.config/moc/keymap
+Theme = ${HOME}/.config/moc/themes/theme
+#readtags = no
+QueueNextSongReturn = no
+FormatString = "%(n:%n :)%(t:%t:)%(a: \(by %a\):)%(A: \(in %A\):)"
+##%(A: \(%A\):)"
+
+MusicDir = ${HOME}/Music
+Fastdir1 = ${HOME}/Talks/Audiobooks
+Fastdir2 = ${HOME}/Talks/Lectures
+Fastdir3 = ${HOME}/Talks/Podcasts
+Fastdir4 = ${HOME}/Downloads
+Fastdir5 = ${HOME}/Creations
diff --git a/.config/moc/keymap b/.config/moc/keymap
new file mode 100755
index 00000000..77f01417
--- /dev/null
+++ b/.config/moc/keymap
@@ -0,0 +1,188 @@
+# This is the example keymap file for MOC. You can define your own key
+# bindings for MOC commands by creating your own keymap file and setting
+# the 'Keymap' option in ~/.moc/config.
+#
+# The format of this file is:
+#
+# - Lines beginning with # are comments.
+# - Blank lines are ignored.
+# - Every other line is expected to be in one of the formats:
+#
+# COMMAND = [KEY ...]
+# COMMAND += KEY ...
+#
+# The KEY can be:
+#
+# - Just a char, like i, L, ", *
+# - CTRL-KEY sequence: ^k (CTRL-k), ^4
+# - ALT-KEY (meta) sequence: M-j (ALT-j), M-/
+# - Special keys: DOWN, UP
+# LEFT, RIGHT
+# HOME, END
+# BACKSPACE
+# INS, DEL
+# ENTER
+# PAGE_UP, PAGE_DOWN
+# SPACE, TAB
+# KEYPAD_CENTER
+# ESCAPE
+# F1 - F12
+#
+# Note that the use of a digit as a KEY is deprecated.
+#
+# Maximum number of KEYs for one COMMAND is 5.
+#
+# Omitting the KEY for a COMMAND will unbind all its default keys. They
+# will also be automatically unbound when you bind new KEYs to it. Individual
+# default KEYs will be automatically unbound when they are explicitly bound
+# to some other COMMAND.
+#
+# Using the '+=' form will cause the KEYs to be appended to any existing
+# (default or explicit) bindings for the COMMAND. Appending an existing
+# default binding for the same COMMAND will cause MOC to think of that KEY
+# as then being explicitly bound.
+#
+# Only one binding for any given COMMAND can appear in the keymap file. One
+# exception to this is that if the default keys for a COMMAND are explicitly
+# unbound then a subsequent binding may appear for it. A second exception
+# is that multiple appending bindings may appear.
+#
+# Meta-key detection is sensitive to the ESCDELAY environment variable (see
+# the manpage for ncurses(3)). In its absence, MOC resets the default
+# delay to 25ms. If you need to emulate meta-key sequences using the ESC
+# key, then you may need to set the value of ESCDELAY back to its ncurses
+# default of 1000ms (but doing so will make the response to the ESC key
+# sluggish).
+#
+# If MOC's keypresses are being filtered through some other program (in a
+# GUI environment, for example) which also does meta-key detection, then
+# MOC is at the mercy of the timings with which that program presents them.
+#
+# Default key configuration for MOC (and a list of all available commands):
+
+# MOC control keys:
+quit_client = q
+quit = Q
+
+# Menu and interface control keys:
+go = ENTER l
+menu_down = DOWN j
+menu_up = UP k
+menu_page_down = PAGE_DOWN ^d d
+menu_page_up = PAGE_UP ^p ^u u
+menu_first_item = HOME g
+menu_last_item = END G
+search_menu = /
+toggle_read_tags = f
+toggle_show_time = ^t
+toggle_show_format = ^f
+toggle_menu = TAB
+#toggle_layout = L
+#toggle_hidden_files = H
+next_search = ^g ^n
+#show_lyrics = L
+theme_menu = T
+help = ?
+refresh = ^r
+reload = r
+
+# Audio playing and positioning keys:
+seek_forward = RIGHT
+seek_backward = LEFT
+seek_forward_fast = ]
+seek_backward_fast = [
+pause = p SPACE
+stop = s
+next = n L ^l
+previous = b H ^h
+toggle_shuffle = S
+toggle_repeat = R
+toggle_auto_next = X N
+#toggle_mixer = x
+go_url = o
+
+# Volume control keys:
+volume_down_1 = <
+volume_up_1 = >
+volume_down_5 = , -
+volume_up_5 = . =
+volume_10 = M-1
+volume_20 = M-2
+volume_30 = M-3
+volume_40 = M-4
+volume_50 = M-5
+volume_60 = M-6
+volume_70 = M-7
+volume_80 = M-8
+volume_90 = M-9
+
+# Directory navigation keys: defaults are Shift-number
+# (i.e., 'shift 1' -> '!' -> 'Fastdir1').
+go_to_a_directory = i
+go_to_music_directory = M-m
+go_to_fast_dir1 = M-a
+go_to_fast_dir2 = M-l
+go_to_fast_dir3 = M-p
+go_to_fast_dir4 = M-d
+go_to_fast_dir5 = M-c
+go_to_fast_dir6 = ^
+go_to_fast_dir7 = &
+go_to_fast_dir8 = *
+go_to_fast_dir9 = (
+go_to_fast_dir10 = )
+#go_to_playing_file = G
+go_up = h
+
+# Playlist specific keys:
+add_file = a
+add_directory = A
+#plist_add_stream = u
+delete_from_playlist = x
+playlist_full_paths = P
+plist_move_up = K
+plist_move_down = J
+save_playlist = V
+remove_dead_entries = Y
+clear_playlist = C
+
+# Queue manipulation keys:
+enqueue_file = z
+clear_queue = Z
+
+# User interaction control:
+history_up = UP
+history_down = DOWN
+delete_to_start = ^u
+delete_to_end = ^k
+cancel = ^x ESCAPE
+hide_message = M
+
+# Softmixer specific keys:
+toggle_softmixer = w
+#toggle_make_mono = J
+
+# Equalizer specific keys:
+toggle_equalizer = E
+equalizer_refresh = e
+#equalizer_prev = K
+#equalizer_next = k
+
+# External commands:
+mark_start = '
+mark_end = "
+exec_command1 = F1
+exec_command2 = F2
+exec_command3 = F3
+exec_command4 = F4
+exec_command5 = F5
+exec_command6 = F6
+exec_command7 = F7
+exec_command8 = F8
+exec_command9 = F9
+exec_command10 = F10
+
+# The following commands are available but not assigned to any keys by
+# default:
+#
+# toggle_percent Switch on/off play progress bar time percentage
+#
diff --git a/.config/moc/themes/theme b/.config/moc/themes/theme
new file mode 100755
index 00000000..561ccec2
--- /dev/null
+++ b/.config/moc/themes/theme
@@ -0,0 +1,32 @@
+background = default default
+frame = red default
+window_title = white default
+directory = blue default bold
+selected_directory = black blue bold
+playlist = white default bold
+selected_playlist = black blue bold
+file = yellow default
+selected_file = black yellow
+marked_file = green default bold
+marked_selected_file = black blue bold
+info = blue default bold
+status = white default
+title = white default bold
+state = white default bold
+current_time = white default bold
+time_left = white default
+total_time = white default bold
+time_total_frames = white default
+sound_parameters = white default bold
+legend = white default
+disabled = blue default
+enabled = white default bold
+empty_mixer_bar = white default
+filled_mixer_bar = black cyan
+empty_time_bar = white default
+filled_time_bar = black cyan
+entry = red default
+entry_title = black cyan
+error = red default bold
+message = green default bold
+plist_time = white default bold
diff --git a/.config/mpv/input.conf b/.config/mpv/input.conf
new file mode 100644
index 00000000..0a9b5fd4
--- /dev/null
+++ b/.config/mpv/input.conf
@@ -0,0 +1,199 @@
+# mpv keybindings
+#
+# Location of user-defined bindings: ~/.config/mpv/input.conf
+#
+# Lines starting with # are comments. Use SHARP to assign the # key.
+# Copy this file and uncomment and edit the bindings you want to change.
+#
+# List of commands and further details: DOCS/man/input.rst
+# List of special keys: --input-keylist
+# Keybindings testing mode: mpv --input-test --force-window --idle
+#
+# Use 'ignore' to unbind a key fully (e.g. 'ctrl+a ignore').
+#
+# Strings need to be quoted and escaped:
+# KEY show-text "This is a single backslash: \\ and a quote: \" !"
+#
+# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with
+# the modifiers Shift, Ctrl, Alt and Meta (may not work on the terminal).
+#
+# The default keybindings are hardcoded into the mpv binary.
+# You can disable them completely with: --no-input-default-bindings
+
+# Developer note:
+# On compilation, this file is baked into the mpv binary, and all lines are
+# uncommented (unless '#' is followed by a space) - thus this file defines the
+# default key bindings.
+
+# If this is enabled, treat all the following bindings as default.
+#default-bindings start
+
+#MOUSE_BTN0 ignore # don't do anything
+#MOUSE_BTN0_DBL cycle fullscreen # toggle fullscreen on/off
+#MOUSE_BTN2 cycle pause # toggle pause on/off
+#MOUSE_BTN3 seek 10
+#MOUSE_BTN4 seek -10
+#MOUSE_BTN5 add volume -2
+#MOUSE_BTN6 add volume 2
+
+# Mouse wheels, touchpad or other input devices that have axes
+# if the input devices supports precise scrolling it will also scale the
+# numeric value accordingly
+#AXIS_UP seek 10
+#AXIS_DOWN seek -10
+#AXIS_LEFT seek 5
+#AXIS_RIGHT seek -5
+
+## Seek units are in seconds, but note that these are limited by keyframes
+#RIGHT seek 5
+#LEFT seek -5
+#UP seek 60
+#DOWN seek -60
+# Do smaller, always exact (non-keyframe-limited), seeks with shift.
+# Don't show them on the OSD (no-osd).
+#Shift+RIGHT no-osd seek 1 exact
+#Shift+LEFT no-osd seek -1 exact
+#Shift+UP no-osd seek 5 exact
+#Shift+DOWN no-osd seek -5 exact
+# Skip to previous/next subtitle (subject to some restrictions; see manpage)
+#Ctrl+LEFT no-osd sub-seek -1
+#Ctrl+RIGHT no-osd sub-seek 1
+#PGUP add chapter 1 # skip to next chapter
+#PGDWN add chapter -1 # skip to previous chapter
+#Shift+PGUP seek 600
+#Shift+PGDWN seek -600
+#[ multiply speed 0.9091 # scale playback speed
+#] multiply speed 1.1
+#{ multiply speed 0.5
+#} multiply speed 2.0
+#BS set speed 1.0 # reset speed to normal
+#q quit
+#Q quit-watch-later
+#q {encode} quit 4
+#ESC set fullscreen no
+#ESC {encode} quit 4
+#p cycle pause # toggle pause/playback mode
+#. frame-step # advance one frame and pause
+#, frame-back-step # go back by one frame and pause
+#SPACE cycle pause
+#> playlist-next # skip to next file
+#ENTER playlist-next # skip to next file
+#< playlist-prev # skip to previous file
+#O no-osd cycle-values osd-level 3 1 # cycle through OSD mode
+#o show-progress
+#P show-progress
+#I show-text "${filename}" # display filename in osd
+#z add sub-delay -0.1 # subtract 100 ms delay from subs
+#x add sub-delay +0.1 # add
+#ctrl++ add audio-delay 0.100 # this changes audio/video sync
+#ctrl+- add audio-delay -0.100
+#9 add volume -2
+#/ add volume -2
+#0 add volume 2
+#* add volume 2
+#m cycle mute
+#1 add contrast -1
+#2 add contrast 1
+#3 add brightness -1
+#4 add brightness 1
+#5 add gamma -1
+#6 add gamma 1
+#7 add saturation -1
+#8 add saturation 1
+#Alt+0 set window-scale 0.5
+#Alt+1 set window-scale 1.0
+#Alt+2 set window-scale 2.0
+# toggle deinterlacer (automatically inserts or removes required filter)
+#d cycle deinterlace
+#r add sub-pos -1 # move subtitles up
+#t add sub-pos +1 # down
+#v cycle sub-visibility
+# stretch SSA/ASS subtitles with anamorphic videos to match historical
+#V cycle sub-ass-vsfilter-aspect-compat
+# switch between applying no style overrides to SSA/ASS subtitles, and
+# overriding them almost completely with the normal subtitle style
+#u cycle-values sub-ass-style-override "force" "no"
+#j cycle sub # cycle through subtitles
+#J cycle sub down # ...backwards
+#SHARP cycle audio # switch audio streams
+#_ cycle video
+#T cycle ontop # toggle video window ontop of other windows
+#f cycle fullscreen # toggle fullscreen
+#s screenshot # take a screenshot
+#S screenshot video # ...without subtitles
+#Ctrl+s screenshot window # ...with subtitles and OSD, and scaled
+#Alt+s screenshot each-frame # automatically screenshot every frame
+#w add panscan -0.1 # zoom out with -panscan 0 -fs
+#e add panscan +0.1 # in
+# cycle video aspect ratios; "-1" is the container aspect
+#A cycle-values video-aspect "16:9" "4:3" "2.35:1" "-1"
+#POWER quit
+#PLAY cycle pause
+#PAUSE cycle pause
+#PLAYPAUSE cycle pause
+#STOP quit
+#FORWARD seek 60
+#REWIND seek -60
+#NEXT playlist-next
+#PREV playlist-prev
+#VOLUME_UP add volume 2
+#VOLUME_DOWN add volume -2
+#MUTE cycle mute
+#CLOSE_WIN quit
+#CLOSE_WIN {encode} quit 4
+#E cycle edition # next edition
+#l ab-loop # Set/clear A-B loop points
+#L cycle-values loop "inf" "no" # toggle infinite looping
+#ctrl+c quit 4
+
+# Apple Remote section
+#AR_PLAY cycle pause
+#AR_PLAY_HOLD quit
+#AR_CENTER cycle pause
+#AR_CENTER_HOLD quit
+#AR_NEXT seek 10
+#AR_NEXT_HOLD seek 120
+#AR_PREV seek -10
+#AR_PREV_HOLD seek -120
+#AR_MENU show-progress
+#AR_MENU_HOLD cycle mute
+#AR_VUP add volume 2
+#AR_VUP_HOLD add chapter 1
+#AR_VDOWN add volume -2
+#AR_VDOWN_HOLD add chapter -1
+
+# For tv://
+#h cycle tv-channel -1 # previous channel
+#k cycle tv-channel +1 # next channel
+
+# For dvb://
+#H cycle dvb-channel-name -1 # previous channel
+#K cycle dvb-channel-name +1 # next channel
+
+#
+# Legacy bindings (may or may not be removed in the future)
+#
+#! add chapter -1 # skip to previous chapter
+#@ add chapter 1 # next
+
+#
+# Not assigned by default
+# (not an exhaustive list of unbound commands)
+#
+
+# ? add sub-scale +0.1 # increase subtitle font size
+# ? add sub-scale -0.1 # decrease subtitle font size
+# ? sub-step -1 # immediately display next subtitle
+# ? sub-step +1 # previous
+# ? cycle angle # switch DVD/Bluray angle
+# ? add balance -0.1 # adjust audio balance in favor of left
+# ? add balance 0.1 # right
+# ? cycle sub-forced-only # toggle DVD forced subs
+# ? cycle program # cycle transport stream programs
+# ? stop # stop playback (quit or enter idle mode)
+
+l seek 5
+h seek -5
+j seek -60
+k seek 60
+S cycle sub
diff --git a/.config/mutt/aliases b/.config/mutt/aliases
new file mode 100644
index 00000000..86a20297
--- /dev/null
+++ b/.config/mutt/aliases
@@ -0,0 +1,13 @@
+#Here you can put email aliases. Here's an example:
+
+alias luke luke@lukesmith.xyz
+
+#When you put "luke" as the recipient in mutt, it will automatically direct it to my email address.
+
+#You can also put multiple emails under one aliases:
+
+alias project_members billy@gmail.com, sally@gmail.com, amanda@gmail.com, chad@gmail.com
+
+#With the above alias, if you put "project_members" as the recipient, mutt will fill in all the emails above as the recipients.
+#
+#This is extremely useful, so take advantage of it!
diff --git a/.config/mutt/gmailrc b/.config/mutt/gmailrc
new file mode 100644
index 00000000..68b6ff9d
--- /dev/null
+++ b/.config/mutt/gmailrc
@@ -0,0 +1,28 @@
+# vim: filetype=muttrc
+set imap_user = "YOURNAME@gmail.com"
+set smtp_url = "smtp://YOURNAME@smtp.gmail.com:587/"
+set from = "YOURNAME@gmail.com"
+set realname = "YOUR ACTUAL NAME"
+set mbox_type = Maildir
+set sendmail = "/usr/bin/msmtp -a gmail"
+set folder = "~/.Mail/Gmail"
+set spoolfile = "+INBOX"
+set record = /dev/null
+set mbox = "+[Gmail].All Mail"
+set postponed = "+[Gmail].Drafts"
+set header_cache = ~/.config/mutt/gmail/cache/headers
+set message_cachedir = ~/.config/mutt/gmail/cache/bodies
+set certificate_file = ~/.config/mutt/gmail/certificates
+set ssl_starttls = yes
+set ssl_force_tls = yes
+
+mailboxes +INBOX
+
+macro index gi "=INBOX" "Go to inbox"
+macro index ga "=[Gmail].All Mail" "Go to all mail"
+macro index gs "=[Gmail].Sent Mail" "Go to sent mail"
+macro index gS "=[Gmail].Spam" "Go to spam"
+macro index gt "=[Gmail].Trash" "Go to trash"
+macro index gd "=[Gmail].Drafts" "Go to drafts"
+macro index gl "=lingcircle" "Go to lingcircle"
+macro index gr "=ugaroml" "Go to ugaroml"
diff --git a/.config/mutt/luke_mutt_readme.md b/.config/mutt/luke_mutt_readme.md
new file mode 100644
index 00000000..b805ca7b
--- /dev/null
+++ b/.config/mutt/luke_mutt_readme.md
@@ -0,0 +1,61 @@
+# Luke's mutt readme
+
+mutt is a terminal email program.
+
+## Using my configs
+
+Move the muttrc file to ~/.muttrc to activate it by default.
+
+Make an rc file for your own email address. If you have a Gmail account, for example, modify the "gmailrc" file to your address. Make sure muttrc is sourcing the file you want.
+
+You should also have Neomutt installed for full compatibility.
+
+## Macros and shortcuts
+
+I've made some changes to the bindings to make mutt a little more vim-like. See them in muttrc for yourself.
+
+I've also employed gotbletu's shortcuts for changing accounts and moving from Inbox to Sent to Drafts etc.
+
+Note that the commands for movement to different boxes are in the individual email configs. This is because the folder structure of each account may differ. When mutt loads another email account, it also loades the new shortcuts. You may need to make changes for your email account.
+
+## Colors and themes
+
+My muttrc has my particular formats for email headers and date.
+
+You have a lot of freedom in how you can configure how mutt works. Here, I have a file "muttcol" which is originally someone else's theme (can't remember where I got it), but I've also made color changes in the muttrc.
+
+My config is made for Neomutt, which allows email headers to have different colors for different information. If you're not running Neomutt, you'll get errors when using my configs. To remove these errors delete the following lines from the muttrc:
+
+color index_author red default '.*'
+color index_number blue default
+color index_subject cyan default '.s'
+color index_size green default
+
+## Aliases
+
+You can use aliases to avoid having to type out email addresses over and over. For example, if you have the following:
+
+alias luke luke@lukesmith.xyz
+
+whenever you type in "luke" as a recipient, it will automatically direct that you my email. You can also have aliases for groups of people, separated by a comma.
+
+alias best_friends billy@gmail.com, sally@gmail.com, amanda@gmail.com, chad@gmail.com
+
+so whenever you type in "best_friends," mutt will direct the mail to all four of these email addresses.
+
+## Passwords
+
+If you don't want mutt to ask for your password when logging on, set imap_pass to your password.
+
+imap_pass = muh_password
+
+
+Same is true of smtp_pass if you want to send emails without a password.
+
+smtp = muh_password
+
+There are securer ways of storing your pass word, which you can research youself. I'm still figuring out an elegant solution that works for me.
+
+## To come...
+
+I'm still figuring out offlineIMAPs and some other little things to interface with mutt. I'll put up a new video when I'm totally successful.
diff --git a/.config/mutt/mailcap b/.config/mutt/mailcap
new file mode 100755
index 00000000..0e8631e7
--- /dev/null
+++ b/.config/mutt/mailcap
@@ -0,0 +1,11 @@
+#text/html; qutebrowser %s &; test=test -n "$DISPLAY"; needsterminal;
+
+text/html; w3m -I %{charset} -T text/html; copiousoutput;
+#text/html; mv %s %s.html && qutebrowser %s.html > /dev/null; needsterminal;
+#
+application/pdf; mv %s %s.pdf && mupdf %s.pdf > /dev/null; needsterminal;
+
+image/*; ~/.config/mutt/muttimage.sh %s ; copiousoutput
+
+#application/pdf; pdftotext '%s' -; copiousoutput; description=PDF Document;
+#nametemplate=%s.pdf
diff --git a/.config/mutt/mutt.sh b/.config/mutt/mutt.sh
new file mode 100644
index 00000000..537322ee
--- /dev/null
+++ b/.config/mutt/mutt.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+pwds='gpg --decrypt ~/.config/Scripts/DELET'
+eval "$pwds"
+exec mutt -F ~/.config/mutt/muttrc "$@"
diff --git a/.config/mutt/muttcol b/.config/mutt/muttcol
new file mode 100755
index 00000000..e9d6defb
--- /dev/null
+++ b/.config/mutt/muttcol
@@ -0,0 +1,151 @@
+# vim: filetype=muttrc
+
+#
+#
+# make sure that you are using mutt linked against slang, not ncurses, or
+# suffer the consequences of weird color issues. use "mutt -v" to check this.
+
+# custom body highlights -----------------------------------------------
+# highlight my name and other personally relevant strings
+#color body yellow default "(ethan|schoonover)"
+# custom index highlights ----------------------------------------------
+# messages which mention my name in the body
+#color index yellow default "~b \"phil(_g|\!| gregory| gold)|pgregory\" !~N !~T !~F !~p !~P"
+#color index J_cream brightwhite "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~N !~T !~F !~p !~P"
+#color index yellow cyan "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~T !~F !~p !~P"
+#color index yellow J_magent "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~F !~p !~P"
+## messages which are in reference to my mails
+#color index J_magent default "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" !~N !~T !~F !~p !~P"
+#color index J_magent brightwhite "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~N !~T !~F !~p !~P"
+#color index J_magent cyan "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~T !~F !~p !~P"
+#color index J_magent red "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~F !~p !~P"
+
+# for background in 16 color terminal, valid background colors include:
+# base03, bg, black, any of the non brights
+
+# basic colors ---------------------------------------------------------
+color normal brightyellow default
+color error red default
+color tilde black default
+color message cyan default
+color markers red white
+color attachment white default
+color search brightmagenta default
+#color status J_black J_status
+color status brightyellow black
+color indicator brightblack yellow
+color tree yellow default # arrow in threads
+
+# basic monocolor screen
+mono bold bold
+mono underline underline
+mono indicator reverse
+mono error bold
+
+# index ----------------------------------------------------------------
+
+#color index red default "~D(!~p|~p)" # deleted
+#color index black default ~F # flagged
+#color index brightred default ~= # duplicate messages
+#color index brightgreen default "~A!~N!~T!~p!~Q!~F!~D!~P" # the rest
+#color index J_base default "~A~N!~T!~p!~Q!~F!~D" # the rest, new
+color index red default "~A" # all messages
+color index brightred default "~E" # expired messages
+color index blue default "~N" # new messages
+color index blue default "~O" # old messages
+color index brightmagenta default "~Q" # messages that have been replied to
+color index brightgreen default "~R" # read messages
+color index blue default "~U" # unread messages
+color index blue default "~U~$" # unread, unreferenced messages
+color index brightyellow default "~v" # messages part of a collapsed thread
+color index brightyellow default "~P" # messages from me
+color index cyan default "~p!~F" # messages to me
+color index cyan default "~N~p!~F" # new messages to me
+color index cyan default "~U~p!~F" # unread messages to me
+color index brightgreen default "~R~p!~F" # messages to me
+color index red default "~F" # flagged messages
+color index red default "~F~p" # flagged messages to me
+color index red default "~N~F" # new flagged messages
+color index red default "~N~F~p" # new flagged messages to me
+color index red default "~U~F~p" # new flagged messages to me
+color index black red "~D" # deleted messages
+color index brightcyan default "~v~(!~N)" # collapsed thread with no unread
+color index yellow default "~v~(~N)" # collapsed thread with some unread
+color index green default "~N~v~(~N)" # collapsed thread with unread parent
+# statusbg used to indicated flagged when foreground color shows other status
+# for collapsed thread
+color index red black "~v~(~F)!~N" # collapsed thread with flagged, no unread
+color index yellow black "~v~(~F~N)" # collapsed thread with some unread & flagged
+color index green black "~N~v~(~F~N)" # collapsed thread with unread parent & flagged
+color index green black "~N~v~(~F)" # collapsed thread with unread parent, no unread inside, but some flagged
+color index cyan black "~v~(~p)" # collapsed thread with unread parent, no unread inside, some to me directly
+color index yellow red "~v~(~D)" # thread with deleted (doesn't differentiate between all or partial)
+#color index yellow default "~(~N)" # messages in threads with some unread
+#color index green default "~S" # superseded messages
+#color index red default "~T" # tagged messages
+#color index brightred red "~=" # duplicated messages
+
+# message headers ------------------------------------------------------
+
+#color header brightgreen default "^"
+color hdrdefault brightgreen default
+color header brightyellow default "^(From)"
+color header blue default "^(Subject)"
+
+# body -----------------------------------------------------------------
+
+color quoted blue default
+color quoted1 cyan default
+color quoted2 yellow default
+color quoted3 red default
+color quoted4 brightred default
+
+color signature brightgreen default
+color bold black default
+color underline black default
+color normal default default
+#
+color body brightcyan default "[;:][-o][)/(|]" # emoticons
+color body brightcyan default "[;:][)(|]" # emoticons
+color body brightcyan default "[*]?((N)?ACK|CU|LOL|SCNR|BRB|BTW|CWYL|\
+ |FWIW|vbg|GD&R|HTH|HTHBE|IMHO|IMNSHO|\
+ |IRL|RTFM|ROTFL|ROFL|YMMV)[*]?"
+color body brightcyan default "[ ][*][^*]*[*][ ]?" # more emoticon?
+color body brightcyan default "[ ]?[*][^*]*[*][ ]" # more emoticon?
+
+## pgp
+
+color body red default "(BAD signature)"
+color body cyan default "(Good signature)"
+color body brightblack default "^gpg: Good signature .*"
+color body brightyellow default "^gpg: "
+color body brightyellow red "^gpg: BAD signature from.*"
+mono body bold "^gpg: Good signature"
+mono body bold "^gpg: BAD signature from.*"
+
+# yes, an insance URL regex
+color body red default "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]"
+# and a heavy handed email regex
+#color body J_magent default "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])"
+
+# Various smilies and the like
+#color body brightwhite default "<[Gg]>" #
+#color body brightwhite default "<[Bb][Gg]>" #
+#color body yellow default " [;:]-*[})>{(<|]" # :-) etc...
+# *bold*
+#color body blue default "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)"
+#mono body bold "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)"
+# _underline_
+#color body blue default "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)"
+#mono body underline "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)"
+# /italic/ (Sometimes gets directory names)
+#color body blue default "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)"
+#mono body underline "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)"
+
+# Border lines.
+#color body blue default "( *[-+=#*~_]){6,}"
+
+#folder-hook . "color status J_black J_status "
+#folder-hook gmail/inbox "color status J_black yellow "
+#folder-hook gmail/important "color status J_black yellow "
+
diff --git a/.config/mutt/muttimage.sh b/.config/mutt/muttimage.sh
new file mode 100755
index 00000000..b67de83b
--- /dev/null
+++ b/.config/mutt/muttimage.sh
@@ -0,0 +1,8 @@
+#! /bin/sh
+
+#### Determine size of Terminal
+height=`stty size | awk 'BEGIN {FS = " "} {print $1;}'`
+width=`stty size | awk 'BEGIN {FS = " "} {print $2;}'`
+
+### Display Image / offset with mutt bar
+echo -e "2;3;\n0;1;210;20;$((width*7-250));$((height*14-100));0;0;0;0;$1\n4;\n3;" | /usr/lib/w3m/w3mimgdisplay &
diff --git a/.config/mutt/muttrc b/.config/mutt/muttrc
new file mode 100755
index 00000000..86b4a0b4
--- /dev/null
+++ b/.config/mutt/muttrc
@@ -0,0 +1,42 @@
+#source "gpg -dq ~/.ayylmao |"
+source ~/.config/mutt/gmailrc
+source ~/.config/mutt/muttcol
+source ~/.config/mutt/aliases
+
+set sort = 'reverse-date'
+set editor = vim
+#set copy = no
+set timeout = "5"
+set mail_check = "10"
+set mailcap_path = ~/.config/mutt/mailcap
+set date_format="%m/%d %I:%M"
+set index_format="%2C %Z %d %-15.15F %s (%-4.4c)"
+#set status_format="mpv ~/.config/mutt/bump.wav"
+#set wrap = 0
+
+auto_view text/html
+alternative_order text/plain text/enriched text/html
+
+mono bold bold
+mono underline underline
+mono indicator reverse
+color index yellow default '.*'
+color index_author red default '.*'
+color index_number blue default
+color index_subject cyan default '.s'
+color index_size green default
+color normal default default
+color body brightred default [\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+
+color body brightblue default (https?|ftp)://[\-\.,/%~_:?&=\#a-zA-Z0-9]+
+
+bind editor noop
+bind index G last-entry
+bind index gg first-entry
+bind index d half-down
+bind index u half-up
+bind index D delete-message
+bind index U undelete-message
+bind index F search
+bind index R group-reply
+
+auto_view application/pdf
diff --git a/.config/neofetch/config b/.config/neofetch/config
new file mode 100755
index 00000000..ece8c0b9
--- /dev/null
+++ b/.config/neofetch/config
@@ -0,0 +1,431 @@
+#!/usr/bin/env bash
+# vim:fdm=marker
+#
+# Neofetch config file
+# https://github.com/dylanaraps/neofetch
+
+# Speed up script by not using unicode
+export LC_ALL=C
+export LANG=C
+
+# Info Options {{{
+
+
+# Info
+# See this wiki page for more info:
+# https://github.com/dylanaraps/neofetch/wiki/Customizing-Info
+printinfo () {
+ info title
+ info underline
+
+ info "Model" model
+ info "OS" distro
+ info "Kernel" kernel
+ info "Uptime" uptime
+ info "Packages" packages
+ info "Shell" shell
+ info "Resolution" resolution
+ info "DE" de
+ info "WM" wm
+ info "WM Theme" wmtheme
+ #info "Theme" theme
+ #info "Icons" icons
+ info "Terminal" term
+ info "Terminal Font" termfont
+ info "CPU" cpu
+ info "GPU" gpu
+ info "Memory" memory
+
+ info "CPU Usage" cpu_usage
+ info "Disk" disk
+ #info "Battery" battery
+ #info "Font" font
+ #info "Song" song
+ # info "Local IP" localip
+ # info "Public IP" publicip
+ #info "Users" users
+ #info "Birthday" birthday
+
+ info linebreak
+ info cols
+}
+
+
+# Kernel
+
+# Show more kernel info
+# --kernel_shorthand on/off
+kernel_shorthand="on"
+
+
+# Distro
+
+# Shorten the output of distro (tiny, on, off)
+# NOTE: This is only possible on Linux
+distro_shorthand="off"
+
+# Mac OS X hide/show build version
+# --osx_buildversion on/off
+osx_buildversion="on"
+
+# Mac OS X hide/show codename
+# --osx_codename on/off
+osx_codename="on"
+
+# Show 'x86_64' and 'x86' in 'Distro:' output.
+# --os_arch on/off
+os_arch="on"
+
+
+# Uptime
+
+# Shorten the output of the uptime function
+# --uptime_shorthand tiny, on, off
+uptime_shorthand="off"
+
+
+# Shell
+
+# Show the path to $SHELL
+# --shell_path on/off
+shell_path="on"
+
+# Show $SHELL's version
+# --shell_version on/off
+shell_version="off"
+
+
+# CPU
+
+# CPU speed type
+# Only works on Linux with cpufreq.
+# --speed_type current, min, max, bios,
+# scaling_current, scaling_min, scaling_max
+speed_type="max"
+
+# CPU Shorthand
+# Set shorthand setting
+# --cpu_shorthand name, speed, tiny, on, off
+cpu_shorthand="off"
+
+# CPU Usage display
+# Set CPU usage display setting
+# --cpu_display bar, infobar, barinfo, off
+cpu_display="off"
+
+# CPU Cores
+# Display CPU cores in output
+# --cpu_cores on/off
+cpu_cores="on"
+
+
+# GPU
+
+# Shorten output of the getgpu funcion
+# --gpu_shorthand on/off/tiny
+gpu_shorthand="on"
+
+# Enable/Disable GPU Brand
+# --gpu_brand on/off
+gpu_brand="on"
+
+# Resolution
+
+# Display refresh rate next to each monitor
+# Unsupported on Windows
+# --refresh_rate on/off
+refresh_rate="off"
+
+
+# Gtk Theme / Icons
+
+# Shorten output (Hide [GTK2] etc)
+# --gtk_shorthand on/off
+gtk_shorthand="off"
+
+
+# Enable/Disable gtk2 theme/icons output
+# --gtk2 on/off
+gtk2="on"
+
+# Enable/Disable gtk3 theme/icons output
+# --gtk3 on/off
+gtk3="on"
+
+
+# Battery
+
+# Which battery to display.
+# By default we display all batteries.
+# NOTE: Only works on Linux.
+# --battery_num all, 0, 1, 2, etc
+battery_num="all"
+
+# Whether or not to print each battery on the same line.
+# By default each battery gets its own line and title.
+# NOTE: Only works on Linux.
+# --battery_shorthand on/off
+battery_shorthand="off"
+
+
+# IP Address
+
+# Website to ping for the public IP
+# --ip_host url
+public_ip_host="http://ident.me"
+
+
+# Song
+
+# Print the Artist and Title on seperate lines
+# --song_shorthand on/off
+song_shorthand="off"
+
+
+# Birthday
+
+# Whether to show a long pretty output
+# or a shortened one
+# NOTE: Long pretty output doesn't work on OpenBSD or NetBSD.
+# --birthday_shorthand on/off
+birthday_shorthand="off"
+
+# Whether to show the time in the output
+# --birthday_time on/off
+birthday_time="on"
+
+# Date format to use when printing birthday
+# --birthday_format "format"
+birthday_format="+%a %d %b %Y %l:%M %p"
+
+
+# }}}
+
+# Text Colors {{{
+
+
+# Text Colors
+# Each number represents a different part of
+# the text in this order:
+# title, @, underline, subtitle, colon, info
+# colors=(4 6 1 8 8 6)
+# You can also specify:
+# fg (foreground color)
+colors=(distro)
+
+
+# }}}
+
+# Text Options {{{
+
+
+# Toggle bold text
+# --bold on/off
+bold="on"
+
+# Enable/Disable Underline
+# --underline on/off
+underline_enabled="on"
+
+# Underline character
+# --underline_char char
+underline_char="-"
+
+
+# }}}
+
+# Color Blocks {{{
+
+
+# Color block range
+# --block_range start end
+start=0
+end=7
+
+# Toggle color blocks
+# --color_blocks on/off
+color_blocks="on"
+
+# Color block width in spaces
+# --block_width num
+block_width=2
+
+# Color block height in lines
+# --block_height num
+block_height=1
+
+
+# }}}
+
+# Progress Bars {{{
+
+
+# Progress bar character
+# --progress_char elapsed_char total_char
+progress_char_elapsed="-"
+progress_char_total="="
+
+# Progress border
+# --progress_border on/off
+progress_border="on"
+
+# Progress bar length in spaces
+# Number of chars long to make the progress bars.
+# --progress_length num
+progress_length="15"
+
+# Progress bar colors
+# When set to distro, uses your distro's logo colors
+# Takes: num, "distro"
+# --progress_colors col col
+progress_color_elapsed="distro"
+progress_color_total="distro"
+
+# Customize how the info is displayed.
+# bar: Only the progress bar is displayed.
+# infobar: The bar is displayed after the info.
+# barinfo: The bar is displayed before the info.
+# off: Only the info is displayed.
+#
+# --memory_display bar/infobar/barinfo/off
+# --battery_display bar/infobar/barinfo/off
+# --disk_display bar/infobar/barinfo/off
+memory_display="off"
+battery_display="off"
+disk_display="off"
+
+
+# }}}
+
+# Image Options {{{
+
+
+# Image Source
+# --image wall, ascii, /path/to/img, /path/to/dir/, off
+image="wall"
+
+# Thumbnail directory
+thumbnail_dir="$HOME/.cache/thumbnails/neofetch"
+
+# W3m-img path
+# Some systems have this in another location
+w3m_img_path="/usr/lib/w3m/w3mimgdisplay"
+
+# Image position
+# Only works with the w3m backend
+# --image_position left/right
+image_position="left"
+
+# Crop mode
+# --crop_mode normal/fit/fill
+crop_mode="normal"
+
+# Crop offset
+# Only affects normal mode.
+# --crop_offset northwest/north/northeast/west/center
+# east/southwest/south/southeast
+crop_offset="center"
+
+# Image size
+# The image is half the terminal width by default.
+# --size auto, 00px, 00%, none
+image_size="auto"
+
+# Right gap between image and text
+# --gap num
+gap=2
+
+# Image offsets
+# --xoffset px
+# --yoffset px
+yoffset=0
+xoffset=0
+
+
+# }}}
+
+# Ascii Options {{{
+
+
+# Default ascii image to use
+# When this is set to distro it will use your
+# distro's logo as the ascii.
+# --ascii 'distro', path/to/ascii
+ascii="distro"
+
+# Ascii distro
+# Which distro's ascii art to display.
+# --ascii_distro 'auto', 'distro_name'
+ascii_distro="auto"
+
+# Ascii colors
+# When this is set to distro it will use your
+# ditro's colors to color the ascii.
+# NOTE: You can also set this to a range of colors
+# which will allow you to custom color distro logos
+# --ascii_colors distro
+# --ascii_colors 2 4 5 6
+ascii_colors=(distro)
+
+# Logo size
+# Arch, Crux and Gentoo have a smaller logo
+# variant. Changing the value below to small
+# will make neofetch use the small logo.
+# --ascii_logo_size small, normal
+ascii_logo_size="normal"
+
+# Bold ascii logo
+# Whether or not to bold the ascii logo.
+# --ascii_bold on/off
+ascii_bold="off"
+
+
+# }}}
+
+# Scrot Options {{{
+
+
+# Whether or not to always take a screenshot
+# You can manually take a screenshot with "--scrot" or "-s"
+scrot="off"
+
+# Screenshot program to launch
+# --scrot_cmd
+scrot_cmd="scrot -c -d 3"
+
+# Scrot dir
+# Where to save the screenshots
+# --scrot_dir /path/to/screenshot/folder
+scrot_dir="$HOME/Pictures/"
+
+# Scrot filename
+# What to name the screenshots
+# --scrot_name str
+scrot_name="neofetch-$(date +%F-%I-%M-%S-${RANDOM}).png"
+
+# Image upload host
+# Where to upload the image.
+# Possible values: imgur, teknik
+image_host="imgur"
+
+# Imgur api key
+# This is an api key for neofetch, you can sign up for your own
+# here: http://api.imgur.com/oauth2/addclient
+imgur_client_id="0e8b44d15e9fc95"
+
+
+# }}}
+
+# Config Options {{{
+
+
+# Enable/Disable config file
+# --config off, none
+config="on"
+
+# Path to custom config file location
+# --config path/to/config
+config_file="${XDG_CONFIG_HOME:-${HOME}/.config}/neofetch/config"
+
+
+# }}}
diff --git a/.config/ranger/commands.py b/.config/ranger/commands.py
new file mode 100755
index 00000000..8c5e7486
--- /dev/null
+++ b/.config/ranger/commands.py
@@ -0,0 +1,216 @@
+# This is a sample commands.py. You can add your own commands here.
+#
+# Please refer to commands_full.py for all the default commands and a complete
+# documentation. Do NOT add them all here, or you may end up with defunct
+# commands when upgrading ranger.
+
+# You always need to import ranger.api.commands here to get the Command class:
+from ranger.api.commands import *
+
+# A simple command for demonstration purposes follows.
+#------------------------------------------------------------------------------
+
+# You can import any python module as needed.
+import os
+
+# Any class that is a subclass of "Command" will be integrated into ranger as a
+# command. Try typing ":my_edit" in ranger!
+class my_edit(Command):
+ # The so-called doc-string of the class will be visible in the built-in
+ # help that is accessible by typing "?c" inside ranger.
+ """:my_edit
+
+ A sample command for demonstration purposes that opens a file in an editor.
+ """
+
+ # The execute method is called when you run this command in ranger.
+ def execute(self):
+ # self.arg(1) is the first (space-separated) argument to the function.
+ # This way you can write ":my_edit somefilename".
+ if self.arg(1):
+ # self.rest(1) contains self.arg(1) and everything that follows
+ target_filename = self.rest(1)
+ else:
+ # self.fm is a ranger.core.filemanager.FileManager object and gives
+ # you access to internals of ranger.
+ # self.fm.thisfile is a ranger.container.file.File object and is a
+ # reference to the currently selected file.
+ target_filename = self.fm.thisfile.path
+
+ # This is a generic function to print text in ranger.
+ self.fm.notify("Let's edit the file " + target_filename + "!")
+
+ # Using bad=True in fm.notify allows you to print error messages:
+ if not os.path.exists(target_filename):
+ self.fm.notify("The given file does not exist!", bad=True)
+ return
+
+ # This executes a function from ranger.core.acitons, a module with a
+ # variety of subroutines that can help you construct commands.
+ # Check out the source, or run "pydoc ranger.core.actions" for a list.
+ self.fm.edit_file(target_filename)
+
+ # The tab method is called when you press tab, and should return a list of
+ # suggestions that the user will tab through.
+ def tab(self):
+ # This is a generic tab-completion function that iterates through the
+ # content of the current directory.
+ return self._tab_directory_content()
+
+
+# https://github.com/ranger/ranger/wiki/Integrating-File-Search-with-fzf
+# Now, simply bind this function to a key, by adding this to your ~/.config/ranger/rc.conf: map fzf_select
+class fzf_select(Command):
+ """
+ :fzf_select
+
+ Find a file using fzf.
+
+ With a prefix argument select only directories.
+
+ See: https://github.com/junegunn/fzf
+ """
+ def execute(self):
+ import subprocess
+ if self.quantifier:
+ # match only directories
+ command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"
+ else:
+ # match files and directories
+ command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"
+ fzf = self.fm.execute_command(command, stdout=subprocess.PIPE)
+ stdout, stderr = fzf.communicate()
+ if fzf.returncode == 0:
+ fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n'))
+ if os.path.isdir(fzf_file):
+ self.fm.cd(fzf_file)
+ else:
+ self.fm.select_file(fzf_file)
+# fzf_locate
+class fzf_locate(Command):
+ """
+ :fzf_locate
+
+ Find a file using fzf.
+
+ With a prefix argument select only directories.
+
+ See: https://github.com/junegunn/fzf
+ """
+ def execute(self):
+ import subprocess
+ if self.quantifier:
+ command="locate home media | fzf -e -i"
+ else:
+ command="locate home media | fzf -e -i"
+ fzf = self.fm.execute_command(command, stdout=subprocess.PIPE)
+ stdout, stderr = fzf.communicate()
+ if fzf.returncode == 0:
+ fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n'))
+ if os.path.isdir(fzf_file):
+ self.fm.cd(fzf_file)
+ else:
+ self.fm.select_file(fzf_file)
+
+class fzf_bring(Command):
+ """
+ :fzf_bring
+
+ Find a file using fzf and bring it to the current directory.
+
+ See: https://github.com/junegunn/fzf
+ """
+ def execute(self):
+ import subprocess
+ if self.quantifier:
+ # match only directories
+ command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"
+ else:
+ # match files and directories
+ command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \
+ -o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m"
+ fzf = self.fm.execute_command(command, stdout=subprocess.PIPE)
+ stdout, stderr = fzf.communicate()
+ if fzf.returncode == 0:
+ fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n'))
+ if os.path.isdir(fzf_file):
+ self.fm.cd(fzf_file)
+ else:
+ self.fm.select_file(fzf_file)
+
+
+import os
+from ranger.core.loader import CommandLoader
+
+class compress(Command):
+ def execute(self):
+ """ Compress marked files to current directory """
+ cwd = self.fm.thisdir
+ marked_files = cwd.get_selection()
+
+ if not marked_files:
+ return
+
+ def refresh(_):
+ cwd = self.fm.get_directory(original_path)
+ cwd.load_content()
+
+ original_path = cwd.path
+ parts = self.line.split()
+ au_flags = parts[1:]
+
+ descr = "compressing files in: " + os.path.basename(parts[1])
+ obj = CommandLoader(args=['apack'] + au_flags + \
+ [os.path.relpath(f.path, cwd.path) for f in marked_files], descr=descr)
+
+ obj.signal_bind('after', refresh)
+ self.fm.loader.add(obj)
+
+ def tab(self):
+ """ Complete with current folder name """
+
+ extension = ['.zip', '.tar.gz', '.rar', '.7z']
+ return ['compress ' + os.path.basename(self.fm.thisdir.path) + ext for ext in extension]
+
+
+
+
+import os
+from ranger.core.loader import CommandLoader
+
+class extracthere(Command):
+ def execute(self):
+ """ Extract copied files to current directory """
+ copied_files = tuple(self.fm.copy_buffer)
+
+ if not copied_files:
+ return
+
+ def refresh(_):
+ cwd = self.fm.get_directory(original_path)
+ cwd.load_content()
+
+ one_file = copied_files[0]
+ cwd = self.fm.thisdir
+ original_path = cwd.path
+ au_flags = ['-X', cwd.path]
+ au_flags += self.line.split()[1:]
+ au_flags += ['-e']
+
+ self.fm.copy_buffer.clear()
+ self.fm.cut_buffer = False
+ if len(copied_files) == 1:
+ descr = "extracting: " + os.path.basename(one_file.path)
+ else:
+ descr = "extracting files from: " + os.path.basename(one_file.dirname)
+ obj = CommandLoader(args=['aunpack'] + au_flags \
+ + [f.path for f in copied_files], descr=descr)
+
+ obj.signal_bind('after', refresh)
+ self.fm.loader.add(obj)
+
+
+
diff --git a/.config/ranger/commands_full.py b/.config/ranger/commands_full.py
new file mode 100755
index 00000000..9f0481ce
--- /dev/null
+++ b/.config/ranger/commands_full.py
@@ -0,0 +1,1508 @@
+# -*- coding: utf-8 -*-
+# This file is part of ranger, the console file manager.
+# This configuration file is licensed under the same terms as ranger.
+# ===================================================================
+#
+# NOTE: If you copied this file to ~/.config/ranger/commands_full.py,
+# then it will NOT be loaded by ranger, and only serve as a reference.
+#
+# ===================================================================
+# This file contains ranger's commands.
+# It's all in python; lines beginning with # are comments.
+#
+# Note that additional commands are automatically generated from the methods
+# of the class ranger.core.actions.Actions.
+#
+# You can customize commands in the file ~/.config/ranger/commands.py.
+# It has the same syntax as this file. In fact, you can just copy this
+# file there with `ranger --copy-config=commands' and make your modifications.
+# But make sure you update your configs when you update ranger.
+#
+# ===================================================================
+# Every class defined here which is a subclass of `Command' will be used as a
+# command in ranger. Several methods are defined to interface with ranger:
+# execute(): called when the command is executed.
+# cancel(): called when closing the console.
+# tab(): called when is pressed.
+# quick(): called after each keypress.
+#
+# The return values for tab() can be either:
+# None: There is no tab completion
+# A string: Change the console to this string
+# A list/tuple/generator: cycle through every item in it
+#
+# The return value for quick() can be:
+# False: Nothing happens
+# True: Execute the command afterwards
+#
+# The return value for execute() and cancel() doesn't matter.
+#
+# ===================================================================
+# Commands have certain attributes and methods that facilitate parsing of
+# the arguments:
+#
+# self.line: The whole line that was written in the console.
+# self.args: A list of all (space-separated) arguments to the command.
+# self.quantifier: If this command was mapped to the key "X" and
+# the user pressed 6X, self.quantifier will be 6.
+# self.arg(n): The n-th argument, or an empty string if it doesn't exist.
+# self.rest(n): The n-th argument plus everything that followed. For example,
+# if the command was "search foo bar a b c", rest(2) will be "bar a b c"
+# self.start(n): Anything before the n-th argument. For example, if the
+# command was "search foo bar a b c", start(2) will be "search foo"
+#
+# ===================================================================
+# And this is a little reference for common ranger functions and objects:
+#
+# self.fm: A reference to the "fm" object which contains most information
+# about ranger.
+# self.fm.notify(string): Print the given string on the screen.
+# self.fm.notify(string, bad=True): Print the given string in RED.
+# self.fm.reload_cwd(): Reload the current working directory.
+# self.fm.thisdir: The current working directory. (A File object.)
+# self.fm.thisfile: The current file. (A File object too.)
+# self.fm.thistab.get_selection(): A list of all selected files.
+# self.fm.execute_console(string): Execute the string as a ranger command.
+# self.fm.open_console(string): Open the console with the given string
+# already typed in for you.
+# self.fm.move(direction): Moves the cursor in the given direction, which
+# can be something like down=3, up=5, right=1, left=1, to=6, ...
+#
+# File objects (for example self.fm.thisfile) have these useful attributes and
+# methods:
+#
+# cf.path: The path to the file.
+# cf.basename: The base name only.
+# cf.load_content(): Force a loading of the directories content (which
+# obviously works with directories only)
+# cf.is_directory: True/False depending on whether it's a directory.
+#
+# For advanced commands it is unavoidable to dive a bit into the source code
+# of ranger.
+# ===================================================================
+
+from ranger.api.commands import *
+
+class alias(Command):
+ """:alias
+
+ Copies the oldcommand as newcommand.
+ """
+
+ context = 'browser'
+ resolve_macros = False
+
+ def execute(self):
+ if not self.arg(1) or not self.arg(2):
+ self.fm.notify('Syntax: alias ', bad=True)
+ else:
+ self.fm.commands.alias(self.arg(1), self.rest(2))
+
+class cd(Command):
+ """:cd [-r]
+
+ The cd command changes the directory.
+ The command 'cd -' is equivalent to typing ``.
+ Using the option "-r" will get you to the real path.
+ """
+
+ def execute(self):
+ import os.path
+ if self.arg(1) == '-r':
+ self.shift()
+ destination = os.path.realpath(self.rest(1))
+ if os.path.isfile(destination):
+ self.fm.select_file(destination)
+ return
+ else:
+ destination = self.rest(1)
+
+ if not destination:
+ destination = '~'
+
+ if destination == '-':
+ self.fm.enter_bookmark('`')
+ else:
+ self.fm.cd(destination)
+
+ def tab(self):
+ import os
+ from os.path import dirname, basename, expanduser, join
+
+ cwd = self.fm.thisdir.path
+ rel_dest = self.rest(1)
+
+ bookmarks = [v.path for v in self.fm.bookmarks.dct.values()
+ if rel_dest in v.path ]
+
+ # expand the tilde into the user directory
+ if rel_dest.startswith('~'):
+ rel_dest = expanduser(rel_dest)
+
+ # define some shortcuts
+ abs_dest = join(cwd, rel_dest)
+ abs_dirname = dirname(abs_dest)
+ rel_basename = basename(rel_dest)
+ rel_dirname = dirname(rel_dest)
+
+ try:
+ # are we at the end of a directory?
+ if rel_dest.endswith('/') or rel_dest == '':
+ _, dirnames, _ = next(os.walk(abs_dest))
+
+ # are we in the middle of the filename?
+ else:
+ _, dirnames, _ = next(os.walk(abs_dirname))
+ dirnames = [dn for dn in dirnames \
+ if dn.startswith(rel_basename)]
+ except (OSError, StopIteration):
+ # os.walk found nothing
+ pass
+ else:
+ dirnames.sort()
+ if self.fm.settings.cd_bookmarks:
+ dirnames = bookmarks + dirnames
+
+ # no results, return None
+ if len(dirnames) == 0:
+ return
+
+ # one result. since it must be a directory, append a slash.
+ if len(dirnames) == 1:
+ return self.start(1) + join(rel_dirname, dirnames[0]) + '/'
+
+ # more than one result. append no slash, so the user can
+ # manually type in the slash to advance into that directory
+ return (self.start(1) + join(rel_dirname, dirname) for dirname in dirnames)
+
+
+class chain(Command):
+ """:chain ; ; ...
+
+ Calls multiple commands at once, separated by semicolons.
+ """
+ def execute(self):
+ for command in self.rest(1).split(";"):
+ self.fm.execute_console(command)
+
+
+class shell(Command):
+ escape_macros_for_shell = True
+
+ def execute(self):
+ if self.arg(1) and self.arg(1)[0] == '-':
+ flags = self.arg(1)[1:]
+ command = self.rest(2)
+ else:
+ flags = ''
+ command = self.rest(1)
+
+ if not command and 'p' in flags:
+ command = 'cat %f'
+ if command:
+ if '%' in command:
+ command = self.fm.substitute_macros(command, escape=True)
+ self.fm.execute_command(command, flags=flags)
+
+ def tab(self):
+ from ranger.ext.get_executables import get_executables
+ if self.arg(1) and self.arg(1)[0] == '-':
+ command = self.rest(2)
+ else:
+ command = self.rest(1)
+ start = self.line[0:len(self.line) - len(command)]
+
+ try:
+ position_of_last_space = command.rindex(" ")
+ except ValueError:
+ return (start + program + ' ' for program \
+ in get_executables() if program.startswith(command))
+ if position_of_last_space == len(command) - 1:
+ selection = self.fm.thistab.get_selection()
+ if len(selection) == 1:
+ return self.line + selection[0].shell_escaped_basename + ' '
+ else:
+ return self.line + '%s '
+ else:
+ before_word, start_of_word = self.line.rsplit(' ', 1)
+ return (before_word + ' ' + file.shell_escaped_basename \
+ for file in self.fm.thisdir.files \
+ if file.shell_escaped_basename.startswith(start_of_word))
+
+class open_with(Command):
+ def execute(self):
+ app, flags, mode = self._get_app_flags_mode(self.rest(1))
+ self.fm.execute_file(
+ files = [f for f in self.fm.thistab.get_selection()],
+ app = app,
+ flags = flags,
+ mode = mode)
+
+ def tab(self):
+ return self._tab_through_executables()
+
+ def _get_app_flags_mode(self, string):
+ """Extracts the application, flags and mode from a string.
+
+ examples:
+ "mplayer f 1" => ("mplayer", "f", 1)
+ "aunpack 4" => ("aunpack", "", 4)
+ "p" => ("", "p", 0)
+ "" => None
+ """
+
+ app = ''
+ flags = ''
+ mode = 0
+ split = string.split()
+
+ if len(split) == 0:
+ pass
+
+ elif len(split) == 1:
+ part = split[0]
+ if self._is_app(part):
+ app = part
+ elif self._is_flags(part):
+ flags = part
+ elif self._is_mode(part):
+ mode = part
+
+ elif len(split) == 2:
+ part0 = split[0]
+ part1 = split[1]
+
+ if self._is_app(part0):
+ app = part0
+ if self._is_flags(part1):
+ flags = part1
+ elif self._is_mode(part1):
+ mode = part1
+ elif self._is_flags(part0):
+ flags = part0
+ if self._is_mode(part1):
+ mode = part1
+ elif self._is_mode(part0):
+ mode = part0
+ if self._is_flags(part1):
+ flags = part1
+
+ elif len(split) >= 3:
+ part0 = split[0]
+ part1 = split[1]
+ part2 = split[2]
+
+ if self._is_app(part0):
+ app = part0
+ if self._is_flags(part1):
+ flags = part1
+ if self._is_mode(part2):
+ mode = part2
+ elif self._is_mode(part1):
+ mode = part1
+ if self._is_flags(part2):
+ flags = part2
+ elif self._is_flags(part0):
+ flags = part0
+ if self._is_mode(part1):
+ mode = part1
+ elif self._is_mode(part0):
+ mode = part0
+ if self._is_flags(part1):
+ flags = part1
+
+ return app, flags, int(mode)
+
+ def _is_app(self, arg):
+ return not self._is_flags(arg) and not arg.isdigit()
+
+ def _is_flags(self, arg):
+ from ranger.core.runner import ALLOWED_FLAGS
+ return all(x in ALLOWED_FLAGS for x in arg)
+
+ def _is_mode(self, arg):
+ return all(x in '0123456789' for x in arg)
+
+
+class set_(Command):
+ """:set