First payload

This commit is contained in:
Luke 2017-06-02 15:49:10 -04:00
parent 68bd2acdc8
commit 34c4203b97
240 changed files with 25320 additions and 2 deletions

5
.Rprofile Normal file
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View File

@ -0,0 +1,10 @@
h ~
d ~/Documents
D ~/Downloads
p ~/Pictures
v ~/Videos
m ~/Music
b ~/Books
s ~/.config/Scripts
/ /
r /

View File

@ -0,0 +1,8 @@
#!/bin/bash
if [ -f $(pgrep offlineimap) ]; then
offlineimap -o
echo "Sync begun."
else
echo "Sync in progress."
fi

View 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.

Binary file not shown.

12
.config/Scripts/mailsyncloop.sh Executable file
View 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
View 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
View File

@ -0,0 +1 @@
keycode 135 = Super_R

169
.config/Scripts/resize-font Normal file
View 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;
}

View 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

View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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>

View File

@ -0,0 +1,15 @@
engines:
duplication:
enabled: true
config:
languages:
- python
fixme:
enabled: true
radon:
enabled: true
ratings:
paths:
- "**.py"
exclude_paths:
- tests/

View File

@ -0,0 +1,10 @@
[run]
omit =
tests/*
*mock*
*funcsigs*
*pbr*
*six*
/usr/lib*
[report]

91
.config/i3/bar/.gitignore vendored Normal file
View 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

View 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
View 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
View File

@ -0,0 +1,134 @@
# bumblebee-status
[![Build Status](https://travis-ci.org/tobi-wan-kenobi/bumblebee-status.svg?branch=master)](https://travis-ci.org/tobi-wan-kenobi/bumblebee-status)
[![Code Climate](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/gpa.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status)
[![Test Coverage](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/coverage.svg)](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/coverage)
[![Issue Count](https://codeclimate.com/github/tobi-wan-kenobi/bumblebee-status/badges/issue_count.svg)](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)):
![Gruvbox Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-gruvbox.png)
Solarized Powerline (`-t solarized-powerline`):
![Solarized Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline-solarized.png)
Gruvbox (`-t gruvbox`):
![Gruvbox](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/gruvbox.png)
Solarized (`-t solarized`):
![Solarized](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/solarized.png)
Powerline (`-t powerline`):
![Powerline](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/powerline.png)
Default (nothing or `-t default`):
![Default](https://github.com/tobi-wan-kenobi/bumblebee-status/blob/master/screenshots/themes/default.png)

View 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

View 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

View 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
View 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

View File

View 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

View 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

View 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

View 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

View 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" ]

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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
View File

@ -0,0 +1,3 @@
#!/bin/sh
find . -name "*.py"|xargs pylint --disable=R0903,R0201,C0330

11
.config/i3/bar/runtests.sh Executable file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Some files were not shown because too many files have changed in this diff Show More