First payload
5
.Rprofile
Normal file
@ -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/")))
|
||||
57
.Xdefaults
Normal file
@ -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
|
||||
13
.asoudrc
Normal file
@ -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"
|
||||
}
|
||||
14
.bash_profile
Normal file
@ -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
|
||||
26
.config/Scripts/audio.sh
Normal file
@ -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
|
||||
5
.config/Scripts/aurinstall.sh
Normal file
@ -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
|
||||
139
.config/Scripts/bashrc
Normal file
@ -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 ;}
|
||||
1
.config/Scripts/clear.sh
Executable file
@ -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
|
||||
24
.config/Scripts/configs
Normal file
@ -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
|
||||
7
.config/Scripts/drives
Executable file
@ -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
|
||||
24
.config/Scripts/extract.sh
Executable file
@ -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
|
||||
13
.config/Scripts/flash_win.sh
Normal file
@ -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
|
||||
10
.config/Scripts/folders
Executable file
@ -0,0 +1,10 @@
|
||||
h ~
|
||||
d ~/Documents
|
||||
D ~/Downloads
|
||||
p ~/Pictures
|
||||
v ~/Videos
|
||||
m ~/Music
|
||||
b ~/Books
|
||||
s ~/.config/Scripts
|
||||
/ /
|
||||
r /
|
||||
8
.config/Scripts/imapsync.sh
Normal file
@ -0,0 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f $(pgrep offlineimap) ]; then
|
||||
offlineimap -o
|
||||
echo "Sync begun."
|
||||
else
|
||||
echo "Sync in progress."
|
||||
fi
|
||||
154
.config/Scripts/luke_guide.md
Normal file
@ -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.
|
||||
BIN
.config/Scripts/luke_guide.pdf
Normal file
12
.config/Scripts/mailsyncloop.sh
Executable file
@ -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
|
||||
16
.config/Scripts/mount.sh
Executable file
@ -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
|
||||
|
||||
1
.config/Scripts/remaps
Normal file
@ -0,0 +1 @@
|
||||
keycode 135 = Super_R
|
||||
169
.config/Scripts/resize-font
Normal file
@ -0,0 +1,169 @@
|
||||
# vim:ft=perl:fenc=utf-8
|
||||
# Copyright (c) 2009-, Simon Lundström <simmel@soy.se>
|
||||
# Copyright (c) 2014 Maarten de Vries <maarten@de-vri.es>
|
||||
#
|
||||
# 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;
|
||||
}
|
||||
29
.config/Scripts/screencast.sh
Normal file
@ -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
|
||||
41
.config/Scripts/shortcuts.py
Normal file
@ -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)
|
||||
13
.config/Scripts/shrinkvid.sh
Executable file
@ -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"
|
||||
7
.config/Scripts/speedvid.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
base=$(basename $1)
|
||||
ext="${base##*.}"
|
||||
base="${base%.*}"
|
||||
|
||||
ffmpeg -i $1 -vf "setpts=$2*PTS" -an $base'_sped.'$ext
|
||||
11
.config/Scripts/status.sh
Normal file
@ -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
|
||||
11
.config/Scripts/test.sh
Executable file
@ -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
|
||||
177
.config/Scripts/tmux.conf
Normal file
@ -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
|
||||
7
.config/Scripts/transmission.sh
Executable file
@ -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
|
||||
4
.config/Scripts/unmount.sh
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/bash
|
||||
sync
|
||||
umount /mnt/* 2> /dev/null
|
||||
rm -d /mnt/* 2> /dev/null
|
||||
26
.config/Scripts/video.sh
Normal file
@ -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
|
||||
166
.config/Scripts/vimrc
Normal file
@ -0,0 +1,166 @@
|
||||
execute pathogen#infect()
|
||||
set number
|
||||
set relativenumber
|
||||
set so=10
|
||||
|
||||
|
||||
"""BASIC TOOLS
|
||||
"Navigating with guides
|
||||
inoremap <Space><Space> <Esc>/<++><Enter>"_c4l
|
||||
vnoremap <Space><Space> <Esc>/<++><Enter>"_c4l
|
||||
map <Space><Space> <Esc>/<++><Enter>"_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 <Esc>
|
||||
inoremap wj <Esc>
|
||||
inoremap <C-l> <Space><Space>
|
||||
"For split navigation
|
||||
map <C-h> <C-w>h
|
||||
map <C-j> <C-w>j
|
||||
map <C-k> <C-w>k
|
||||
map <C-l> <C-w>l
|
||||
|
||||
set nocompatible
|
||||
filetype plugin on
|
||||
|
||||
"""LATEX
|
||||
autocmd FileType tex inoremap ;fr \begin{frame}<Enter>\frametitle{}<Enter><Enter><++><Enter><Enter>\end{frame}<Enter><Enter><++><Esc>6kf}i
|
||||
autocmd FileType tex inoremap ;fi \begin{fitch}<Enter><Enter>\end{fitch}<Enter><Enter><++><Esc>3kA
|
||||
autocmd FileType tex inoremap ;exe \begin{exe}<Enter>\ex<Space><Enter>\end{exe}<Enter><Enter><++><Esc>3kA
|
||||
autocmd FileType tex inoremap ;em \emph{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;bf \textbf{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;it \textit{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;ct \textcite{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;cp \parencite{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;glos {\gll<Space><++><Space>\\<Enter><++><Space>\\<Enter>\trans{``<++>''}}<Esc>2k2bcw
|
||||
autocmd FileType tex inoremap ;x \begin{xlist}<Enter>\ex<Space><Enter>\end{xlist}<Esc>kA<Space>
|
||||
autocmd FileType tex inoremap ;ol \begin{enumerate}<Enter><Enter>\end{enumerate}<Enter><Enter><++><Esc>3kA\item<Space>
|
||||
autocmd FileType tex inoremap ;ul \begin{itemize}<Enter><Enter>\end{itemize}<Enter><Enter><++><Esc>3kA\item<Space>
|
||||
autocmd FileType tex inoremap ;li <Enter>\item<Space>
|
||||
autocmd FileType tex inoremap ;ref \ref{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;tab \begin{tabular}<Enter><++><Enter>\end{tabular}<Enter><Enter><++><Esc>4kA{}<Esc>i
|
||||
autocmd FileType tex inoremap ;ot \begin{tableau}<Enter>\inp{<++>}<Tab>\const{<++>}<Tab><++><Enter><++><Enter>\end{tableau}<Enter><Enter><++><Esc>5kA{}<Esc>i
|
||||
autocmd FileType tex inoremap ;can \cand{}<Tab><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;con \const{}<Tab><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;v \vio{}<Tab><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;a \href{}{<++>}<Space><++><Esc>2T{i
|
||||
autocmd FileType tex inoremap ;sc \textsc{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;sec \section{}<Enter><Enter><++><Esc>2kf}i
|
||||
autocmd FileType tex inoremap ;ssec \subsection{}<Enter><Enter><++><Esc>2kf}i
|
||||
autocmd FileType tex inoremap ;sssec \subsubsection{}<Enter><Enter><++><Esc>2kf}i
|
||||
autocmd FileType tex inoremap ;st <Esc>F{i*<Esc>f}i
|
||||
autocmd FileType tex inoremap ;beg \begin{%DELRN%}<Enter><++><Enter>\end{%DELRN%}<Enter><Enter><++><Esc>4kfR:MultipleCursorsFind<Space>%DELRN%<Enter>c
|
||||
"autocmd FileType tex inoremap ;up \usepackage{}<Esc>i
|
||||
autocmd FileType tex inoremap ;up <Esc>/usepackage<Enter>o\usepackage{}<Esc>i
|
||||
autocmd FileType tex nnoremap ;up /usepackage<Enter>o\usepackage{}<Esc>i
|
||||
autocmd FileType tex inoremap ;tt \texttt{}<Space><++><Esc>T{i
|
||||
autocmd FileType tex inoremap ;bt {\blindtext}
|
||||
autocmd FileType tex inoremap ;nu $\varnothing$
|
||||
autocmd FileType tex inoremap ;col \begin{columns}[T]<Enter>\begin{column}{.5\textwidth}<Enter><Enter>\end{column}<Enter>\begin{column}{.5\textwidth}<Enter><++><Enter>\end{column}<Enter>\end{columns}<Esc>5kA
|
||||
autocmd FileType tex inoremap ;rn (\ref{})<++><Esc>F}i
|
||||
"""END
|
||||
|
||||
"""Logical Symbols
|
||||
autocmd FileType tex inoremap ;m $$<Space><++><Esc>2T$i
|
||||
autocmd FileType tex inoremap ;M $$$$<Enter><Enter><++><Esc>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{}<Space><++><Esc>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 <b></b><Space><++><Esc>FbT>i
|
||||
autocmd FileType html inoremap ;i <em></em><Space><++><Esc>FeT>i
|
||||
autocmd FileType html inoremap ;1 <h1></h1><Enter><Enter><++><Esc>2kf<i
|
||||
autocmd FileType html inoremap ;2 <h2></h2><Enter><Enter><++><Esc>2kf<i
|
||||
autocmd FileType html inoremap ;3 <h3></h3><Enter><Enter><++><Esc>2kf<i
|
||||
autocmd FileType html inoremap ;p <p></p><Enter><Enter><++><Esc>02kf>a
|
||||
autocmd FileType html inoremap ;a <a<Space>href=""><++></a><Space><++><Esc>F"i
|
||||
autocmd FileType html inoremap ;ul <ul><Enter><li></li><Enter></ul><Enter><Enter><++><Esc>03kf<i
|
||||
autocmd FileType html inoremap ;li <Esc>o<li></li><Esc>F>a
|
||||
autocmd FileType html inoremap ;ol <ol><Enter><li></li><Enter></ol><Enter><Enter><++><Esc>03kf<i
|
||||
|
||||
"""END
|
||||
|
||||
""".bib
|
||||
autocmd FileType bib inoremap ;a @article{<Enter><Tab>author<Space>=<Space>"<++>",<Enter><Tab>year<Space>=<Space>"<++>",<Enter><Tab>title<Space>=<Space>"<++>",<Enter><Tab>journal<Space>=<Space>"<++>",<Enter><Tab>volume<Space>=<Space>"<++>",<Enter><Tab>pages<Space>=<Space>"<++>",<Enter><Tab>}<Enter><++><Esc>8kA,<Esc>i
|
||||
autocmd FileType bib inoremap ;b @book{<Enter><Tab>author<Space>=<Space>"<++>",<Enter><Tab>year<Space>=<Space>"<++>",<Enter><Tab>title<Space>=<Space>"<++>",<Enter><Tab>publisher<Space>=<Space>"<++>",<Enter><Tab>}<Enter><++><Esc>6kA,<Esc>i
|
||||
autocmd FileType bib inoremap ;c @incollection{<Enter><Tab>author<Space>=<Space>"<++>",<Enter><Tab>title<Space>=<Space>"<++>",<Enter><Tab>booktitle<Space>=<Space>"<++>",<Enter><Tab>editor<Space>=<Space>"<++>",<Enter><Tab>year<Space>=<Space>"<++>",<Enter><Tab>publisher<Space>=<Space>"<++>",<Enter><Tab>}<Enter><++><Esc>8kA,<Esc>i
|
||||
"""END
|
||||
|
||||
let g:instant_markdown_autostart = 0
|
||||
|
||||
autocmd Filetype markdown inoremap ;n ---<Enter><Enter>
|
||||
autocmd Filetype markdown inoremap ;b ****<Space><++><Esc>F*hi
|
||||
autocmd Filetype markdown inoremap ;s ~~~~<Space><++><Esc>F~hi
|
||||
autocmd Filetype markdown inoremap ;e **<Space><++><Esc>F*i
|
||||
autocmd Filetype markdown inoremap ;h ====<Space><++><Esc>F=hi
|
||||
autocmd Filetype markdown inoremap ;i <Space><++><Esc>F[a
|
||||
autocmd Filetype markdown inoremap ;a [](<++>)<Space><++><Esc>F[a
|
||||
autocmd Filetype markdown inoremap ;1 #<Space><Enter><++><Esc>kA
|
||||
autocmd Filetype markdown inoremap ;2 ##<Space><Enter><++><Esc>kA
|
||||
autocmd Filetype markdown inoremap ;3 ###<Space><Enter><++><Esc>kA
|
||||
autocmd Filetype markdown inoremap ;l --------<Enter>
|
||||
autocmd Filetype markdown nnoremap <C-p> :!pandoc<Space>-t<Space>beamer<Space>-s<Space><C-r>%<space>-o<Space><C-r>%<Backspace><Backspace>pdf<Space>-V<Space>theme:Copenhagen<Space>--latex-engine=xelatex<Enter>
|
||||
|
||||
|
||||
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 <F6> :setlocal spell! spelllang=en_us<CR>
|
||||
map <F10> :Goyo<CR>
|
||||
|
||||
set wildmode=longest,list,full
|
||||
set wildmenu
|
||||
16
.config/Scripts/wall_looper.sh
Normal file
@ -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
|
||||
25
.config/calcurse/conf
Normal file
@ -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
|
||||
70
.config/calcurse/keys
Normal file
@ -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 -
|
||||
56
.config/compton.conf
Normal file
@ -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; };
|
||||
};
|
||||
12
.config/feh/keys
Executable file
@ -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
|
||||
24
.config/fontconfig/fonts.conf
Executable file
@ -0,0 +1,24 @@
|
||||
<?xml version='1.0'?>
|
||||
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
|
||||
<fontconfig>
|
||||
|
||||
<alias>
|
||||
<family>serif</family>
|
||||
<prefer><family>Tinos</family></prefer>
|
||||
</alias>
|
||||
<alias>
|
||||
<family>sans-serif</family>
|
||||
<prefer><family>Arimo</family></prefer>
|
||||
</alias>
|
||||
<alias>
|
||||
<family>sans</family>
|
||||
<prefer><family>Arimo</family></prefer>
|
||||
</alias>
|
||||
<alias>
|
||||
<family>monospace</family>
|
||||
<prefer><family>Tamsyn</family></prefer>
|
||||
</alias>
|
||||
|
||||
</fontconfig>
|
||||
|
||||
|
||||
15
.config/i3/bar/.codeclimate.yml
Normal file
@ -0,0 +1,15 @@
|
||||
engines:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- python
|
||||
fixme:
|
||||
enabled: true
|
||||
radon:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.py"
|
||||
exclude_paths:
|
||||
- tests/
|
||||
10
.config/i3/bar/.coveragerc
Normal file
@ -0,0 +1,10 @@
|
||||
[run]
|
||||
omit =
|
||||
tests/*
|
||||
*mock*
|
||||
*funcsigs*
|
||||
*pbr*
|
||||
*six*
|
||||
/usr/lib*
|
||||
|
||||
[report]
|
||||
91
.config/i3/bar/.gitignore
vendored
Normal file
@ -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
|
||||
18
.config/i3/bar/.travis.yml
Normal file
@ -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
|
||||
21
.config/i3/bar/LICENSE
Normal file
@ -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.
|
||||
134
.config/i3/bar/README.md
Normal file
@ -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 = <path to bumblebee-status/bumblebee-status> -m <list of modules> -p <list of module parameters> -t <theme>
|
||||
}
|
||||
```
|
||||
|
||||
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 <list of modules> -p interval=<interval in seconds>
|
||||
```
|
||||
|
||||
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 <list of modules>
|
||||
```
|
||||
|
||||
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`):
|
||||
|
||||

|
||||
26
.config/i3/bar/bin/load-i3-bars.sh
Executable file
@ -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
|
||||
22
.config/i3/bar/bin/pacman-updates
Executable file
@ -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
|
||||
12
.config/i3/bar/bin/toggle-display.sh
Executable file
@ -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
|
||||
66
.config/i3/bar/bumblebee-status
Executable file
@ -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
|
||||
0
.config/i3/bar/bumblebee/__init__.py
Normal file
99
.config/i3/bar/bumblebee/config.py
Normal file
@ -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 <module>:<alias> 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 <module>.<key>=<value>"
|
||||
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
|
||||
166
.config/i3/bar/bumblebee/engine.py
Normal file
@ -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
|
||||
15
.config/i3/bar/bumblebee/error.py
Normal file
@ -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
|
||||
134
.config/i3/bar/bumblebee/input.py
Normal file
@ -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
|
||||
0
.config/i3/bar/bumblebee/modules/__init__.py
Normal file
36
.config/i3/bar/bumblebee/modules/amixer.py
Normal file
@ -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" ]
|
||||
82
.config/i3/bar/bumblebee/modules/battery.py
Normal file
@ -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
|
||||
75
.config/i3/bar/bumblebee/modules/battery.py_ori
Normal file
@ -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
|
||||
37
.config/i3/bar/bumblebee/modules/brightness.py
Normal file
@ -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
|
||||
48
.config/i3/bar/bumblebee/modules/caffeine.py
Normal file
@ -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
|
||||
92
.config/i3/bar/bumblebee/modules/cmus.py
Normal file
@ -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
|
||||
37
.config/i3/bar/bumblebee/modules/cpu.py
Normal file
@ -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
|
||||
35
.config/i3/bar/bumblebee/modules/datetime.py
Normal file
@ -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
|
||||
47
.config/i3/bar/bumblebee/modules/disk.py
Normal file
@ -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
|
||||
45
.config/i3/bar/bumblebee/modules/disk.py_ori
Normal file
@ -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
|
||||
80
.config/i3/bar/bumblebee/modules/dnf.py
Normal file
@ -0,0 +1,80 @@
|
||||
# pylint: disable=C0111,R0903
|
||||
|
||||
"""Displays DNF package update information (<security>/<bugfixes>/<enhancements>/<other>)
|
||||
|
||||
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
|
||||
26
.config/i3/bar/bumblebee/modules/error.py
Normal file
@ -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
|
||||
58
.config/i3/bar/bumblebee/modules/gpmdp.py
Normal file
@ -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
|
||||
21
.config/i3/bar/bumblebee/modules/kernel.py
Normal file
@ -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
|
||||
74
.config/i3/bar/bumblebee/modules/layout.py
Normal file
@ -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
|
||||
41
.config/i3/bar/bumblebee/modules/load.py
Normal file
@ -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
|
||||
47
.config/i3/bar/bumblebee/modules/memory.py
Normal file
@ -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
|
||||
83
.config/i3/bar/bumblebee/modules/nic.py
Normal file
@ -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
|
||||
88
.config/i3/bar/bumblebee/modules/nic.py_ori
Normal file
@ -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
|
||||
70
.config/i3/bar/bumblebee/modules/pacman.py
Normal file
@ -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
|
||||
77
.config/i3/bar/bumblebee/modules/ping.py
Normal file
@ -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
|
||||
107
.config/i3/bar/bumblebee/modules/pulseaudio.py
Normal file
@ -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
|
||||
46
.config/i3/bar/bumblebee/modules/redshift.py
Normal file
@ -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
|
||||
45
.config/i3/bar/bumblebee/modules/sensors.py
Normal file
@ -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
|
||||
23
.config/i3/bar/bumblebee/modules/spacer.py
Normal file
@ -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
|
||||
62
.config/i3/bar/bumblebee/modules/stock.py
Normal file
@ -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
|
||||
16
.config/i3/bar/bumblebee/modules/test.py
Normal file
@ -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
|
||||
72
.config/i3/bar/bumblebee/modules/traffic.py
Normal file
@ -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
|
||||
74
.config/i3/bar/bumblebee/modules/weather.py
Normal file
@ -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
|
||||
74
.config/i3/bar/bumblebee/modules/xrandr.py
Normal file
@ -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
|
||||
140
.config/i3/bar/bumblebee/output.py
Normal file
@ -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
|
||||
21
.config/i3/bar/bumblebee/store.py
Normal file
@ -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
|
||||
180
.config/i3/bar/bumblebee/theme.py
Normal file
@ -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
|
||||
47
.config/i3/bar/bumblebee/util.py
Normal file
@ -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
|
||||
3
.config/i3/bar/runlint.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
find . -name "*.py"|xargs pylint --disable=R0903,R0201,C0330
|
||||
11
.config/i3/bar/runtests.sh
Executable file
@ -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
|
||||
BIN
.config/i3/bar/screenshots/amixer.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
.config/i3/bar/screenshots/battery.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
.config/i3/bar/screenshots/brightness.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
.config/i3/bar/screenshots/caffeine.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
.config/i3/bar/screenshots/cmus.png
Normal file
|
After Width: | Height: | Size: 4.9 KiB |
BIN
.config/i3/bar/screenshots/cpu.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
.config/i3/bar/screenshots/date.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
.config/i3/bar/screenshots/datetime.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
BIN
.config/i3/bar/screenshots/disk.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
BIN
.config/i3/bar/screenshots/dnf.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
.config/i3/bar/screenshots/kernel.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |