aboutsummaryrefslogtreecommitdiff
path: root/i3/.config
diff options
context:
space:
mode:
Diffstat (limited to 'i3/.config')
-rw-r--r--i3/.config/i3/bar/.codeclimate.yml15
-rw-r--r--i3/.config/i3/bar/.coveragerc10
-rw-r--r--i3/.config/i3/bar/.gitignore91
-rw-r--r--i3/.config/i3/bar/.travis.yml18
-rw-r--r--i3/.config/i3/bar/LICENSE21
-rw-r--r--i3/.config/i3/bar/README.md134
-rw-r--r--i3/.config/i3/bar/bin/load-i3-bars.sh26
-rw-r--r--i3/.config/i3/bar/bin/pacman-updates22
-rw-r--r--i3/.config/i3/bar/bin/toggle-display.sh12
-rw-r--r--i3/.config/i3/bar/bumblebee-status66
-rw-r--r--i3/.config/i3/bar/bumblebee/__init__.py0
-rw-r--r--i3/.config/i3/bar/bumblebee/config.py99
-rw-r--r--i3/.config/i3/bar/bumblebee/engine.py166
-rw-r--r--i3/.config/i3/bar/bumblebee/error.py15
-rw-r--r--i3/.config/i3/bar/bumblebee/input.py134
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/__init__.py0
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/amixer.py36
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/battery.py82
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/battery.py_ori75
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/brightness.py37
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/caffeine.py48
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/cmus.py92
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/cpu.py37
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/datetime.py35
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/disk.py47
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/disk.py_ori45
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/dnf.py80
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/error.py26
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/gpmdp.py58
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/kernel.py21
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/layout.py74
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/load.py41
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/memory.py47
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/nic.py83
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/nic.py_ori88
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/pacman.py70
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/ping.py77
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/pulseaudio.py107
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/redshift.py46
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/sensors.py45
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/spacer.py23
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/stock.py62
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/test.py16
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/traffic.py72
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/weather.py74
-rw-r--r--i3/.config/i3/bar/bumblebee/modules/xrandr.py74
-rw-r--r--i3/.config/i3/bar/bumblebee/output.py140
-rw-r--r--i3/.config/i3/bar/bumblebee/store.py21
-rw-r--r--i3/.config/i3/bar/bumblebee/theme.py180
-rw-r--r--i3/.config/i3/bar/bumblebee/util.py47
-rw-r--r--i3/.config/i3/bar/runlint.sh3
-rw-r--r--i3/.config/i3/bar/runtests.sh11
-rw-r--r--i3/.config/i3/bar/screenshots/amixer.pngbin0 -> 1645 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/battery.pngbin0 -> 1733 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/brightness.pngbin0 -> 1323 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/caffeine.pngbin0 -> 1143 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/cmus.pngbin0 -> 5031 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/cpu.pngbin0 -> 2003 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/date.pngbin0 -> 2877 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/datetime.pngbin0 -> 4889 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/disk.pngbin0 -> 3721 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/dnf.pngbin0 -> 1113 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/kernel.pngbin0 -> 3125 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/layout.pngbin0 -> 1198 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/load.pngbin0 -> 2005 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/memory.pngbin0 -> 3603 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/nic.pngbin0 -> 2474 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/pacman.pngbin0 -> 860 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/pasink.pngbin0 -> 1486 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/pasource.pngbin0 -> 1505 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/ping.pngbin0 -> 1655 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/pulseaudio.pngbin0 -> 2495 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/redshift.pngbin0 -> 1325 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/sensors.pngbin0 -> 1054 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/spacer.pngbin0 -> 521 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/stock.pngbin0 -> 2087 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/themes/default.pngbin0 -> 7251 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/themes/gruvbox.pngbin0 -> 10310 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/themes/powerline-gruvbox.pngbin0 -> 11594 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/themes/powerline-solarized.pngbin0 -> 11991 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/themes/powerline.pngbin0 -> 10701 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/themes/solarized.pngbin0 -> 9612 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/time.pngbin0 -> 2456 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/traffic.pngbin0 -> 3811 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/weather.pngbin0 -> 1111 bytes
-rw-r--r--i3/.config/i3/bar/screenshots/xrandr.pngbin0 -> 2693 bytes
-rw-r--r--i3/.config/i3/bar/testjson.sh3
-rw-r--r--i3/.config/i3/bar/tests/__init__.py0
-rw-r--r--i3/.config/i3/bar/tests/mocks.py141
-rw-r--r--i3/.config/i3/bar/tests/modules/__init__.py0
-rw-r--r--i3/.config/i3/bar/tests/modules/test_battery.py108
-rw-r--r--i3/.config/i3/bar/tests/modules/test_brightness.py47
-rw-r--r--i3/.config/i3/bar/tests/modules/test_caffeine.py53
-rw-r--r--i3/.config/i3/bar/tests/modules/test_cmus.py114
-rw-r--r--i3/.config/i3/bar/tests/modules/test_cpu.py45
-rw-r--r--i3/.config/i3/bar/tests/modules/test_disk.py47
-rw-r--r--i3/.config/i3/bar/tests/modules/test_load.py57
-rw-r--r--i3/.config/i3/bar/tests/modules/test_memory.py52
-rw-r--r--i3/.config/i3/bar/tests/modules/test_pulseaudio.py34
-rw-r--r--i3/.config/i3/bar/tests/test_config.py77
-rw-r--r--i3/.config/i3/bar/tests/test_engine.py90
-rw-r--r--i3/.config/i3/bar/tests/test_i3barinput.py110
-rw-r--r--i3/.config/i3/bar/tests/test_i3baroutput.py133
-rw-r--r--i3/.config/i3/bar/tests/test_module.py89
-rw-r--r--i3/.config/i3/bar/tests/test_modules.py58
-rw-r--r--i3/.config/i3/bar/tests/test_store.py26
-rw-r--r--i3/.config/i3/bar/tests/test_theme.py134
-rw-r--r--i3/.config/i3/bar/tests/test_util.py56
-rw-r--r--i3/.config/i3/bar/themes/default.json7
-rw-r--r--i3/.config/i3/bar/themes/gruvbox-powerline.json41
-rw-r--r--i3/.config/i3/bar/themes/gruvbox.json41
-rw-r--r--i3/.config/i3/bar/themes/icons/ascii.json70
-rw-r--r--i3/.config/i3/bar/themes/icons/awesome-fonts.json86
-rw-r--r--i3/.config/i3/bar/themes/icons/paxy97.json5
-rw-r--r--i3/.config/i3/bar/themes/icons/test.json6
-rw-r--r--i3/.config/i3/bar/themes/kulade.json46
-rw-r--r--i3/.config/i3/bar/themes/powerline.json41
-rw-r--r--i3/.config/i3/bar/themes/solarized-powerline.json40
-rw-r--r--i3/.config/i3/bar/themes/solarized.json42
-rw-r--r--i3/.config/i3/bar/themes/test.json25
-rw-r--r--i3/.config/i3/bar/themes/test_cycle.json18
-rw-r--r--i3/.config/i3/bar/themes/test_invalid.json1
-rw-r--r--i3/.config/i3/config560
-rw-r--r--i3/.config/i3/lock.pngbin0 -> 5016 bytes
-rw-r--r--i3/.config/i3/lock.sh8
-rw-r--r--i3/.config/i3blocks/config70
126 files changed, 5530 insertions, 0 deletions
diff --git a/i3/.config/i3/bar/.codeclimate.yml b/i3/.config/i3/bar/.codeclimate.yml
new file mode 100644
index 0000000..ec80874
--- /dev/null
+++ b/i3/.config/i3/bar/.codeclimate.yml
@@ -0,0 +1,15 @@
+engines:
+ duplication:
+ enabled: true
+ config:
+ languages:
+ - python
+ fixme:
+ enabled: true
+ radon:
+ enabled: true
+ratings:
+ paths:
+ - "**.py"
+exclude_paths:
+- tests/
diff --git a/i3/.config/i3/bar/.coveragerc b/i3/.config/i3/bar/.coveragerc
new file mode 100644
index 0000000..e809714
--- /dev/null
+++ b/i3/.config/i3/bar/.coveragerc
@@ -0,0 +1,10 @@
+[run]
+omit =
+ tests/*
+ *mock*
+ *funcsigs*
+ *pbr*
+ *six*
+ /usr/lib*
+
+[report]
diff --git a/i3/.config/i3/bar/.gitignore b/i3/.config/i3/bar/.gitignore
new file mode 100644
index 0000000..d7d9fd4
--- /dev/null
+++ b/i3/.config/i3/bar/.gitignore
@@ -0,0 +1,91 @@
+*swp
+
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+env/
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+*.egg-info/
+.installed.cfg
+*.egg
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*,cover
+.hypothesis/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# IPython Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# dotenv
+.env
+
+# virtualenv
+venv/
+ENV/
+
+# Spyder project settings
+.spyderproject
+
+# Rope project settings
+.ropeproject
diff --git a/i3/.config/i3/bar/.travis.yml b/i3/.config/i3/bar/.travis.yml
new file mode 100644
index 0000000..0e198ad
--- /dev/null
+++ b/i3/.config/i3/bar/.travis.yml
@@ -0,0 +1,18 @@
+language: python
+python:
+ - "2.7"
+ - "3.3"
+ - "3.4"
+ - "3.5"
+ - "3.6"
+install:
+ - pip install psutil
+ - pip install netifaces
+ - pip install -U coverage==4.3
+ - pip install codeclimate-test-reporter
+script:
+ - nosetests -v --with-coverage --cover-erase tests/
+ - CODECLIMATE_REPO_TOKEN=40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf codeclimate-test-reporter
+addons:
+ code_climate:
+ repo_token: 40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf
diff --git a/i3/.config/i3/bar/LICENSE b/i3/.config/i3/bar/LICENSE
new file mode 100644
index 0000000..b472f2e
--- /dev/null
+++ b/i3/.config/i3/bar/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 tobi-wan-kenobi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/i3/.config/i3/bar/README.md b/i3/.config/i3/bar/README.md
new file mode 100644
index 0000000..3570c33
--- /dev/null
+++ b/i3/.config/i3/bar/README.md
@@ -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)
diff --git a/i3/.config/i3/bar/bin/load-i3-bars.sh b/i3/.config/i3/bar/bin/load-i3-bars.sh
new file mode 100644
index 0000000..cbe0e95
--- /dev/null
+++ b/i3/.config/i3/bar/bin/load-i3-bars.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+if [ ! -f ~/.i3/config.template ]; then
+ cp ~/.i3/config ~/.i3/config.template
+else
+ cp ~/.i3/config.template ~/.i3/config
+fi
+
+screens=$(xrandr -q|grep ' connected'| grep -P '\d+x\d+' |cut -d' ' -f1)
+
+echo "screens: $screens"
+
+while read -r line; do
+ screen=$(echo $line | cut -d' ' -f1)
+ others=$(echo $screens|tr ' ' '\n'|grep -v $screen|tr '\n' '-'|sed 's/.$//')
+
+ if [ -f ~/.i3/config.$screen-$others ]; then
+ cat ~/.i3/config.$screen-$others >> ~/.i3/config
+ else
+ if [ -f ~/.i3/config.$screen ]; then
+ cat ~/.i3/config.$screen >> ~/.i3/config
+ fi
+ fi
+done <<< "$screens"
+
+i3-msg restart
diff --git a/i3/.config/i3/bar/bin/pacman-updates b/i3/.config/i3/bar/bin/pacman-updates
new file mode 100644
index 0000000..baf0b93
--- /dev/null
+++ b/i3/.config/i3/bar/bin/pacman-updates
@@ -0,0 +1,22 @@
+#!/usr/bin/bash
+
+if ! type -P fakeroot >/dev/null; then
+ error 'Cannot find the fakeroot binary.'
+ exit 1
+fi
+
+if [[ -z $CHECKUPDATES_DB ]]; then
+ CHECKUPDATES_DB="${TMPDIR:-/tmp}/checkup-db-${USER}/"
+fi
+
+trap 'rm -f $CHECKUPDATES_DB/db.lck' INT TERM EXIT
+
+DBPath="${DBPath:-/var/lib/pacman/}"
+eval $(awk -F' *= *' '$1 ~ /DBPath/ { print $1 "=" $2 }' /etc/pacman.conf)
+
+mkdir -p "$CHECKUPDATES_DB"
+ln -s "${DBPath}/local" "$CHECKUPDATES_DB" &> /dev/null
+fakeroot -- pacman -Sy --dbpath "$CHECKUPDATES_DB" --logfile /dev/null &> /dev/null
+fakeroot pacman -Su -p --dbpath "$CHECKUPDATES_DB"
+
+exit 0
diff --git a/i3/.config/i3/bar/bin/toggle-display.sh b/i3/.config/i3/bar/bin/toggle-display.sh
new file mode 100644
index 0000000..bd13a29
--- /dev/null
+++ b/i3/.config/i3/bar/bin/toggle-display.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+
+echo $(dirname $(readlink -f "$0"))
+
+i3bar_update=$(dirname $(readlink -f "$0"))/load-i3-bars.sh
+
+xrandr "$@"
+
+if [ -f $i3bar_update ]; then
+ sleep 1
+ $i3bar_update
+fi
diff --git a/i3/.config/i3/bar/bumblebee-status b/i3/.config/i3/bar/bumblebee-status
new file mode 100644
index 0000000..582f314
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee-status
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+import os
+import sys
+import logging
+import bumblebee.theme
+import bumblebee.engine
+import bumblebee.config
+import bumblebee.output
+import bumblebee.input
+import bumblebee.modules.error
+
+def main():
+ config = bumblebee.config.Config(sys.argv[1:])
+
+ if config.debug():
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format="[%(asctime)s] %(levelname)-8s %(message)s",
+ filename=os.path.expanduser(config.logfile())
+ )
+ else:
+ logging.basicConfig(
+ level=logging.DEBUG,
+ format="[%(asctime)s] %(levelname)-8s %(message)s",
+ stream=sys.stderr
+ )
+
+ theme = bumblebee.theme.Theme(config.theme())
+ output = bumblebee.output.I3BarOutput(theme=theme)
+ inp = bumblebee.input.I3BarInput()
+ engine = None
+
+ try:
+ engine = bumblebee.engine.Engine(
+ config=config,
+ output=output,
+ inp=inp,
+ )
+ engine.run()
+ except KeyboardInterrupt as error:
+ inp.stop()
+ sys.exit(0)
+ except BaseException as e:
+ logging.exception(e)
+ if output.started():
+ output.flush()
+ output.end()
+ else:
+ output.start()
+ import time
+ while True:
+ output.begin()
+ error = bumblebee.modules.error.Module(engine, config)
+ error.set("exception occurred: {}".format(e))
+ widget = error.widgets()[0]
+ widget.link_module(error)
+ output.draw(widget, error)
+ output.flush()
+ output.end()
+ time.sleep(1)
+
+if __name__ == "__main__":
+ main()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/__init__.py b/i3/.config/i3/bar/bumblebee/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/__init__.py
diff --git a/i3/.config/i3/bar/bumblebee/config.py b/i3/.config/i3/bar/bumblebee/config.py
new file mode 100644
index 0000000..4e746bd
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/config.py
@@ -0,0 +1,99 @@
+"""Configuration handling
+
+This module provides configuration information (loaded modules,
+module parameters, etc.) to all other components
+"""
+
+import os
+import sys
+import logging
+import argparse
+import textwrap
+import importlib
+import bumblebee.store
+
+MODULE_HELP = "Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use <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
diff --git a/i3/.config/i3/bar/bumblebee/engine.py b/i3/.config/i3/bar/bumblebee/engine.py
new file mode 100644
index 0000000..c0ea9cc
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/engine.py
@@ -0,0 +1,166 @@
+"""Core application engine"""
+
+import os
+import time
+import pkgutil
+import importlib
+import bumblebee.error
+import bumblebee.modules
+
+def all_modules():
+ """Return a list of available modules"""
+ result = []
+ path = os.path.dirname(bumblebee.modules.__file__)
+ for mod in [name for _, name, _ in pkgutil.iter_modules([path])]:
+ result.append({
+ "name": mod
+ })
+ return result
+
+class Module(object):
+ """Module instance base class
+
+ Objects of this type represent the modules that
+ the user configures. Concrete module implementations
+ (e.g. CPU utilization, disk usage, etc.) derive from
+ this base class.
+ """
+ def __init__(self, engine, config={}, widgets=[]):
+ self.name = config.get("name", self.__module__.split(".")[-1])
+ self._config = config
+ self.id = self.name
+ self._widgets = []
+ if widgets:
+ self._widgets = widgets if isinstance(widgets, list) else [widgets]
+
+ def widgets(self):
+ """Return the widgets to draw for this module"""
+ return self._widgets
+
+ def widget(self, name):
+ for widget in self._widgets:
+ if widget.name == name:
+ return widget
+
+ def widget_by_id(self, uid):
+ for widget in self._widgets:
+ if widget.id == uid:
+ return widget
+ return None
+
+ def update(self, widgets):
+ """By default, update() is a NOP"""
+ pass
+
+ def update_all(self):
+ self.update(self._widgets)
+
+ def parameter(self, name, default=None):
+ """Return the config parameter 'name' for this module"""
+ name = "{}.{}".format(self.name, name)
+ return self._config["config"].get(name, default)
+
+ def threshold_state(self, value, warn, crit):
+ if value > float(self.parameter("critical", crit)):
+ return "critical"
+ if value > float(self.parameter("warning", warn)):
+ return "warning"
+ return None
+
+class Engine(object):
+ """Engine for driving the application
+
+ This class connects input/output, instantiates all
+ required modules and drives the "event loop"
+ """
+ def __init__(self, config, output=None, inp=None):
+ self._output = output
+ self._config = config
+ self._running = True
+ self._modules = []
+ self.input = inp
+ self._aliases = self._read_aliases()
+ self.load_modules(config.modules())
+
+ self.input.register_callback(None, bumblebee.input.WHEEL_UP,
+ "i3-msg workspace prev_on_output")
+ self.input.register_callback(None, bumblebee.input.WHEEL_DOWN,
+ "i3-msg workspace next_on_output")
+
+ self.input.start()
+
+ def modules(self):
+ return self._modules
+
+ def load_modules(self, modules):
+ """Load specified modules and return them as list"""
+ for module in modules:
+ mod = self._load_module(module["module"], module["name"])
+ self._modules.append(mod)
+ self._register_module_callbacks(mod)
+ return self._modules
+
+ def _register_module_callbacks(self, module):
+ buttons = [
+ { "name": "left-click", "id": bumblebee.input.LEFT_MOUSE },
+ { "name": "middle-click", "id": bumblebee.input.MIDDLE_MOUSE },
+ { "name": "right-click", "id": bumblebee.input.RIGHT_MOUSE },
+ { "name": "wheel-up", "id": bumblebee.input.WHEEL_UP },
+ { "name": "wheel-down", "id": bumblebee.input.WHEEL_DOWN },
+ ]
+ for button in buttons:
+ if module.parameter(button["name"], None):
+ self.input.register_callback(obj=module,
+ button=button["id"], cmd=module.parameter(button["name"]))
+
+ def _read_aliases(self):
+ result = {}
+ for module in all_modules():
+ mod = importlib.import_module("bumblebee.modules.{}".format(module["name"]))
+ for alias in getattr(mod, "ALIASES", []):
+ result[alias] = module["name"]
+ return result
+
+ def _load_module(self, module_name, config_name=None):
+ """Load specified module and return it as object"""
+ if module_name in self._aliases:
+ config_name is config_name if config_name else module_name
+ module_name = self._aliases[module_name]
+ if config_name is None:
+ config_name = module_name
+ try:
+ module = importlib.import_module("bumblebee.modules.{}".format(module_name))
+ except ImportError as error:
+ raise bumblebee.error.ModuleLoadError(error)
+ return getattr(module, "Module")(self, {
+ "name": config_name,
+ "config": self._config
+ })
+
+ def running(self):
+ """Check whether the event loop is running"""
+ return self._running
+
+ def stop(self):
+ """Stop the event loop"""
+ self._running = False
+
+ def run(self):
+ """Start the event loop"""
+ self._output.start()
+ while self.running():
+ self._output.begin()
+ for module in self._modules:
+ module.update(module.widgets())
+ for widget in module.widgets():
+ widget.link_module(module)
+ self._output.draw(widget=widget, module=module, engine=self)
+ self._output.flush()
+ self._output.end()
+ if self.running():
+ self.input.wait(float(self._config.get("interval", 1)))
+
+ self._output.stop()
+ self.input.stop()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/error.py b/i3/.config/i3/bar/bumblebee/error.py
new file mode 100644
index 0000000..129f02d
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/error.py
@@ -0,0 +1,15 @@
+"""Collection of all exceptions raised by this tool"""
+
+class BaseError(Exception):
+ """Base class for all exceptions generated by this tool"""
+ pass
+
+class ModuleLoadError(BaseError):
+ """Raised whenever loading a module fails"""
+ pass
+
+class ThemeLoadError(BaseError):
+ """Raised whenever loading a theme fails"""
+ pass
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/input.py b/i3/.config/i3/bar/bumblebee/input.py
new file mode 100644
index 0000000..1678b2e
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/input.py
@@ -0,0 +1,134 @@
+"""Input classes"""
+
+import sys
+import json
+import uuid
+import time
+import select
+import threading
+import bumblebee.util
+
+LEFT_MOUSE = 1
+MIDDLE_MOUSE = 2
+RIGHT_MOUSE = 3
+WHEEL_UP = 4
+WHEEL_DOWN = 5
+
+def is_terminated():
+ for thread in threading.enumerate():
+ if thread.name == "MainThread" and not thread.is_alive():
+ return True
+ return False
+
+def read_input(inp):
+ """Read i3bar input and execute callbacks"""
+ epoll = select.epoll()
+ epoll.register(sys.stdin.fileno(), select.EPOLLIN)
+ while inp.running:
+ if is_terminated():
+ return
+
+ events = epoll.poll(1)
+ for fileno, event in events:
+ line = "["
+ while "[" in line:
+ line = sys.stdin.readline().strip(",").strip()
+ inp.has_event = True
+ try:
+ event = json.loads(line)
+ if "instance" in event:
+ inp.callback(event)
+ inp.redraw()
+ except ValueError:
+ pass
+ epoll.unregister(sys.stdin.fileno())
+ epoll.close()
+ inp.has_event = True
+ inp.clean_exit = True
+
+class I3BarInput(object):
+ """Process incoming events from the i3bar"""
+ def __init__(self):
+ self.running = True
+ self._callbacks = {}
+ self.clean_exit = False
+ self.global_id = str(uuid.uuid4())
+ self.need_event = False
+ self.has_event = False
+ self._condition = threading.Condition()
+
+ def start(self):
+ """Start asynchronous input processing"""
+ self.has_event = False
+ self.running = True
+ self._condition.acquire()
+ self._thread = threading.Thread(target=read_input, args=(self,))
+ self._thread.start()
+
+ def redraw(self):
+ self._condition.acquire()
+ self._condition.notify()
+ self._condition.release()
+
+ def alive(self):
+ """Check whether the input processing is still active"""
+ return self._thread.is_alive()
+
+ def wait(self, timeout):
+ self._condition.wait(timeout)
+
+ def _wait(self):
+ while not self.has_event:
+ time.sleep(0.1)
+ self.has_event = False
+
+ def stop(self):
+ """Stop asynchronous input processing"""
+ self._condition.release()
+ if self.need_event:
+ self._wait()
+ self.running = False
+ self._thread.join()
+ return self.clean_exit
+
+ def _uuidstr(self, name, button):
+ return "{}::{}".format(name, button)
+
+ def _uid(self, obj, button):
+ uid = self.global_id
+ if obj:
+ uid = obj.id
+ return self._uuidstr(uid, button)
+
+ def deregister_callbacks(self, obj):
+ to_delete = []
+ uid = obj.id if obj else self.global_id
+ for key in self._callbacks:
+ if uid in key:
+ to_delete.append(key)
+ for key in to_delete:
+ del self._callbacks[key]
+
+ def register_callback(self, obj, button, cmd):
+ """Register a callback function or system call"""
+ uid = self._uid(obj, button)
+ if uid not in self._callbacks:
+ self._callbacks[uid] = {}
+ self._callbacks[uid] = cmd
+
+ def callback(self, event):
+ """Execute callback action for an incoming event"""
+ button = event["button"]
+
+ cmd = self._callbacks.get(self._uuidstr(self.global_id, button), None)
+ cmd = self._callbacks.get(self._uuidstr(event["name"], button), cmd)
+ cmd = self._callbacks.get(self._uuidstr(event["instance"], button), cmd)
+
+ if cmd is None:
+ return
+ if callable(cmd):
+ cmd(event)
+ else:
+ bumblebee.util.execute(cmd, False)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/__init__.py b/i3/.config/i3/bar/bumblebee/modules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/__init__.py
diff --git a/i3/.config/i3/bar/bumblebee/modules/amixer.py b/i3/.config/i3/bar/bumblebee/modules/amixer.py
new file mode 100644
index 0000000..fa08ec3
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/amixer.py
@@ -0,0 +1,36 @@
+"""get volume level
+
+"""
+import re
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.volume)
+ )
+ self._level = "0"
+ self._muted = True
+ device = self.parameter("device", "Master,0")
+ self._cmdString = "amixer get {}".format(device)
+
+ def volume(self, widget):
+ m = re.search(r'([\d]+)\%', self._level)
+ self._muted = True
+ if m:
+ if m.group(1) != "0":
+ self._muted = False
+ return "{}%".format(m.group(1))
+ else:
+ return "0%"
+
+ def update(self, widgets):
+ self._level = bumblebee.util.execute(self._cmdString)
+
+ def state(self, widget):
+ if self._muted:
+ return [ "warning", "muted" ]
+ return [ "unmuted" ]
diff --git a/i3/.config/i3/bar/bumblebee/modules/battery.py b/i3/.config/i3/bar/bumblebee/modules/battery.py
new file mode 100644
index 0000000..f00c603
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/battery.py
@@ -0,0 +1,82 @@
+# pylint: disable=C0111,R0903
+
+"""Displays battery status, remaining percentage and charging information.
+
+Parameters:
+ * battery.device : The device to read information from (defaults to BAT0)
+ * battery.warning : Warning threshold in % of remaining charge (defaults to 20)
+ * battery.critical: Critical threshold in % of remaining charge (defaults to 10)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.capacity)
+ )
+ battery = self.parameter("device", "BAT0")
+ self._path = "/sys/class/power_supply/{}".format(battery)
+ self._capacity = 100
+ self._ac = False
+
+ def capacity(self, widget):
+ if self._ac:
+ return "ac"
+ if self._capacity == -1:
+ return "n/a"
+ return "{:03d}%".format(self._capacity)
+
+ def update(self, widgets):
+ widget = widgets[0]
+ self._ac = False
+ if not os.path.exists(self._path):
+ self._ac = True
+ self._capacity = 100
+ return
+
+ try:
+ with open(self._path + "/capacity") as f:
+ self._capacity = int(f.read())
+ except IOError:
+ self._capacity = -1
+ self._capacity = self._capacity if self._capacity < 100 else 100
+
+ def state(self, widget):
+ state = []
+
+ if self._capacity < 0:
+ return ["critical", "unknown"]
+
+ if self._capacity < int(self.parameter("critical", 10)):
+ state.append("critical")
+ elif self._capacity > int(self.parameter("great", 90)):
+ state.append("great")
+ elif self._capacity > int(self.parameter("good", 80)):
+ state.append("good")
+ elif self._capacity < int(self.parameter("warning", 20)):
+ state.append("warning")
+ elif self._capacity < int(self.parameter("mid", 80)):
+ state.append("mid")
+
+ if self._ac:
+ state.append("AC")
+ else:
+ charge = ""
+ with open(self._path + "/status") as f:
+ charge = f.read().strip()
+ if charge == "Discharging":
+ state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity))))
+ else:
+ if self._capacity > 95:
+ state.append("charged")
+ else:
+ state.append("charging")
+
+ return state
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/battery.py_ori b/i3/.config/i3/bar/bumblebee/modules/battery.py_ori
new file mode 100644
index 0000000..5384716
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/battery.py_ori
@@ -0,0 +1,75 @@
+# pylint: disable=C0111,R0903
+
+"""Displays battery status, remaining percentage and charging information.
+
+Parameters:
+ * battery.device : The device to read information from (defaults to BAT0)
+ * battery.warning : Warning threshold in % of remaining charge (defaults to 20)
+ * battery.critical: Critical threshold in % of remaining charge (defaults to 10)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.capacity)
+ )
+ battery = self.parameter("device", "BAT0")
+ self._path = "/sys/class/power_supply/{}".format(battery)
+ self._capacity = 100
+ self._ac = False
+
+ def capacity(self, widget):
+ if self._ac:
+ return "ac"
+ if self._capacity == -1:
+ return "n/a"
+ return "{:03d}%".format(self._capacity)
+
+ def update(self, widgets):
+ self._ac = False
+ if not os.path.exists(self._path):
+ self._ac = True
+ self._capacity = 100
+ return
+
+ try:
+ with open(self._path + "/capacity") as f:
+ self._capacity = int(f.read())
+ except IOError:
+ self._capacity = -1
+ self._capacity = self._capacity if self._capacity < 100 else 100
+
+ def state(self, widget):
+ state = []
+
+ if self._capacity < 0:
+ return ["critical", "unknown"]
+
+ if self._capacity < int(self.parameter("critical", 10)):
+ state.append("critical")
+ elif self._capacity < int(self.parameter("warning", 20)):
+ state.append("warning")
+
+ if self._ac:
+ state.append("AC")
+ else:
+ charge = ""
+ with open(self._path + "/status") as f:
+ charge = f.read().strip()
+ if charge == "Discharging":
+ state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity))))
+ else:
+ if self._capacity > 95:
+ state.append("charged")
+ else:
+ state.append("charging")
+
+ return state
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/brightness.py b/i3/.config/i3/bar/bumblebee/modules/brightness.py
new file mode 100644
index 0000000..716a734
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/brightness.py
@@ -0,0 +1,37 @@
+# pylint: disable=C0111,R0903
+
+"""Displays the brightness of a display
+
+Requires the following executable:
+ * xbacklight
+
+Parameters:
+ * brightness.step: The amount of increase/decrease on scroll in % (defaults to 2)
+
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.brightness)
+ )
+ self._brightness = 0
+
+ step = self.parameter("step", 2)
+
+ engine.input.register_callback(self, button=bumblebee.input.WHEEL_UP,
+ cmd="xbacklight +{}%".format(step))
+ engine.input.register_callback(self, button=bumblebee.input.WHEEL_DOWN,
+ cmd="xbacklight -{}%".format(step))
+
+ def brightness(self, widget):
+ return "{:03.0f}%".format(self._brightness)
+
+ def update(self, widgets):
+ self._brightness = float(bumblebee.util.execute("xbacklight -get"))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/caffeine.py b/i3/.config/i3/bar/bumblebee/modules/caffeine.py
new file mode 100644
index 0000000..2354ed0
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/caffeine.py
@@ -0,0 +1,48 @@
+# pylint: disable=C0111,R0903
+
+"""Enable/disable automatic screen locking.
+
+Requires the following executables:
+ * xset
+ * notify-send
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.caffeine)
+ )
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd=self._toggle
+ )
+
+ def caffeine(self, widget):
+ return ""
+
+ def state(self, widget):
+ if self._active():
+ return "activated"
+ return "deactivated"
+
+ def _active(self):
+ for line in bumblebee.util.execute("xset q").split("\n"):
+ if "timeout" in line:
+ timeout = int(line.split(" ")[4])
+ if timeout == 0:
+ return True
+ return False
+ return False
+
+ def _toggle(self, widget):
+ if self._active():
+ bumblebee.util.execute("xset s default")
+ bumblebee.util.execute("notify-send \"Out of coffee\"")
+ else:
+ bumblebee.util.execute("xset s off")
+ bumblebee.util.execute("notify-send \"Consuming caffeine\"")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/cmus.py b/i3/.config/i3/bar/bumblebee/modules/cmus.py
new file mode 100644
index 0000000..4bc0e56
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/cmus.py
@@ -0,0 +1,92 @@
+# pylint: disable=C0111,R0903
+# -*- coding: utf-8 -*-
+
+"""Displays information about the current song in cmus.
+
+Requires the following executable:
+ * cmus-remote
+
+Parameters:
+ * cmus.format: Format string for the song information. Tag values can be put in curly brackets (i.e. {artist})
+"""
+
+from collections import defaultdict
+
+import string
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+from bumblebee.output import scrollable
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = [
+ bumblebee.output.Widget(name="cmus.prev"),
+ bumblebee.output.Widget(name="cmus.main", full_text=self.description),
+ bumblebee.output.Widget(name="cmus.next"),
+ bumblebee.output.Widget(name="cmus.shuffle"),
+ bumblebee.output.Widget(name="cmus.repeat"),
+ ]
+ super(Module, self).__init__(engine, config, widgets)
+
+ engine.input.register_callback(widgets[0], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -r")
+ engine.input.register_callback(widgets[1], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -u")
+ engine.input.register_callback(widgets[2], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -n")
+ engine.input.register_callback(widgets[3], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -S")
+ engine.input.register_callback(widgets[4], button=bumblebee.input.LEFT_MOUSE,
+ cmd="cmus-remote -R")
+
+ self._fmt = self.parameter("format", "{artist} - {title} {position}/{duration}")
+ self._status = None
+ self._shuffle = False
+ self._repeat = False
+ self._tags = defaultdict(lambda: '')
+
+ @scrollable
+ def description(self, widget):
+ return string.Formatter().vformat(self._fmt, (), self._tags)
+
+ def update(self, widgets):
+ self._load_song()
+
+ def state(self, widget):
+ if widget.name == "cmus.shuffle":
+ return "shuffle-on" if self._shuffle else "shuffle-off"
+ if widget.name == "cmus.repeat":
+ return "repeat-on" if self._repeat else "repeat-off"
+ if widget.name == "cmus.prev":
+ return "prev"
+ if widget.name == "cmus.next":
+ return "next"
+ return self._status
+
+ def _load_song(self):
+ info = ""
+ try:
+ info = bumblebee.util.execute("cmus-remote -Q")
+ except RuntimeError:
+ pass
+ self._tags = defaultdict(lambda: '')
+ for line in info.split("\n"):
+ if line.startswith("status"):
+ self._status = line.split(" ", 2)[1]
+ if line.startswith("tag"):
+ key, value = line.split(" ", 2)[1:]
+ self._tags.update({ key: value })
+ for key in ["duration", "position"]:
+ if line.startswith(key):
+ dur = int(line.split(" ")[1])
+ self._tags.update({key:bumblebee.util.durationfmt(dur)})
+ if line.startswith("set repeat "):
+ self._repeat = False if "false" in line else True
+ if line.startswith("set shuffle "):
+ self._shuffle = False if "false" in line else True
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/cpu.py b/i3/.config/i3/bar/bumblebee/modules/cpu.py
new file mode 100644
index 0000000..6a24bdc
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/cpu.py
@@ -0,0 +1,37 @@
+# pylint: disable=C0111,R0903
+
+"""Displays CPU utilization across all CPUs.
+
+Parameters:
+ * cpu.warning : Warning threshold in % of CPU usage (defaults to 70%)
+ * cpu.critical: Critical threshold in % of CPU usage (defaults to 80%)
+"""
+
+try:
+ import psutil
+except ImportError:
+ pass
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.utilization)
+ )
+ self._utilization = psutil.cpu_percent(percpu=False)
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="gnome-system-monitor")
+
+ def utilization(self, widget):
+ return "{:06.02f}%".format(self._utilization)
+
+ def update(self, widgets):
+ self._utilization = psutil.cpu_percent(percpu=False)
+
+ def state(self, widget):
+ return self.threshold_state(self._utilization, 70, 80)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/datetime.py b/i3/.config/i3/bar/bumblebee/modules/datetime.py
new file mode 100644
index 0000000..4141a53
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/datetime.py
@@ -0,0 +1,35 @@
+# pylint: disable=C0111,R0903
+
+"""Displays the current date and time.
+
+Parameters:
+ * datetime.format: strftime()-compatible formatting string
+ * date.format : alias for datetime.format
+ * time.format : alias for datetime.format
+"""
+
+from __future__ import absolute_import
+import datetime
+import bumblebee.engine
+
+ALIASES = [ "date", "time" ]
+
+def default_format(module):
+ default = "%x %X"
+ if module == "date":
+ default = "%x"
+ if module == "time":
+ default = "%X"
+ return default
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.get_time)
+ )
+ self._fmt = self.parameter("format", default_format(self.name))
+
+ def get_time(self, widget):
+ return datetime.datetime.now().strftime(self._fmt)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/disk.py b/i3/.config/i3/bar/bumblebee/modules/disk.py
new file mode 100644
index 0000000..1f53bda
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/disk.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0111,R0903
+
+"""Shows free diskspace, total diskspace and the percentage of free disk space.
+
+Parameters:
+ * disk.warning: Warning threshold in % of disk space (defaults to 80%)
+ * disk.critical: Critical threshold in % of disk space (defaults ot 90%)
+ * disk.path: Path to calculate disk usage from (defaults to /)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.diskspace)
+ )
+ self._path = self.parameter("path", "/")
+ self._perc = 0
+ self._used = 0
+ self._size = 0
+
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="termite -e ranger {}".format(self._path))
+
+ def diskspace(self, widget):
+ #return "{} {}/{} ({:05.02f}%)".format(self._path,
+ #bumblebee.util.bytefmt(self._used),
+ #bumblebee.util.bytefmt(self._size), self._perc
+ #)
+ #return "{} {:05.02f}%".format(self._path, self._perc)
+ return "{}".format(bumblebee.util.bytefmt(self._size - self._used))
+
+ def update(self, widgets):
+ st = os.statvfs(self._path)
+ self._size = st.f_frsize*st.f_blocks
+ self._used = self._size - st.f_frsize*st.f_bavail
+ self._perc = 100.0*self._used/self._size
+
+ def state(self, widget):
+ return self.threshold_state(self._perc, 90, 95)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/disk.py_ori b/i3/.config/i3/bar/bumblebee/modules/disk.py_ori
new file mode 100644
index 0000000..aee40ac
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/disk.py_ori
@@ -0,0 +1,45 @@
+# pylint: disable=C0111,R0903
+
+"""Shows free diskspace, total diskspace and the percentage of free disk space.
+
+Parameters:
+ * disk.warning: Warning threshold in % of disk space (defaults to 80%)
+ * disk.critical: Critical threshold in % of disk space (defaults ot 90%)
+ * disk.path: Path to calculate disk usage from (defaults to /)
+"""
+
+import os
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.diskspace)
+ )
+ self._path = self.parameter("path", "/")
+ self._perc = 0
+ self._used = 0
+ self._size = 0
+
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="nautilus {}".format(self._path))
+
+ def diskspace(self, widget):
+ return "{} {}/{} ({:05.02f}%)".format(self._path,
+ bumblebee.util.bytefmt(self._used),
+ bumblebee.util.bytefmt(self._size), self._perc
+ )
+
+ def update(self, widgets):
+ st = os.statvfs(self._path)
+ self._size = st.f_blocks * st.f_frsize
+ self._used = (st.f_blocks - st.f_bfree) * st.f_frsize
+ self._perc = 100.0*self._used/self._size
+
+ def state(self, widget):
+ return self.threshold_state(self._perc, 80, 90)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/dnf.py b/i3/.config/i3/bar/bumblebee/modules/dnf.py
new file mode 100644
index 0000000..f36dfa9
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/dnf.py
@@ -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
diff --git a/i3/.config/i3/bar/bumblebee/modules/error.py b/i3/.config/i3/bar/bumblebee/modules/error.py
new file mode 100644
index 0000000..16d3834
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/error.py
@@ -0,0 +1,26 @@
+# pylint: disable=C0111,R0903
+
+"""Draws an error widget.
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.text)
+ )
+ self._text = ""
+
+ def set(self, text):
+ self._text = text
+
+ def text(self, widget):
+ return self._text
+
+ def state(self, widget):
+ return ["critical"]
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/gpmdp.py b/i3/.config/i3/bar/bumblebee/modules/gpmdp.py
new file mode 100644
index 0000000..36988e8
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/gpmdp.py
@@ -0,0 +1,58 @@
+# pylint: disable=C0111,R0903
+
+"""Displays information about the current song in Google Play music player.
+
+Requires the following executable:
+ * gpmdp-remote
+"""
+
+import string
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = [
+ bumblebee.output.Widget(name="gpmdp.prev"),
+ bumblebee.output.Widget(name="gpmdp.main", full_text=self.description),
+ bumblebee.output.Widget(name="gpmdp.next"),
+ ]
+ super(Module, self).__init__(engine, config, widgets)
+
+ engine.input.register_callback(widgets[0], button=bumblebee.input.LEFT_MOUSE,
+ cmd="playerctl previous")
+ engine.input.register_callback(widgets[1], button=bumblebee.input.LEFT_MOUSE,
+ cmd="playerctl play-pause")
+ engine.input.register_callback(widgets[2], button=bumblebee.input.LEFT_MOUSE,
+ cmd="playerctl next")
+
+ self._status = None
+ self._tags = None
+
+ def description(self, widget):
+ return self._tags if self._tags else "n/a"
+
+ def update(self, widgets):
+ self._load_song()
+
+ def state(self, widget):
+ if widget.name == "gpmdp.prev":
+ return "prev"
+ if widget.name == "gpmdp.next":
+ return "next"
+ return self._status
+
+ def _load_song(self):
+ info = ""
+ try:
+ info = bumblebee.util.execute("gpmdp-remote current")
+ status = bumblebee.util.execute("gpmdp-remote status")
+ except RuntimeError:
+ pass
+ self._status = status.split("\n")[0].lower()
+ self._tags = info.split("\n")[0]
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/kernel.py b/i3/.config/i3/bar/bumblebee/modules/kernel.py
new file mode 100644
index 0000000..761fa8c
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/kernel.py
@@ -0,0 +1,21 @@
+# pylint: disable=C0111,R0903
+
+"""Shows Linux kernel version information"""
+
+import platform
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.output)
+ )
+ self._release_name = platform.release()
+
+ def output(self, widget):
+ return self._release_name
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/layout.py b/i3/.config/i3/bar/bumblebee/modules/layout.py
new file mode 100644
index 0000000..d9daadb
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/layout.py
@@ -0,0 +1,74 @@
+# pylint: disable=C0111,R0903
+
+"""Displays and changes the current keyboard layout
+
+Requires the following executable:
+ * setxkbmap
+
+Parameters:
+ * layout.lang: pipe-separated list of languages to cycle through (e.g. us|rs|de). Default: en
+"""
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.layout)
+ )
+ self._languages = self.parameter("lang", "us").split("|")
+ self._idx = 0
+
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd=self._next_keymap)
+ engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE,
+ cmd=self._prev_keymap)
+
+ def _next_keymap(self, event):
+ self._idx = (self._idx + 1) % len(self._languages)
+ self._set_keymap()
+
+ def _prev_keymap(self, event):
+ self._idx = self._idx - 1 if self._idx > 0 else len(self._languages) - 1
+ self._set_keymap()
+
+ def _set_keymap(self):
+ tmp = self._languages[self._idx].split(":")
+ layout = tmp[0]
+ variant = ""
+ if len(tmp) > 1:
+ variant = "-variant {}".format(tmp[1])
+ try:
+ bumblebee.util.execute("setxkbmap -layout {} {}".format(layout, variant))
+ except RuntimeError:
+ pass
+
+ def layout(self, widget):
+ try:
+ res = bumblebee.util.execute("setxkbmap -query")
+ except RuntimeError:
+ return "n/a"
+ layout = ""
+ variant = None
+ for line in res.split("\n"):
+ if not line:
+ continue
+ if "layout" in line:
+ layout = line.split(":")[1].strip()
+ if "variant" in line:
+ variant = line.split(":")[1].strip()
+ if variant:
+ layout += ":" + variant
+
+ if layout in self._languages:
+ self._idx = self._languages.index(layout)
+ else:
+ self._languages.append(layout)
+ self._idx = len(self._languages) - 1
+
+ return layout
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/load.py b/i3/.config/i3/bar/bumblebee/modules/load.py
new file mode 100644
index 0000000..4d94ee1
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/load.py
@@ -0,0 +1,41 @@
+# pylint: disable=C0111,R0903
+
+"""Displays system load.
+
+Parameters:
+ * load.warning : Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs)
+ * load.critical: Critical threshold for the one-minute load average (defaults to 80% of the number of CPUs)
+"""
+
+import os
+import multiprocessing
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.load)
+ )
+ self._load = [0, 0, 0]
+ try:
+ self._cpus = multiprocessing.cpu_count()
+ except NotImplementedError as e:
+ self._cpus = 1
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="gnome-system-monitor")
+
+ def load(self, widget):
+ return "{:.02f}/{:.02f}/{:.02f}".format(
+ self._load[0], self._load[1], self._load[2]
+ )
+
+ def update(self, widgets):
+ self._load = os.getloadavg()
+
+ def state(self, widget):
+ return self.threshold_state(self._load[0], self._cpus*0.7, self._cpus*0.8)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/memory.py b/i3/.config/i3/bar/bumblebee/modules/memory.py
new file mode 100644
index 0000000..0534f7d
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/memory.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0111,R0903
+
+"""Displays available RAM, total amount of RAM and percentage available.
+
+Parameters:
+ * cpu.warning : Warning threshold in % of memory used (defaults to 80%)
+ * cpu.critical: Critical threshold in % of memory used (defaults to 90%)
+"""
+
+try:
+ import psutil
+except ImportError:
+ pass
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.memory_usage)
+ )
+ self._mem = psutil.virtual_memory()
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE,
+ cmd="gnome-system-monitor")
+
+ def memory_usage(self, widget):
+ used = self._mem.total - self._mem.available
+ return "{}/{} ({:05.02f}%)".format(
+ bumblebee.util.bytefmt(used),
+ bumblebee.util.bytefmt(self._mem.total),
+ self._mem.percent
+ )
+
+ def update(self, widgets):
+ self._mem = psutil.virtual_memory()
+
+ def state(self, widget):
+ if self._mem.percent > float(self.parameter("critical", 90)):
+ return "critical"
+ if self._mem.percent > float(self.parameter("warning", 80)):
+ return "warning"
+ return None
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/nic.py b/i3/.config/i3/bar/bumblebee/modules/nic.py
new file mode 100644
index 0000000..4fd8e55
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/nic.py
@@ -0,0 +1,83 @@
+#pylint: disable=C0111,R0903
+
+"""Displays the name, IP address(es) and status of each available network interface.
+
+Parameters:
+ * nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth")
+"""
+
+import netifaces
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ super(Module, self).__init__(engine, config, widgets)
+ self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
+ self._update_widgets(widgets)
+
+ def update(self, widgets):
+ self._update_widgets(widgets)
+
+ def state(self, widget):
+ states = []
+
+ if widget.get("state") == "down":
+ states.append("critical")
+ elif widget.get("state") != "up":
+ states.append("warning")
+ else:
+ states.append("good")
+
+ intf = widget.get("intf")
+ iftype = "wireless" if self._iswlan(intf) else "wired"
+ iftype = "tunnel" if self._istunnel(intf) else iftype
+
+ states.append("{}-{}".format(iftype, widget.get("state")))
+
+ return states
+
+ def _iswlan(self, intf):
+ # wifi, wlan, wlp, seems to work for me
+ if intf.startswith("w"): return True
+ return False
+
+ def _istunnel(self, intf):
+ return intf.startswith("tun")
+
+ def _update_widgets(self, widgets):
+ interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ]
+
+ for widget in widgets:
+ widget.set("visited", False)
+
+ for intf in interfaces:
+ addr = []
+ state = "down"
+ try:
+ if netifaces.AF_INET in netifaces.ifaddresses(intf):
+ for ip in netifaces.ifaddresses(intf)[netifaces.AF_INET]:
+ if "addr" in ip and ip["addr"] != "":
+ addr.append(ip["addr"])
+ state = "up"
+ except Exception as e:
+ addr = []
+ widget = self.widget(intf)
+ if not widget:
+ widget = bumblebee.output.Widget(name=intf)
+ widgets.append(widget)
+ widget.full_text("{}".format(",".join(addr)))
+ #widget.full_text("{} {} {}".format(intf, state, ", ".join(addr)))
+ widget.set("intf", intf)
+ widget.set("state", state)
+ widget.set("visited", True)
+
+ for widget in widgets:
+ if widget.get("visited") == False:
+ widgets.remove(widget)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/nic.py_ori b/i3/.config/i3/bar/bumblebee/modules/nic.py_ori
new file mode 100644
index 0000000..2ca87a7
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/nic.py_ori
@@ -0,0 +1,88 @@
+#pylint: disable=C0111,R0903
+
+"""Displays the name, IP address(es) and status of each available network interface.
+
+Parameters:
+ * nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth")
+"""
+
+try:
+ import netifaces
+except ImportError:
+ pass
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ super(Module, self).__init__(engine, config, widgets)
+ self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
+ self._update_widgets(widgets)
+
+ def update(self, widgets):
+ self._update_widgets(widgets)
+
+ def state(self, widget):
+ states = []
+
+ if widget.get("state") == "down":
+ states.append("critical")
+ elif widget.get("state") != "up":
+ states.append("warning")
+
+ intf = widget.get("intf")
+ iftype = "wireless" if self._iswlan(intf) else "wired"
+ iftype = "tunnel" if self._istunnel(intf) else iftype
+
+ states.append("{}-{}".format(iftype, widget.get("state")))
+
+ return states
+
+ def _iswlan(self, intf):
+ # wifi, wlan, wlp, seems to work for me
+ if intf.startswith("w"): return True
+ return False
+
+ def _istunnel(self, intf):
+ return intf.startswith("tun")
+
+ def get_addresses(self, intf):
+ retval = []
+ try:
+ for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []):
+ if ip.get("addr", "") != "":
+ retval.append(ip.get("addr"))
+ except Exception:
+ return []
+ return retval
+
+ def _update_widgets(self, widgets):
+ interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ]
+
+ for widget in widgets:
+ widget.set("visited", False)
+
+ for intf in interfaces:
+ addr = []
+ state = "down"
+ for ip in self.get_addresses(intf):
+ addr.append(ip)
+ state = "up"
+ widget = self.widget(intf)
+ if not widget:
+ widget = bumblebee.output.Widget(name=intf)
+ widgets.append(widget)
+ widget.full_text("{} {} {}".format(intf, state, ", ".join(addr)))
+ widget.set("intf", intf)
+ widget.set("state", state)
+ widget.set("visited", True)
+
+ for widget in widgets:
+ if widget.get("visited") == False:
+ widgets.remove(widget)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/pacman.py b/i3/.config/i3/bar/bumblebee/modules/pacman.py
new file mode 100644
index 0000000..a7780a8
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/pacman.py
@@ -0,0 +1,70 @@
+# pylint: disable=C0111,R0903
+
+"""Displays update information per repository for pacman."
+
+Requires the following executables:
+ * fakeroot
+ * pacman
+"""
+
+import os
+import threading
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+#list of repositories.
+#the last one sould always be other
+repos = ["core", "extra", "community", "multilib", "testing", "other"]
+
+def get_pacman_info(widget, path):
+ try:
+ result = bumblebee.util.execute("{}/../../bin/pacman-updates".format(path))
+ except:
+ pass
+
+ count = len(repos)*[0]
+
+ for line in result.splitlines():
+ if line.startswith(("http", "rsync")):
+ for i in range(len(repos)-1):
+ if "/" + repos[i] + "/" in line:
+ count[i] += 1
+ break
+ else:
+ result[-1] += 1
+
+ for i in range(len(repos)):
+ widget.set(repos[i], count[i])
+
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.updates)
+ )
+ self._count = 0
+
+ def updates(self, widget):
+ return '/'.join(map(lambda x: str(widget.get(x,0)), repos))
+
+ def update(self, widgets):
+ path = os.path.dirname(os.path.abspath(__file__))
+ if self._count == 0:
+ thread = threading.Thread(target=get_pacman_info, args=(widgets[0],path))
+ thread.start()
+
+ # TODO: improve this waiting mechanism a bit
+ self._count += 1
+ self._count = 0 if self._count > 300 else self._count
+
+ def state(self, widget):
+ weightedCount = sum(map(lambda x: (len(repos)-x[0]) * widget.get(x[1],0), enumerate(repos)))
+
+ if weightedCount < 10:
+ return "good"
+
+ return self.threshold_state(weightedCount, 100, 150)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/ping.py b/i3/.config/i3/bar/bumblebee/modules/ping.py
new file mode 100644
index 0000000..469871a
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/ping.py
@@ -0,0 +1,77 @@
+# pylint: disable=C0111,R0903
+
+"""Periodically checks the RTT of a configurable host using ICMP echos
+
+Requires the following executable:
+ * ping
+
+Parameters:
+ * ping.interval: Time in seconds between two RTT checks (defaults to 60)
+ * ping.address : IP address to check
+ * ping.timeout : Timeout for waiting for a reply (defaults to 5.0)
+ * ping.probes : Number of probes to send (defaults to 5)
+ * ping.warning : Threshold for warning state, in seconds (defaults to 1.0)
+ * ping.critical: Threshold for critical state, in seconds (defaults to 2.0)
+"""
+
+import re
+import time
+import threading
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+def get_rtt(module, widget):
+ try:
+ widget.set("rtt-unreachable", False)
+ res = bumblebee.util.execute("ping -n -q -c {} -W {} {}".format(
+ widget.get("rtt-probes"), widget.get("rtt-timeout"), widget.get("address")
+ ))
+
+ for line in res.split("\n"):
+ if not line.startswith("rtt"): continue
+ m = re.search(r'([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+)\s+(\S+)', line)
+
+ widget.set("rtt-min", float(m.group(1)))
+ widget.set("rtt-avg", float(m.group(2)))
+ widget.set("rtt-max", float(m.group(3)))
+ widget.set("rtt-unit", m.group(5))
+ except Exception as e:
+ widget.set("rtt-unreachable", True)
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widget = bumblebee.output.Widget(full_text=self.rtt)
+ super(Module, self).__init__(engine, config, widget)
+
+ widget.set("address", self.parameter("address", "8.8.8.8"))
+ widget.set("interval", self.parameter("interval", 60))
+ widget.set("rtt-probes", self.parameter("probes", 5))
+ widget.set("rtt-timeout", self.parameter("timeout", 5.0))
+ widget.set("rtt-avg", 0.0)
+ widget.set("rtt-unit", "")
+
+ self._next_check = 0
+
+ def rtt(self, widget):
+ if widget.get("rtt-unreachable"):
+ return "{}: unreachable".format(widget.get("address"))
+ return "{}: {:.1f}{}".format(
+ widget.get("address"),
+ widget.get("rtt-avg"),
+ widget.get("rtt-unit")
+ )
+
+ def state(self, widget):
+ if widget.get("rtt-unreachable"): return ["critical"]
+ return self.threshold_state(widget.get("rtt-avg"), 1000.0, 2000.0)
+
+ def update(self, widgets):
+ if int(time.time()) < self._next_check:
+ return
+ thread = threading.Thread(target=get_rtt, args=(self,widgets[0],))
+ thread.start()
+ self._next_check = int(time.time()) + int(widgets[0].get("interval"))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/pulseaudio.py b/i3/.config/i3/bar/bumblebee/modules/pulseaudio.py
new file mode 100644
index 0000000..0bf88f4
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/pulseaudio.py
@@ -0,0 +1,107 @@
+# pylint: disable=C0111,R0903
+
+"""Displays volume and mute status of PulseAudio devices.
+
+Aliases: pasink, pasource
+
+Requires the following executable:
+ * pactl
+"""
+
+import re
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+ALIASES = [ "pasink", "pasource" ]
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.volume)
+ )
+
+ self._left = 0
+ self._right = 0
+ self._mono = 0
+ self._mute = False
+ channel = "sink" if self.name == "pasink" else "source"
+
+ self._patterns = [
+ { "expr": "Name:", "callback": (lambda line: False) },
+ { "expr": "Mute:", "callback": (lambda line: self.mute(False if " no" in line.lower() else True)) },
+ { "expr": "Volume:", "callback": self.getvolume },
+ ]
+
+ engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd="pavucontrol")
+
+ events = [
+ { "type": "mute", "action": "toggle", "button": bumblebee.input.LEFT_MOUSE },
+ { "type": "volume", "action": "+2%", "button": bumblebee.input.WHEEL_UP },
+ { "type": "volume", "action": "-2%", "button": bumblebee.input.WHEEL_DOWN },
+ ]
+
+ for event in events:
+ engine.input.register_callback(self, button=event["button"],
+ cmd="pactl set-{}-{} @DEFAULT_{}@ {}".format(channel, event["type"],
+ channel.upper(), event["action"]))
+
+ def mute(self, value):
+ self._mute = value
+
+ def getvolume(self, line):
+ if "mono" in line:
+ m = re.search(r'mono:.*\s*\/\s*(\d+)%', line)
+ if m:
+ self._mono = m.group(1)
+ else:
+ m = re.search(r'left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%', line)
+ if m:
+ self._left = m.group(1)
+ self._right = m.group(2)
+ return True
+
+ def _default_device(self):
+ output = bumblebee.util.execute("pactl info")
+ pattern = "Default Sink: " if self.name == "pasink" else "Default Source: "
+ for line in output.split("\n"):
+ if line.startswith(pattern):
+ return line.replace(pattern, "")
+ return "n/a"
+
+ def volume(self, widget):
+ if int(self._mono) > 0:
+ return "{}%".format(self._mono)
+ elif self._left == self._right:
+ return "{}%".format(self._left)
+ else:
+ return "{}%/{}%".format(self._left, self._right)
+ return "n/a"
+
+ def update(self, widgets):
+ channel = "sinks" if self.name == "pasink" else "sources"
+ device = self._default_device()
+
+ result = bumblebee.util.execute("pactl list {}".format(channel))
+ found = False
+
+ for line in result.split("\n"):
+ if device in line:
+ found = True
+ continue
+ if found == False:
+ continue
+ for pattern in self._patterns:
+ if not pattern["expr"] in line:
+ continue
+ if pattern["callback"](line) == False and found == True:
+ return
+
+ def state(self, widget):
+ if self._mute:
+ return [ "warning", "muted" ]
+ return [ "unmuted" ]
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/redshift.py b/i3/.config/i3/bar/bumblebee/modules/redshift.py
new file mode 100644
index 0000000..2fd1dfb
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/redshift.py
@@ -0,0 +1,46 @@
+# pylint: disable=C0111,R0903
+
+"""Displays the current color temperature of redshift
+
+Requires the following executable:
+ * redshift
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.text)
+ )
+ self._text = ""
+ self._state = "transition"
+
+ def text(self, widget):
+ return "{}".format(self._text)
+
+ def update(self, widgets):
+ result = bumblebee.util.execute("redshift -p")
+
+ temp = ""
+ transition = ""
+ for line in result.split("\n"):
+ if "temperature" in line.lower():
+ temp = line.split(" ")[2]
+ if "period" in line.lower():
+ state = line.split(" ")[1].lower()
+ if "day" in state:
+ self._state = "day"
+ elif "night" in state:
+ self._state = "night"
+ else:
+ self._state = "transition"
+ transition = " ".join(line.split(" ")[2:])
+ self._text = "{} {}".format(temp, transition)
+
+ def state(self, widget):
+ return self._state
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/sensors.py b/i3/.config/i3/bar/bumblebee/modules/sensors.py
new file mode 100644
index 0000000..36dabbd
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/sensors.py
@@ -0,0 +1,45 @@
+"""Displays sensor temperature
+
+Requires the following executable:
+ * sensors
+
+Parameters:
+ * sensors.match: What line in the output of `sensors -u` should be matched against (default: temp1_input)
+ * sensors.match_number: which of the matches you want (default -1: last match).
+"""
+
+import os
+import re
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.temperature)
+ )
+ self._temperature = "unknown"
+ pattern = self.parameter("match", "temp1_input")
+ pattern_string = r"^\s*{}:\s*([\d.]+)$".format(pattern)
+ self._match_number = int(self.parameter("match_number", "-1"))
+ self._pattern = re.compile(pattern_string, re.MULTILINE)
+ engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd="xsensors")
+
+ def get_temp(self):
+ temperatures = bumblebee.util.execute("sensors -u")
+ matching_temp = self._pattern.findall(temperatures)
+ temperature = "unknown"
+ if matching_temp:
+ temperature = int(float(matching_temp[self._match_number]))
+
+ return temperature
+
+ def temperature(self, widget):
+ return self._temperature
+
+ def update(self, widgets):
+ self._temperature = self.get_temp()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/spacer.py b/i3/.config/i3/bar/bumblebee/modules/spacer.py
new file mode 100644
index 0000000..b89c233
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/spacer.py
@@ -0,0 +1,23 @@
+# pylint: disable=C0111,R0903
+
+"""Draws a widget with configurable text content.
+
+Parameters:
+ * spacer.text: Widget contents (defaults to empty string)
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.text)
+ )
+ self._text = self.parameter("text", "")
+
+ def text(self, widget):
+ return self._text
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/stock.py b/i3/.config/i3/bar/bumblebee/modules/stock.py
new file mode 100644
index 0000000..2297c3a
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/stock.py
@@ -0,0 +1,62 @@
+# -*- coding: UTF-8 -*-
+# pylint: disable=C0111,R0903
+
+"""Display a stock quote from yahoo finance.
+
+Requires the following python packages:
+ * requests
+
+Parameters:
+ * stock.symbols : Comma-separated list of symbols to fetch
+ * stock.change : Should we fetch change in stock value (defaults to True)
+ * stock.currencies : List of symbols to go with the values (default $)
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+import requests
+from requests.exceptions import RequestException
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.value)
+ )
+ self._symbols = self.parameter("symbols", "")
+ self._change = self.parameter("change", True)
+ self._currencies = self.parameter('currencies', None)
+ self._baseurl = 'http://download.finance.yahoo.com/d/quotes.csv'
+ self._value = self.fetch()
+
+ if not self._currencies:
+ self._currencies = '$' * len(self._symbols)
+
+ # The currencies could be unicode, like the € symbol. Convert to a unicode object.
+ if hasattr(self._currencies, "decode"):
+ self._currencies = self._currencies.decode("utf-8", "ignore")
+
+ def value(self, widget):
+ results = []
+ for i, val in enumerate(self._value.split('\n')):
+ try:
+ currency_symbol = self._currencies[i]
+ except:
+ currency_symbol = '$'
+ results.append('%s%s' % (currency_symbol, val))
+ return u' '.join(results)
+
+ def fetch(self):
+ if self._symbols:
+ url = self._baseurl
+ url += '?s=%s&f=l1' % self._symbols
+ if self._change:
+ url += 'c1'
+ return requests.get(url).text.strip()
+ else:
+ return ''
+
+ def update(self, widgets):
+ self._value = self.fetch()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/test.py b/i3/.config/i3/bar/bumblebee/modules/test.py
new file mode 100644
index 0000000..e4099c6
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/test.py
@@ -0,0 +1,16 @@
+# pylint: disable=C0111,R0903
+
+"""Test module
+"""
+
+import bumblebee.engine
+
+ALIASES = [ "test-alias" ]
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text="test")
+ )
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/traffic.py b/i3/.config/i3/bar/bumblebee/modules/traffic.py
new file mode 100644
index 0000000..0da9fd1
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/traffic.py
@@ -0,0 +1,72 @@
+# pylint: disable=C0111,R0903
+
+"""Displays network IO for interfaces.
+
+Parameters:
+ * traffic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth")
+"""
+
+import re
+import psutil
+import netifaces
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ super(Module, self).__init__(engine, config, widgets)
+ self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(",")))
+ self._update_widgets(widgets)
+ self._status = ""
+
+ def state(self, widget):
+ if "traffic.rx" in widget.name:
+ return "rx"
+ if "traffic.tx" in widget.name:
+ return "tx"
+ return self._status
+
+ def update(self, widgets):
+ self._update_widgets(widgets)
+
+ def create_widget(self, widgets, name, txt=None, attributes={}):
+ widget = self.widget(name)
+ if widget: return widget
+
+ widget = bumblebee.output.Widget(name=name)
+ widget.full_text(txt)
+ widgets.append(widget)
+
+ for key in attributes:
+ widget.set(key, attributes[key])
+
+ return widget
+
+ def _update_widgets(self, widgets):
+ interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ]
+
+ counters = psutil.net_io_counters(pernic=True)
+ for interface in interfaces:
+ if not interface: interface = "lo"
+ data = {
+ "rx": counters[interface].bytes_recv,
+ "tx": counters[interface].bytes_sent,
+ }
+
+ name = "traffic-{}".format(interface)
+
+ self.create_widget(widgets, name, interface)
+
+ for direction in ["rx", "tx"]:
+ name = "traffic.{}-{}".format(direction, interface)
+ widget = self.create_widget(widgets, name, attributes={"theme.minwidth": "1000.00MB"})
+ prev = widget.get(direction, 0)
+ speed = bumblebee.util.bytefmt(int(data[direction]) - int(prev))
+ widget.full_text(speed)
+ widget.set(direction, data[direction])
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/weather.py b/i3/.config/i3/bar/bumblebee/modules/weather.py
new file mode 100644
index 0000000..683f61d
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/weather.py
@@ -0,0 +1,74 @@
+# -*- coding: UTF-8 -*-
+# pylint: disable=C0111,R0903
+
+"""Displays the temperature on the current location based on the ip
+
+Requires the following python packages:
+ * requests
+
+Parameters:
+ * weather.interval: Interval (in minutes) for updating weather information
+ * weather.location: Set location (ISO 3166 country code), defaults to 'auto' for getting location from http://ipinfo.io
+ * weather.unit: metric (default), kelvin, imperial
+ * weather.apikey: API key from http://api.openweathermap.org
+"""
+
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+import json
+import time
+try:
+ import requests
+ from requests.exceptions import RequestException
+except ImportError:
+ pass
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ super(Module, self).__init__(engine, config,
+ bumblebee.output.Widget(full_text=self.temperature)
+ )
+ self._temperature = 0
+ self._apikey = self.parameter("apikey", "af7bfe22287c652d032a3064ffa44088")
+ self._location = self.parameter("location", "auto")
+ self._interval = int(self.parameter("interval", "15"))
+ self._unit = self.parameter("unit", "metric")
+ self._nextcheck = 0
+ self._valid = False
+
+ def _unit_suffix(self):
+ if self._unit == "metric":
+ return "C"
+ if self._unit == "kelvin":
+ return "K"
+ if self._unit == "imperial":
+ return "F"
+ return ""
+
+ def temperature(self, widget):
+ if not self._valid:
+ return u"?"
+ return u"{}°{}".format(self._temperature, self._unit_suffix())
+
+ def update(self, widgets):
+ timestamp = int(time.time())
+ if self._nextcheck < int(time.time()):
+ try:
+ self._nextcheck = int(time.time()) + self._interval*60
+ weather_url = "http://api.openweathermap.org/data/2.5/weather?appid={}".format(self._apikey)
+ weather_url = "{}&units={}".format(weather_url, self._unit)
+ if self._location == "auto":
+ location_url = "http://ipinfo.io/json"
+ location = json.loads(requests.get(location_url).text)
+ coord = location["loc"].split(",")
+ weather_url = "{url}&lat={lat}&lon={lon}".format(url=weather_url, lat=coord[0], lon=coord[1])
+ else:
+ weather_url = "{url}&q={city}".format(url=weather_url, city=self._location)
+ weather = json.loads(requests.get(weather_url).text)
+ self._temperature = int(weather['main']['temp'])
+ self._valid = True
+ except RequestException:
+ self._valid = False
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/modules/xrandr.py b/i3/.config/i3/bar/bumblebee/modules/xrandr.py
new file mode 100644
index 0000000..a1db224
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/modules/xrandr.py
@@ -0,0 +1,74 @@
+# pylint: disable=C0111,R0903
+
+"""Shows a widget for each connected screen and allows the user to enable/disable screens.
+
+Requires the following executable:
+ * xrandr
+"""
+
+import os
+import re
+import sys
+
+import bumblebee.util
+import bumblebee.input
+import bumblebee.output
+import bumblebee.engine
+
+class Module(bumblebee.engine.Module):
+ def __init__(self, engine, config):
+ widgets = []
+ self._engine = engine
+ super(Module, self).__init__(engine, config, widgets)
+ self.update_widgets(widgets)
+
+ def update_widgets(self, widgets):
+ new_widgets = []
+ for line in bumblebee.util.execute("xrandr -q").split("\n"):
+ if not " connected" in line:
+ continue
+ display = line.split(" ", 2)[0]
+ m = re.search(r'\d+x\d+\+(\d+)\+\d+', line)
+
+ widget = self.widget(display)
+ if not widget:
+ widget = bumblebee.output.Widget(full_text=display, name=display)
+ self._engine.input.register_callback(widget, button=1, cmd=self._toggle)
+ self._engine.input.register_callback(widget, button=3, cmd=self._toggle)
+ new_widgets.append(widget)
+ widget.set("state", "on" if m else "off")
+ widget.set("pos", int(m.group(1)) if m else sys.maxsize)
+
+ while len(widgets) > 0:
+ del widgets[0]
+ for widget in new_widgets:
+ widgets.append(widget)
+
+ def update(self, widgets):
+ self.update_widgets(widgets)
+
+ def state(self, widget):
+ return widget.get("state", "off")
+
+ def _toggle(self, event):
+ path = os.path.dirname(os.path.abspath(__file__))
+ toggle_cmd = "{}/../../bin/toggle-display.sh".format(path)
+
+ widget = self.widget_by_id(event["instance"])
+
+ if widget.get("state") == "on":
+ bumblebee.util.execute("{} --output {} --off".format(toggle_cmd, widget.name))
+ else:
+ first_neighbor = next((widget for widget in self.widgets() if widget.get("state") == "on"), None)
+ last_neighbor = next((widget for widget in reversed(self.widgets()) if widget.get("state") == "on"), None)
+
+ neighbor = first_neighbor if event["button"] == bumblebee.input.LEFT_MOUSE else last_neighbor
+
+ if neighbor == None:
+ bumblebee.util.execute("{} --output {} --auto".format(toggle_cmd, widget.name))
+ else:
+ bumblebee.util.execute("{} --output {} --auto --{}-of {}".format(toggle_cmd, widget.name,
+ "left" if event.get("button") == bumblebee.input.LEFT_MOUSE else "right",
+ neighbor.name))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/output.py b/i3/.config/i3/bar/bumblebee/output.py
new file mode 100644
index 0000000..992eec6
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/output.py
@@ -0,0 +1,140 @@
+# pylint: disable=R0201
+
+"""Output classes"""
+
+import sys
+import json
+import uuid
+
+import bumblebee.store
+
+def scrollable(func):
+ def wrapper(module, widget):
+ text = func(module, widget)
+ width = widget.get("theme.width", module.parameter("width", 30))
+ widget.set("theme.minwidth", "A"*width)
+ if len(text) <= width:
+ return text
+ # we need to shorten
+ start = widget.get("scrolling.start", -1)
+ direction = widget.get("scrolling.direction", "right")
+ start += 1 if direction == "right" else -1
+ widget.set("scrolling.start", start)
+ if width + start >= len(text):
+ widget.set("scrolling.direction", "left")
+ if start <= 0:
+ widget.set("scrolling.direction", "right")
+ text = text[start:width+start]
+
+ return text
+ return wrapper
+
+class Widget(bumblebee.store.Store):
+ """Represents a single visible block in the status bar"""
+ def __init__(self, full_text="", name=""):
+ super(Widget, self).__init__()
+ self._full_text = full_text
+ self.module = None
+ self._module = None
+ self.name = name
+ self.id = str(uuid.uuid4())
+
+ def link_module(self, module):
+ """Set the module that spawned this widget
+
+ This is done outside the constructor to avoid having to
+ pass in the module name in every concrete module implementation"""
+ self.module = module.name
+ self._module = module
+
+ def cls(self):
+ if not self._module:
+ return None
+ return self._module.__module__.replace("bumblebee.modules.", "")
+
+ def state(self):
+ """Return the widget's state"""
+ if self._module and hasattr(self._module, "state"):
+ states = self._module.state(self)
+ if not isinstance(states, list):
+ return [states]
+ return states
+ return []
+
+ def full_text(self, value=None):
+ """Set or retrieve the full text to display in the widget"""
+ if value:
+ self._full_text = value
+ else:
+ if callable(self._full_text):
+ return self._full_text(self)
+ else:
+ return self._full_text
+
+class I3BarOutput(object):
+ """Manage output according to the i3bar protocol"""
+ def __init__(self, theme):
+ self._theme = theme
+ self._widgets = []
+ self._started = False
+
+ def started(self):
+ return self._started
+
+ def start(self):
+ """Print start preamble for i3bar protocol"""
+ self._started = True
+ sys.stdout.write(json.dumps({"version": 1, "click_events": True}) + "\n[\n")
+
+ def stop(self):
+ """Finish i3bar protocol"""
+ sys.stdout.write("]\n")
+
+ def draw(self, widget, module=None, engine=None):
+ """Draw a single widget"""
+ full_text = widget.full_text()
+ padding = self._theme.padding(widget)
+ prefix = self._theme.prefix(widget, padding)
+ suffix = self._theme.suffix(widget, padding)
+ minwidth = self._theme.minwidth(widget)
+ if prefix:
+ full_text = u"{}{}".format(prefix, full_text)
+ if suffix:
+ full_text = u"{}{}".format(full_text, suffix)
+ separator = self._theme.separator(widget)
+ if separator:
+ self._widgets.append({
+ u"full_text": separator,
+ "separator": False,
+ "color": self._theme.separator_fg(widget),
+ "background": self._theme.separator_bg(widget),
+ "separator_block_width": self._theme.separator_block_width(widget),
+ })
+ width = self._theme.minwidth(widget)
+ self._widgets.append({
+ u"full_text": full_text,
+ "color": self._theme.fg(widget),
+ "background": self._theme.bg(widget),
+ "separator_block_width": self._theme.separator_block_width(widget),
+ "separator": True if separator is None else False,
+ "min_width": width + "A"*(len(prefix) + len(suffix)) if width else None,
+ "align": self._theme.align(widget),
+ "instance": widget.id,
+ "name": module.id,
+ })
+
+ def begin(self):
+ """Start one output iteration"""
+ self._widgets = []
+ self._theme.reset()
+
+ def flush(self):
+ """Flushes output"""
+ sys.stdout.write(json.dumps(self._widgets))
+
+ def end(self):
+ """Finalizes output"""
+ sys.stdout.write(",\n")
+ sys.stdout.flush()
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/store.py b/i3/.config/i3/bar/bumblebee/store.py
new file mode 100644
index 0000000..8fac71b
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/store.py
@@ -0,0 +1,21 @@
+"""Store interface
+
+Allows arbitrary classes to offer a simple get/set
+store interface by deriving from the Store class in
+this module
+"""
+
+class Store(object):
+ """Interface for storing and retrieving simple values"""
+ def __init__(self):
+ self._data = {}
+
+ def set(self, key, value):
+ """Set 'key' to 'value', overwriting 'key' if it exists already"""
+ self._data[key] = value
+
+ def get(self, key, default=None):
+ """Return the current value of 'key', or 'default' if 'key' is not set"""
+ return self._data.get(key, default)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/theme.py b/i3/.config/i3/bar/bumblebee/theme.py
new file mode 100644
index 0000000..e16fbb6
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/theme.py
@@ -0,0 +1,180 @@
+# pylint: disable=C0103
+
+"""Theme support"""
+
+import os
+import glob
+import copy
+import json
+import io
+
+import bumblebee.error
+
+def theme_path():
+ """Return the path of the theme directory"""
+ return os.path.dirname("{}/../themes/".format(os.path.dirname(os.path.realpath(__file__))))
+
+def themes():
+ result = []
+
+ for filename in glob.iglob("{}/*.json".format(theme_path())):
+ if "test" not in filename:
+ result.append(os.path.basename(filename).replace(".json", ""))
+ return result
+
+class Theme(object):
+ """Represents a collection of icons and colors"""
+ def __init__(self, name):
+ self._init(self.load(name))
+ self._widget = None
+ self._cycle_idx = 0
+ self._cycle = {}
+ self._prevbg = None
+
+ def _init(self, data):
+ """Initialize theme from data structure"""
+ for iconset in data.get("icons", []):
+ self._merge(data, self._load_icons(iconset))
+ self._theme = data
+ self._defaults = data.get("defaults", {})
+ self._cycles = self._theme.get("cycle", [])
+ self.reset()
+
+ def data(self):
+ """Return the raw theme data"""
+ return self._theme
+
+ def reset(self):
+ """Reset theme to initial state"""
+ self._cycle = self._cycles[0] if len(self._cycles) > 0 else {}
+ self._cycle_idx = 0
+ self._widget = None
+ self._prevbg = None
+
+ def padding(self, widget):
+ """Return padding for widget"""
+ return self._get(widget, "padding", "")
+
+ def prefix(self, widget, default=None):
+ """Return the theme prefix for a widget's full text"""
+ padding = self.padding(widget)
+ pre = self._get(widget, "prefix", None)
+ return u"{}{}{}".format(padding, pre, padding) if pre else default
+
+ def suffix(self, widget, default=None):
+ """Return the theme suffix for a widget's full text"""
+ padding = self._get(widget, "padding", "")
+ suf = self._get(widget, "suffix", None)
+ return u"{}{}{}".format(padding, suf, padding) if suf else default
+
+ def fg(self, widget):
+ """Return the foreground color for this widget"""
+ return self._get(widget, "fg", None)
+
+ def bg(self, widget):
+ """Return the background color for this widget"""
+ return self._get(widget, "bg", None)
+
+ def align(self, widget):
+ """Return the widget alignment"""
+ return self._get(widget, "align", None)
+
+ def minwidth(self, widget):
+ """Return the minimum width string for this widget"""
+ return self._get(widget, "minwidth", "")
+
+ def separator(self, widget):
+ """Return the separator between widgets"""
+ return self._get(widget, "separator", None)
+
+ def separator_fg(self, widget):
+ """Return the separator's foreground/text color"""
+ return self.bg(widget)
+
+ def separator_bg(self, widget):
+ """Return the separator's background color"""
+ return self._prevbg
+
+ def separator_block_width(self, widget):
+ """Return the SBW"""
+ return self._get(widget, "separator-block-width", None)
+
+ def _load_icons(self, name):
+ """Load icons for a theme"""
+ path = "{}/icons/".format(theme_path())
+ return self.load(name, path=path)
+
+ def load(self, name, path=theme_path()):
+ """Load and parse a theme file"""
+ themefile = "{}/{}.json".format(path, name)
+
+ if os.path.isfile(themefile):
+ try:
+ with io.open(themefile,encoding="utf-8") as data:
+ return json.load(data)
+ except ValueError as exception:
+ raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception))
+ else:
+ raise bumblebee.error.ThemeLoadError("no such theme: {}".format(name))
+
+ def _get(self, widget, name, default=None):
+ """Return the config value 'name' for 'widget'"""
+
+ if not self._widget:
+ self._widget = widget
+
+ if self._widget != widget:
+ self._prevbg = self.bg(self._widget)
+ self._widget = widget
+ if len(self._cycles) > 0:
+ self._cycle_idx = (self._cycle_idx + 1) % len(self._cycles)
+ self._cycle = self._cycles[self._cycle_idx]
+
+ module_theme = self._theme.get(widget.module, {})
+ class_theme = self._theme.get(widget.cls(), {})
+
+ state_themes = []
+ # avoid infinite recursion
+ states = widget.state()
+ if name not in states:
+ for state in states:
+ state_themes.append(self._get(widget, state, {}))
+
+ value = self._defaults.get(name, default)
+ value = widget.get("theme.{}".format(name), value)
+ value = self._cycle.get(name, value)
+ value = class_theme.get(name, value)
+ value = module_theme.get(name, value)
+
+ for theme in state_themes:
+ value = theme.get(name, value)
+
+ if isinstance(value, list):
+ key = "{}-idx".format(name)
+ idx = widget.get(key, 0)
+ widget.set(key, (idx + 1) % len(value))
+ value = value[idx]
+
+ return value
+
+ # algorithm copied from
+ # http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts
+ # nicely done :)
+ def _merge(self, target, *args):
+ """Merge two arbitrarily nested data structures"""
+ if len(args) > 1:
+ for item in args:
+ self._merge(item)
+ return target
+
+ item = args[0]
+ if not isinstance(item, dict):
+ return item
+ for key, value in item.items():
+ if key in target and isinstance(target[key], dict):
+ self._merge(target[key], value)
+ else:
+ target[key] = copy.deepcopy(value)
+ return target
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/bumblebee/util.py b/i3/.config/i3/bar/bumblebee/util.py
new file mode 100644
index 0000000..9efa146
--- /dev/null
+++ b/i3/.config/i3/bar/bumblebee/util.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 -*-
+
+import shlex
+import logging
+import subprocess
+
+try:
+ from exceptions import RuntimeError
+except ImportError:
+ # Python3 doesn't require this anymore
+ pass
+
+def execute(cmd, wait=True):
+ logging.info("executing command '{}'".format(cmd))
+ args = shlex.split(cmd)
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ rv = None
+
+ if wait:
+ out, _ = proc.communicate()
+ if proc.returncode != 0:
+ raise RuntimeError("{} exited with {}".format(cmd, proc.returncode))
+
+ if hasattr(out, "decode"):
+ rv = out.decode("utf-8")
+ else:
+ rv = out
+
+ logging.info(u"command returned '{}'".format("" if not rv else rv))
+ return rv
+
+def bytefmt(num):
+ for unit in [ "", "Ki", "Mi", "Gi" ]:
+ if num < 1024.0:
+ return "{:.2f}{}B".format(num, unit)
+ num /= 1024.0
+ return "{:.2f}GiB".format(num*1024.0)
+
+def durationfmt(duration):
+ minutes, seconds = divmod(duration, 60)
+ hours, minutes = divmod(minutes, 60)
+ res = "{:02d}:{:02d}".format(minutes, seconds)
+ if hours > 0: res = "{:02d}:{}".format(hours, res)
+
+ return res
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/runlint.sh b/i3/.config/i3/bar/runlint.sh
new file mode 100644
index 0000000..6902ce9
--- /dev/null
+++ b/i3/.config/i3/bar/runlint.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+find . -name "*.py"|xargs pylint --disable=R0903,R0201,C0330
diff --git a/i3/.config/i3/bar/runtests.sh b/i3/.config/i3/bar/runtests.sh
new file mode 100644
index 0000000..60f058e
--- /dev/null
+++ b/i3/.config/i3/bar/runtests.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+echo "testing with $(python2 -V 2>&1)"
+python2 $(which nosetests) --rednose -v --with-coverage --cover-erase tests/
+
+if [ $? == 0 ]; then
+ echo
+
+ echo "testing with $(python3 -V 2>&1)"
+ python3 $(which nosetests-3) --rednose -v --with-coverage --cover-erase tests/
+fi
diff --git a/i3/.config/i3/bar/screenshots/amixer.png b/i3/.config/i3/bar/screenshots/amixer.png
new file mode 100644
index 0000000..0da57c5
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/amixer.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/battery.png b/i3/.config/i3/bar/screenshots/battery.png
new file mode 100644
index 0000000..bdbafee
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/battery.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/brightness.png b/i3/.config/i3/bar/screenshots/brightness.png
new file mode 100644
index 0000000..298abf3
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/brightness.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/caffeine.png b/i3/.config/i3/bar/screenshots/caffeine.png
new file mode 100644
index 0000000..5444777
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/caffeine.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/cmus.png b/i3/.config/i3/bar/screenshots/cmus.png
new file mode 100644
index 0000000..52c0b51
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/cmus.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/cpu.png b/i3/.config/i3/bar/screenshots/cpu.png
new file mode 100644
index 0000000..0b5527c
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/cpu.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/date.png b/i3/.config/i3/bar/screenshots/date.png
new file mode 100644
index 0000000..36621e7
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/date.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/datetime.png b/i3/.config/i3/bar/screenshots/datetime.png
new file mode 100644
index 0000000..0c15843
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/datetime.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/disk.png b/i3/.config/i3/bar/screenshots/disk.png
new file mode 100644
index 0000000..7a659b8
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/disk.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/dnf.png b/i3/.config/i3/bar/screenshots/dnf.png
new file mode 100644
index 0000000..4992416
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/dnf.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/kernel.png b/i3/.config/i3/bar/screenshots/kernel.png
new file mode 100644
index 0000000..9d20cdc
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/kernel.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/layout.png b/i3/.config/i3/bar/screenshots/layout.png
new file mode 100644
index 0000000..d09250e
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/layout.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/load.png b/i3/.config/i3/bar/screenshots/load.png
new file mode 100644
index 0000000..e136e7e
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/load.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/memory.png b/i3/.config/i3/bar/screenshots/memory.png
new file mode 100644
index 0000000..db0c59b
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/memory.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/nic.png b/i3/.config/i3/bar/screenshots/nic.png
new file mode 100644
index 0000000..d1512c0
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/nic.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/pacman.png b/i3/.config/i3/bar/screenshots/pacman.png
new file mode 100644
index 0000000..8f4169e
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/pacman.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/pasink.png b/i3/.config/i3/bar/screenshots/pasink.png
new file mode 100644
index 0000000..2fd6359
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/pasink.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/pasource.png b/i3/.config/i3/bar/screenshots/pasource.png
new file mode 100644
index 0000000..ae2ee77
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/pasource.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/ping.png b/i3/.config/i3/bar/screenshots/ping.png
new file mode 100644
index 0000000..1c8bf2b
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/ping.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/pulseaudio.png b/i3/.config/i3/bar/screenshots/pulseaudio.png
new file mode 100644
index 0000000..b38a4aa
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/pulseaudio.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/redshift.png b/i3/.config/i3/bar/screenshots/redshift.png
new file mode 100644
index 0000000..999328f
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/redshift.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/sensors.png b/i3/.config/i3/bar/screenshots/sensors.png
new file mode 100644
index 0000000..69d2554
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/sensors.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/spacer.png b/i3/.config/i3/bar/screenshots/spacer.png
new file mode 100644
index 0000000..5200d0f
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/spacer.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/stock.png b/i3/.config/i3/bar/screenshots/stock.png
new file mode 100644
index 0000000..427f7cf
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/stock.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/themes/default.png b/i3/.config/i3/bar/screenshots/themes/default.png
new file mode 100644
index 0000000..c2680bd
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/themes/default.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/themes/gruvbox.png b/i3/.config/i3/bar/screenshots/themes/gruvbox.png
new file mode 100644
index 0000000..2f2c385
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/themes/gruvbox.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/themes/powerline-gruvbox.png b/i3/.config/i3/bar/screenshots/themes/powerline-gruvbox.png
new file mode 100644
index 0000000..5b80ff3
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/themes/powerline-gruvbox.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/themes/powerline-solarized.png b/i3/.config/i3/bar/screenshots/themes/powerline-solarized.png
new file mode 100644
index 0000000..5be0b16
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/themes/powerline-solarized.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/themes/powerline.png b/i3/.config/i3/bar/screenshots/themes/powerline.png
new file mode 100644
index 0000000..1bd551a
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/themes/powerline.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/themes/solarized.png b/i3/.config/i3/bar/screenshots/themes/solarized.png
new file mode 100644
index 0000000..a15d08c
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/themes/solarized.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/time.png b/i3/.config/i3/bar/screenshots/time.png
new file mode 100644
index 0000000..39d5210
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/time.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/traffic.png b/i3/.config/i3/bar/screenshots/traffic.png
new file mode 100644
index 0000000..bcb27da
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/traffic.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/weather.png b/i3/.config/i3/bar/screenshots/weather.png
new file mode 100644
index 0000000..1efee83
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/weather.png
Binary files differ
diff --git a/i3/.config/i3/bar/screenshots/xrandr.png b/i3/.config/i3/bar/screenshots/xrandr.png
new file mode 100644
index 0000000..ec1c6de
--- /dev/null
+++ b/i3/.config/i3/bar/screenshots/xrandr.png
Binary files differ
diff --git a/i3/.config/i3/bar/testjson.sh b/i3/.config/i3/bar/testjson.sh
new file mode 100644
index 0000000..d4df9f9
--- /dev/null
+++ b/i3/.config/i3/bar/testjson.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+find themes/ -name "*.json"|grep -v invalid|xargs cat|json_verify -s
diff --git a/i3/.config/i3/bar/tests/__init__.py b/i3/.config/i3/bar/tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/i3/.config/i3/bar/tests/__init__.py
diff --git a/i3/.config/i3/bar/tests/mocks.py b/i3/.config/i3/bar/tests/mocks.py
new file mode 100644
index 0000000..f5be753
--- /dev/null
+++ b/i3/.config/i3/bar/tests/mocks.py
@@ -0,0 +1,141 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import json
+import shlex
+import random
+import subprocess
+
+from bumblebee.input import I3BarInput
+from bumblebee.output import Widget
+from bumblebee.config import Config
+
+def rand(cnt):
+ return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for i in range(cnt))
+
+def setup_test(test, Module):
+ test._stdin, test._select, test.stdin, test.select = epoll_mock("bumblebee.input")
+
+ test.popen = MockPopen()
+
+ test.config = Config()
+ test.input = I3BarInput()
+ test.engine = mock.Mock()
+ test.engine.input = test.input
+ test.input.need_event = True
+ test.module = Module(engine=test.engine, config={ "config": test.config })
+ for widget in test.module.widgets():
+ widget.link_module(test.module)
+ test.anyWidget = widget
+
+def teardown_test(test):
+ test._stdin.stop()
+ test._select.stop()
+ test.popen.cleanup()
+
+def epoll_mock(module=""):
+ if len(module) > 0: module = "{}.".format(module)
+
+ stdin = mock.patch("{}sys.stdin".format(module))
+ select = mock.patch("{}select".format(module))
+ epoll = mock.Mock()
+
+ stdin_mock = stdin.start()
+ select_mock = select.start()
+
+ stdin_mock.fileno.return_value = 1
+ select_mock.epoll.return_value = epoll
+ epoll.poll.return_value = [(stdin_mock.fileno.return_value, 100)]
+
+ return stdin, select, stdin_mock, select_mock
+
+def mouseEvent(stdin, button, inp, module=None, instance=None):
+ stdin.readline.return_value = json.dumps({
+ "name": module.id if module else rand(10),
+ "button": button,
+ "instance": instance
+ })
+ inp.start()
+ inp.stop()
+ stdin.readline.assert_any_call()
+
+class MockPopen(object):
+ def __init__(self, module=""):
+ if len(module) > 0: module = "{}.".format(module)
+ self._patch = mock.patch("{}subprocess.Popen".format(module))
+ self._popen = self._patch.start()
+ self.mock = mock.Mock()
+ # for a nicer, more uniform interface
+ self.mock.popen = self._popen
+ # for easier command execution checks
+ self.mock.popen.assert_call = self.assert_call
+ self._popen.return_value = self.mock
+
+ self.mock.communicate.return_value = [ "", None ]
+ self.mock.returncode = 0
+
+ def assert_call(self, cmd):
+ self.mock.popen.assert_any_call(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+ def cleanup(self):
+ self._patch.stop()
+
+class MockInput(object):
+ def __init__(self):
+ self._callbacks = {}
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def get_callback(self, uid):
+ return self._callbacks.get(uid, None)
+
+ def register_callback(self, obj, button, cmd):
+ if not obj:
+ return
+ self._callbacks[obj.id] = {
+ "button": button,
+ "command": cmd,
+ }
+
+class MockOutput(object):
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def draw(self, widget, engine, module):
+ engine.stop()
+
+ def begin(self):
+ pass
+
+ def flush(self):
+ pass
+
+ def end(self):
+ pass
+
+class MockEngine(object):
+ def __init__(self):
+ self.input = MockInput()
+
+class MockWidget(Widget):
+ def __init__(self, text):
+ super(MockWidget, self).__init__(text)
+ self.module = None
+ self.attr_state = ["state-default"]
+ self.id = rand(10)
+
+ self.full_text(text)
+
+# def state(self):
+# return self.attr_state
+
+ def update(self, widgets):
+ pass
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/__init__.py b/i3/.config/i3/bar/tests/modules/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/__init__.py
diff --git a/i3/.config/i3/bar/tests/modules/test_battery.py b/i3/.config/i3/bar/tests/modules/test_battery.py
new file mode 100644
index 0000000..0c86cc9
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_battery.py
@@ -0,0 +1,108 @@
+# pylint: disable=C0103,C0111
+
+import sys
+import mock
+import unittest
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.modules.battery import Module
+from bumblebee.config import Config
+
+class TestBatteryModule(unittest.TestCase):
+ def setUp(self):
+ self._stdout = mock.patch("sys.stdout", new_callable=StringIO)
+ self._exists = mock.patch("bumblebee.modules.battery.os.path.exists")
+ self._open = mock.patch("bumblebee.modules.battery.open", create=True)
+
+ self.stdout = self._stdout.start()
+ self.exists = self._exists.start()
+ self.open = self._open.start()
+ self.file = mock.Mock()
+ self.file.__enter__ = lambda x: self.file
+ self.file.__exit__ = lambda x, a, b, c: ""
+ self.open.return_value = self.file
+
+ self.exists.return_value = True
+ self.engine = mock.Mock()
+ self.config = Config()
+ self.module = Module(engine=self.engine, config={"config":self.config})
+
+ self.config.set("battery.critical", "20")
+ self.config.set("battery.warning", "25")
+ self.criticalValue = "19"
+ self.warningValue = "21"
+ self.normalValue = "26"
+ self.chargedValue = "96"
+
+ for widget in self.module.widgets():
+ widget.link_module(self.module)
+ self.anyWidget = widget
+
+ def tearDown(self):
+ self._stdout.stop()
+ self._exists.stop()
+ self._open.stop()
+
+ def test_format(self):
+ for widget in self.module.widgets():
+ self.assertEquals(len(widget.full_text()), len("100%"))
+
+ def test_critical(self):
+ self.file.read.return_value = self.criticalValue
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+ def test_warning(self):
+ self.file.read.return_value = self.warningValue
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_normal(self):
+ self.file.read.return_value = self.normalValue
+ self.module.update_all()
+ self.assertTrue(not "warning" in self.module.state(self.anyWidget))
+ self.assertTrue(not "critical" in self.module.state(self.anyWidget))
+
+ def test_overload(self):
+ self.file.read.return_value = "120"
+ self.module.update_all()
+ self.assertTrue(not "warning" in self.module.state(self.anyWidget))
+ self.assertTrue(not "critical" in self.module.state(self.anyWidget))
+ self.assertEquals(self.module.capacity(self.anyWidget), "100%")
+
+ def test_ac(self):
+ self.exists.return_value = False
+ self.module.update_all()
+ self.assertEquals(self.module.capacity(self.anyWidget), "ac")
+ self.assertTrue("AC" in self.module.state(self.anyWidget))
+
+ def test_error(self):
+ self.file.read.side_effect = IOError("failed to read")
+ self.module.update_all()
+ self.assertEquals(self.module.capacity(self.anyWidget), "n/a")
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+ self.assertTrue("unknown" in self.module.state(self.anyWidget))
+
+ def test_charging(self):
+ self.file.read.return_value = self.chargedValue
+ self.module.update_all()
+ self.assertTrue("charged" in self.module.state(self.anyWidget))
+ self.file.read.return_value = self.normalValue
+ self.module.update_all()
+ self.assertTrue("charging" in self.module.state(self.anyWidget))
+
+ def test_discharging(self):
+ for limit in [ 10, 25, 50, 80, 100 ]:
+ value = limit - 1
+ self.file.read.return_value = str(value)
+ self.module.update_all()
+ self.file.read.return_value = "Discharging"
+ self.assertTrue("discharging-{}".format(limit) in self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_brightness.py b/i3/.config/i3/bar/tests/modules/test_brightness.py
new file mode 100644
index 0000000..b636d44
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_brightness.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.input import WHEEL_UP, WHEEL_DOWN
+from bumblebee.modules.brightness import Module
+
+class TestBrightnessModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_format(self):
+ for widget in self.module.widgets():
+ self.assertEquals(len(widget.full_text()), len("100%"))
+
+ def test_wheel_up(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module)
+ self.popen.assert_call("xbacklight +2%")
+
+ def test_wheel_down(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module)
+ self.popen.assert_call("xbacklight -2%")
+
+ def test_custom_step(self):
+ self.config.set("brightness.step", "10")
+ module = Module(engine=self.engine, config={"config": self.config})
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=module)
+ self.popen.assert_call("xbacklight -10%")
+
+ def test_update(self):
+ self.popen.mock.communicate.return_value = ("20.0", None)
+ self.module.update_all()
+ self.assertEquals(self.module.brightness(self.anyWidget), "020%")
+
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_caffeine.py b/i3/.config/i3/bar/tests/modules/test_caffeine.py
new file mode 100644
index 0000000..e95e2ac
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_caffeine.py
@@ -0,0 +1,53 @@
+# pylint: disable=C0103,C0111
+
+import json
+import unittest
+import mock
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.config import Config
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.caffeine import Module
+
+class TestCaffeineModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ self.xset_active = " timeout: 0 cycle: 123"
+ self.xset_inactive = " timeout: 600 cycle: 123"
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_text(self):
+ self.assertEquals(self.module.caffeine(self.anyWidget), "")
+
+ def test_active(self):
+ self.popen.mock.communicate.return_value = (self.xset_active, None)
+ self.assertTrue(not "deactivated" in self.module.state(self.anyWidget))
+ self.assertTrue("activated" in self.module.state(self.anyWidget))
+
+ def test_inactive(self):
+ self.popen.mock.communicate.return_value = (self.xset_inactive, None)
+ self.assertTrue("deactivated" in self.module.state(self.anyWidget))
+ self.popen.mock.communicate.return_value = ("no text", None)
+ self.assertTrue("deactivated" in self.module.state(self.anyWidget))
+
+ def test_toggle(self):
+ self.popen.mock.communicate.return_value = (self.xset_active, None)
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("xset s default")
+ self.popen.assert_call("notify-send \"Out of coffee\"")
+
+ self.popen.mock.communicate.return_value = (self.xset_inactive, None)
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("xset s off")
+ self.popen.assert_call("notify-send \"Consuming caffeine\"")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_cmus.py b/i3/.config/i3/bar/tests/modules/test_cmus.py
new file mode 100644
index 0000000..ce1f77c
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_cmus.py
@@ -0,0 +1,114 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.cmus import Module
+
+class TestCmusModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ self.songTemplate = """
+status {status}
+file /path/to/file
+duration {duration}
+position {position}
+tag title {title}
+tag artist {artist}
+tag album {album}
+tag tracknumber 1
+tag date 1984
+tag comment comment
+ """
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_read_song(self):
+ self.popen.mock.communicate.return_value = ("song", None)
+ self.module.update_all()
+ self.popen.assert_call("cmus-remote -Q")
+
+ def test_handle_runtimeerror(self):
+ self.popen.mock.communicate.side_effect = RuntimeError("error loading song")
+ self.module.update_all()
+ self.assertEquals(self.module.description(self.anyWidget), " - /")
+
+ def test_format(self):
+ self.popen.mock.communicate.return_value = (self.songTemplate.format(
+ artist="an artist", title="a title", duration="100", position="20",
+ album="an album", status="irrelevant"
+ ), None)
+ self.module.update_all()
+ self.anyWidget.set("theme.width", 1000)
+ self.assertEquals(self.module.description(self.anyWidget),
+ "an artist - a title 00:20/01:40"
+ )
+
+ def test_scrollable_format(self):
+ self.popen.mock.communicate.return_value = (self.songTemplate.format(
+ artist="an artist", title="a title", duration="100", position="20",
+ album="an album", status="irrelevant"
+ ), None)
+ self.module.update_all()
+ self.anyWidget.set("theme.width", 10)
+ self.assertEquals(self.module.description(self.anyWidget),
+ "an artist - a title 00:20/01:40"[:10]
+ )
+
+ def test_repeat(self):
+ self.popen.mock.communicate.return_value = ("set repeat false", None)
+ self.module.update_all()
+ self.assertTrue("repeat-off" in self.module.state(self.module.widget("cmus.repeat")))
+ self.popen.mock.communicate.return_value = ("set repeat true", None)
+ self.module.update_all()
+ self.assertTrue("repeat-on" in self.module.state(self.module.widget("cmus.repeat")))
+
+ def test_shuffle(self):
+ self.popen.mock.communicate.return_value = ("set shuffle false", None)
+ self.module.update_all()
+ self.assertTrue("shuffle-off" in self.module.state(self.module.widget("cmus.shuffle")))
+ self.popen.mock.communicate.return_value = ("set shuffle true", None)
+ self.module.update_all()
+ self.assertTrue("shuffle-on" in self.module.state(self.module.widget("cmus.shuffle")))
+
+ def test_prevnext(self):
+ self.assertTrue("prev" in self.module.state(self.module.widget("cmus.prev")))
+ self.assertTrue("next" in self.module.state(self.module.widget("cmus.next")))
+
+ def test_main(self):
+ self.popen.mock.communicate.return_value = ("status paused", None)
+ self.module.update_all()
+ self.assertTrue("paused" in self.module.state(self.module.widget("cmus.main")))
+
+ self.popen.mock.communicate.return_value = ("status playing", None)
+ self.module.update_all()
+ self.assertTrue("playing" in self.module.state(self.module.widget("cmus.main")))
+
+ self.popen.mock.communicate.return_value = ("status stopped", None)
+ self.module.update_all()
+ self.assertTrue("stopped" in self.module.state(self.module.widget("cmus.main")))
+
+ def test_widget(self):
+ self.assertEquals(len(self.module.widgets()), 5)
+
+ for idx, val in enumerate(["prev", "main", "next", "shuffle", "repeat"]):
+ self.assertEquals(self.module.widgets()[idx].name, "cmus.{}".format(val))
+
+ def test_interaction(self):
+ events = [
+ {"widget": "cmus.shuffle", "action": "cmus-remote -S"},
+ {"widget": "cmus.repeat", "action": "cmus-remote -R"},
+ {"widget": "cmus.next", "action": "cmus-remote -n"},
+ {"widget": "cmus.prev", "action": "cmus-remote -r"},
+ {"widget": "cmus.main", "action": "cmus-remote -u"},
+ ]
+ for event in events:
+ mocks.mouseEvent(stdin=self.stdin, inp=self.input, module=self.module, instance=self.module.widget(event["widget"]).id, button=LEFT_MOUSE)
+ self.popen.assert_call(event["action"])
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_cpu.py b/i3/.config/i3/bar/tests/modules/test_cpu.py
new file mode 100644
index 0000000..cd5ce89
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_cpu.py
@@ -0,0 +1,45 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.cpu import Module
+
+class TestCPUModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+ self._psutil = mock.patch("bumblebee.modules.cpu.psutil")
+ self.psutil = self._psutil.start()
+
+ def tearDown(self):
+ self._psutil.stop()
+ mocks.teardown_test(self)
+
+ def test_format(self):
+ self.psutil.cpu_percent.return_value = 21.0
+ self.module.update_all()
+ for widget in self.module.widgets():
+ self.assertEquals(len(widget.full_text()), len("100.00%"))
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("gnome-system-monitor")
+
+ def test_warning(self):
+ self.config.set("cpu.critical", "20")
+ self.config.set("cpu.warning", "18")
+ self.psutil.cpu_percent.return_value = 19.0
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("cpu.critical", "20")
+ self.config.set("cpu.warning", "19")
+ self.psutil.cpu_percent.return_value = 21.0
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_disk.py b/i3/.config/i3/bar/tests/modules/test_disk.py
new file mode 100644
index 0000000..16f31c4
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_disk.py
@@ -0,0 +1,47 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.disk import Module
+
+class MockVFS(object):
+ def __init__(self, perc):
+ self.f_blocks = 1024*1024
+ self.f_frsize = 1
+ self.f_bfree = self.f_blocks*(1.0 - perc/100.0)
+
+class TestDiskModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+ self._os = mock.patch("bumblebee.modules.disk.os")
+ self.os = self._os.start()
+ self.config.set("disk.path", "somepath")
+
+ def tearDown(self):
+ self._os.stop()
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ module = Module(engine=self.engine, config={"config":self.config})
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=module)
+ self.popen.assert_call("nautilus {}".format(self.module.parameter("path")))
+
+ def test_warning(self):
+ self.config.set("disk.critical", "80")
+ self.config.set("disk.warning", "70")
+ self.os.statvfs.return_value = MockVFS(75.0)
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("disk.critical", "80")
+ self.config.set("disk.warning", "70")
+ self.os.statvfs.return_value = MockVFS(85.0)
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_load.py b/i3/.config/i3/bar/tests/modules/test_load.py
new file mode 100644
index 0000000..e57bc2b
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_load.py
@@ -0,0 +1,57 @@
+# pylint: disable=C0103,C0111
+
+import json
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.load import Module
+
+class TestLoadModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ self._mp = mock.patch("bumblebee.modules.load.multiprocessing")
+ self._os = mock.patch("bumblebee.modules.load.os")
+
+ self.mp = self._mp.start()
+ self.os = self._os.start()
+
+ self.mp.cpu_count.return_value = 1
+
+ def tearDown(self):
+ self._mp.stop()
+ self._os.stop()
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("gnome-system-monitor")
+
+ def test_load_format(self):
+ self.os.getloadavg.return_value = [ 5.9, 1.2, 0.8 ]
+ self.module.update_all()
+ self.assertEquals(self.module.load(self.anyWidget), "5.90/1.20/0.80")
+
+ def test_warning(self):
+ self.config.set("load.critical", "1")
+ self.config.set("load.warning", "0.8")
+ self.os.getloadavg.return_value = [ 0.9, 0, 0 ]
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("load.critical", "1")
+ self.config.set("load.warning", "0.8")
+ self.os.getloadavg.return_value = [ 1.1, 0, 0 ]
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+ def test_assume_single_core(self):
+ self.mp.cpu_count.side_effect = NotImplementedError
+ module = Module(engine=self.engine, config={"config": mock.Mock() })
+ self.assertEquals(1, module._cpus)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_memory.py b/i3/.config/i3/bar/tests/modules/test_memory.py
new file mode 100644
index 0000000..94c61f9
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_memory.py
@@ -0,0 +1,52 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE
+from bumblebee.modules.memory import Module
+
+class VirtualMemory(object):
+ def __init__(self, percent):
+ self.percent = percent
+
+class TestMemoryModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+ self._psutil = mock.patch("bumblebee.modules.memory.psutil")
+ self.psutil = self._psutil.start()
+
+ def tearDown(self):
+ self._psutil.stop()
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("gnome-system-monitor")
+
+ def test_warning(self):
+ self.config.set("memory.critical", "80")
+ self.config.set("memory.warning", "70")
+ self.psutil.virtual_memory.return_value = VirtualMemory(75)
+ self.module.update_all()
+ self.assertTrue("warning" in self.module.state(self.anyWidget))
+
+ def test_critical(self):
+ self.config.set("memory.critical", "80")
+ self.config.set("memory.warning", "70")
+ self.psutil.virtual_memory.return_value = VirtualMemory(81)
+ self.module.update_all()
+ self.assertTrue("critical" in self.module.state(self.anyWidget))
+
+ def test_usage(self):
+ rv = VirtualMemory(50)
+ rv.total = 1000
+ rv.available = 500
+ self.psutil.virtual_memory.return_value = rv
+ self.module.update_all()
+ self.assertEquals("500.00B/1000.00B (50.00%)", self.module.memory_usage(self.anyWidget))
+ self.assertEquals(None, self.module.state(self.anyWidget))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/modules/test_pulseaudio.py b/i3/.config/i3/bar/tests/modules/test_pulseaudio.py
new file mode 100644
index 0000000..f8754c6
--- /dev/null
+++ b/i3/.config/i3/bar/tests/modules/test_pulseaudio.py
@@ -0,0 +1,34 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import LEFT_MOUSE, RIGHT_MOUSE, WHEEL_UP, WHEEL_DOWN
+from bumblebee.modules.pulseaudio import Module
+
+class TestPulseAudioModule(unittest.TestCase):
+ def setUp(self):
+ mocks.setup_test(self, Module)
+
+ def tearDown(self):
+ mocks.teardown_test(self)
+
+ def test_leftclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("pactl set-source-mute @DEFAULT_SOURCE@ toggle")
+
+ def test_rightclick(self):
+ mocks.mouseEvent(stdin=self.stdin, button=RIGHT_MOUSE, inp=self.input, module=self.module)
+ self.popen.assert_call("pavucontrol")
+
+ def test_wheelup(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module)
+ self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ +2%")
+
+ def test_wheeldown(self):
+ mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module)
+ self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ -2%")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_config.py b/i3/.config/i3/bar/tests/test_config.py
new file mode 100644
index 0000000..395dcb7
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_config.py
@@ -0,0 +1,77 @@
+# pylint: disable=C0103,C0111
+
+import unittest
+import mock
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+from bumblebee.config import Config
+from bumblebee.theme import themes
+from bumblebee.engine import all_modules
+
+class TestConfig(unittest.TestCase):
+ def setUp(self):
+ self._stdout = mock.patch("bumblebee.config.sys.stdout", new_callable=StringIO)
+ self._stderr = mock.patch("bumblebee.config.sys.stderr", new_callable=StringIO)
+
+ self.stdout = self._stdout.start()
+ self.stderr = self._stderr.start()
+
+ self.defaultConfig = Config()
+ self.someSimpleModules = ["foo", "bar", "baz"]
+ self.someAliasModules = ["foo:a", "bar:b", "baz:c"]
+ self.someTheme = "some-theme"
+
+ def tearDown(self):
+ self._stdout.stop()
+ self._stderr.stop()
+
+ def test_no_modules_by_default(self):
+ self.assertEquals(self.defaultConfig.modules(), [])
+
+ def test_simple_modules(self):
+ cfg = Config(["-m"] + self.someSimpleModules)
+ self.assertEquals(cfg.modules(), [{
+ "name": x, "module": x
+ } for x in self.someSimpleModules])
+
+ def test_alias_modules(self):
+ cfg = Config(["-m"] + self.someAliasModules)
+ self.assertEquals(cfg.modules(), [{
+ "module": x.split(":")[0],
+ "name": x.split(":")[1],
+ } for x in self.someAliasModules])
+
+ def test_parameters(self):
+ cfg = Config(["-m", "module", "-p", "module.key=value"])
+ self.assertEquals(cfg.get("module.key"), "value")
+
+ def test_theme(self):
+ cfg = Config(["-t", self.someTheme])
+ self.assertEquals(cfg.theme(), self.someTheme)
+
+ def test_notheme(self):
+ self.assertEquals(self.defaultConfig.theme(), "default")
+
+ def test_list_themes(self):
+ with self.assertRaises(SystemExit):
+ cfg = Config(["-l", "themes"])
+ result = self.stdout.getvalue()
+ for theme in themes():
+ self.assertTrue(theme in result)
+
+ def test_list_modules(self):
+ with self.assertRaises(SystemExit):
+ cfg = Config(["-l", "modules"])
+ result = self.stdout.getvalue()
+ for module in all_modules():
+ self.assertTrue(module["name"] in result)
+
+ def test_invalid_list(self):
+ with self.assertRaises(SystemExit):
+ cfg = Config(["-l", "invalid"])
+ self.assertTrue("invalid choice" in "".join(self.stderr.getvalue()))
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_engine.py b/i3/.config/i3/bar/tests/test_engine.py
new file mode 100644
index 0000000..946f604
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_engine.py
@@ -0,0 +1,90 @@
+# pylint: disable=C0103,C0111,W0703,W0212
+
+import shlex
+import unittest
+
+from bumblebee.error import ModuleLoadError
+from bumblebee.engine import Engine
+from bumblebee.config import Config
+import bumblebee.input
+
+from tests.mocks import MockOutput, MockInput
+
+class TestEngine(unittest.TestCase):
+ def setUp(self):
+ self.engine = Engine(config=Config(), output=MockOutput(), inp=MockInput())
+ self.testModule = "test"
+ self.testAlias = "test-alias"
+ self.singleWidgetModule = [{"module": self.testModule, "name": "a"}]
+ self.singleWidgetAlias = [{"module": self.testAlias, "name": "a" }]
+ self.invalidModule = "no-such-module"
+ self.testModuleSpec = "bumblebee.modules.{}".format(self.testModule)
+ self.testModules = [
+ {"module": "test", "name": "a"},
+ {"module": "test", "name": "b"},
+ ]
+
+ def test_stop(self):
+ self.assertTrue(self.engine.running())
+ self.engine.stop()
+ self.assertFalse(self.engine.running())
+
+ def test_load_module(self):
+ module = self.engine._load_module(self.testModule)
+ self.assertEquals(module.__module__, self.testModuleSpec)
+
+ def test_load_invalid_module(self):
+ with self.assertRaises(ModuleLoadError):
+ self.engine._load_module(self.invalidModule)
+
+ def test_load_none(self):
+ with self.assertRaises(ModuleLoadError):
+ self.engine._load_module(None)
+
+ def test_load_modules(self):
+ modules = self.engine.load_modules(self.testModules)
+ self.assertEquals(len(modules), len(self.testModules))
+ self.assertEquals(
+ [module.__module__ for module in modules],
+ [self.testModuleSpec for module in modules]
+ )
+
+ def test_run(self):
+ self.engine.load_modules(self.singleWidgetModule)
+ try:
+ self.engine.run()
+ except Exception as e:
+ self.fail(e)
+
+ def test_aliases(self):
+ modules = self.engine.load_modules(self.singleWidgetAlias)
+ self.assertEquals(len(modules), 1)
+ self.assertEquals(modules[0].__module__, self.testModuleSpec)
+
+ def test_custom_cmd(self):
+ testmodules = [
+ { "name": "test", "button": "test.left-click", "action": "echo" },
+ { "name": "test:alias", "button": "alias.right-click", "action": "echo2" },
+ ]
+ cmd = "-m"
+ for test in testmodules:
+ cmd += " " + test["name"]
+ cmd += " -p"
+ for test in testmodules:
+ cmd += " " + test["button"] + "=" + test["action"]
+ cfg = Config(shlex.split(cmd))
+ inp = MockInput()
+ engine = Engine(config=cfg, output=MockOutput(), inp=inp)
+
+ i = 0
+ for module in engine.modules():
+ callback = inp.get_callback(module.id)
+ self.assertTrue(callback is not None)
+ self.assertEquals(callback["command"], testmodules[i]["action"])
+ if "left" in testmodules[i]["button"]:
+ self.assertTrue(callback["button"], bumblebee.input.LEFT_MOUSE)
+ if "right" in testmodules[i]["button"]:
+ self.assertTrue(callback["button"], bumblebee.input.RIGHT_MOUSE)
+ i += 1
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_i3barinput.py b/i3/.config/i3/bar/tests/test_i3barinput.py
new file mode 100644
index 0000000..6852f97
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_i3barinput.py
@@ -0,0 +1,110 @@
+# pylint: disable=C0103,C0111
+
+import json
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.input import I3BarInput, LEFT_MOUSE, RIGHT_MOUSE
+
+class TestI3BarInput(unittest.TestCase):
+ def setUp(self):
+ self.input = I3BarInput()
+ self.input.need_event = True
+
+ self._stdin = mock.patch("bumblebee.input.sys.stdin")
+ self.stdin = self._stdin.start()
+ self._select = mock.patch("bumblebee.input.select")
+ self.select = self._select.start()
+ self.popen = mocks.MockPopen()
+
+ self.stdin.fileno.return_value = 1
+ epoll = mock.Mock()
+ self.select.epoll.return_value = epoll
+
+ epoll.poll.return_value = [(self.stdin.fileno.return_value, 2)]
+
+ self.anyModule = mock.Mock()
+ self.anyModule.id = mocks.rand(10)
+ self.anotherModule = mock.Mock()
+ self.anotherModule.id = mocks.rand(10)
+ self.anyWidget = mocks.MockWidget("some-widget")
+ self.anotherWidget = mocks.MockWidget("another-widget")
+ self.anyData = self.invalidData = "any data"
+ self.invalidEvent = json.dumps({"name": None, "instance": None, "button": 1})
+ self.incompleteEvent = json.dumps({"button": 1})
+ self.anyCommand = "this is a command with arguments"
+
+ self._called = 0
+
+ def tearDown(self):
+ self._stdin.stop()
+ self._select.stop()
+ self.popen.cleanup()
+
+ def callback(self, event):
+ self._called += 1
+
+ def calls(self):
+ rv = self._called
+ self._called = 0
+ return rv
+
+ def test_read_event(self):
+ self.stdin.readline.return_value = self.anyData
+ self.input.start()
+ self.input.stop()
+ self.stdin.readline.assert_any_call()
+
+ def test_ignore_invalid_input(self):
+ for data in [ self.invalidData, self.incompleteEvent, self.invalidEvent ]:
+ self.stdin.readline.return_value = data
+ self.input.start()
+ self.assertEquals(self.input.alive(), True)
+ self.assertEquals(self.input.stop(), True)
+ self.stdin.readline.assert_any_call()
+
+ def test_global_callback(self):
+ self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin)
+ self.assertTrue(self.calls() > 0)
+
+ def test_remove_global_callback(self):
+ self.test_global_callback()
+ self.input.deregister_callbacks(None)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin)
+ self.assertTrue(self.calls() == 0)
+
+ def test_global_callback_wrong_button(self):
+ self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=RIGHT_MOUSE, inp=self.input, stdin=self.stdin)
+ self.assertTrue(self.calls() == 0)
+
+ def test_module_callback(self):
+ self.input.register_callback(self.anyModule, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule)
+ self.assertTrue(self.calls() > 0)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherModule)
+ self.assertTrue(self.calls() == 0)
+
+ def test_remove_module_callback(self):
+ self.test_module_callback()
+ self.input.deregister_callbacks(self.anyModule)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule)
+ self.assertTrue(self.calls() == 0)
+
+ def test_widget_callback(self):
+ self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.callback)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget)
+ self.assertTrue(self.calls() > 0)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherWidget)
+ self.assertTrue(self.calls() == 0)
+
+ def test_widget_cmd_callback(self):
+ self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.anyCommand)
+ mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget)
+ self.popen.assert_call(self.anyCommand)
+
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_i3baroutput.py b/i3/.config/i3/bar/tests/test_i3baroutput.py
new file mode 100644
index 0000000..8c566ef
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_i3baroutput.py
@@ -0,0 +1,133 @@
+# pylint: disable=C0103,C0111
+
+import json
+import mock
+import unittest
+
+try:
+ from StringIO import StringIO
+except ImportError:
+ from io import StringIO
+
+import tests.mocks as mocks
+
+from bumblebee.output import I3BarOutput
+
+class TestI3BarOutput(unittest.TestCase):
+ def setUp(self):
+ self.theme = mock.Mock()
+ self.theme.separator_fg.return_value = "#123456"
+ self.theme.separator_bg.return_value = "#000000"
+ self.theme.separator.return_value = ""
+ self.theme.prefix.return_value = ""
+ self.theme.suffix.return_value = ""
+ self.theme.separator_block_width.return_value = 1
+ self.theme.fg.return_value = "#ababab"
+ self.theme.bg.return_value = "#ababab"
+ self.theme.align.return_value = None
+ self.theme.minwidth.return_value = ""
+ self.output = I3BarOutput(self.theme)
+
+ self._stdout = mock.patch("bumblebee.output.sys.stdout", new_callable=StringIO)
+ self.stdout = self._stdout.start()
+
+ self.anyWidget = mocks.MockWidget("some text")
+ self.anyModule = mock.Mock()
+ self.anyModule.id = mocks.rand(10)
+ self.anyModule.name = mocks.rand(10)
+
+ self.expectedStart = json.dumps({"version": 1, "click_events": True}) + "\n[\n"
+ self.expectedStop = "]\n"
+
+ self.anyColor = "#ffffff"
+ self.anotherColor = "#cdcdcd"
+
+ def tearDown(self):
+ self._stdout.stop()
+
+ def test_start(self):
+ self.output.start()
+ self.assertEquals(self.expectedStart, self.stdout.getvalue())
+
+ def test_stop(self):
+ self.output.stop()
+ self.assertEquals(self.expectedStop, self.stdout.getvalue())
+
+ def test_draw_single_widget(self):
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], self.anyWidget.full_text())
+
+ def test_draw_multiple_widgets(self):
+ for i in range(4):
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())
+ for res in result:
+ self.assertEquals(res["full_text"], self.anyWidget.full_text())
+
+ def test_begin(self):
+ self.output.begin()
+ self.assertEquals("", self.stdout.getvalue())
+
+ def test_end(self):
+ self.output.end()
+ self.assertEquals(",\n", self.stdout.getvalue())
+
+ def test_prefix(self):
+ self.theme.prefix.return_value = " - "
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], " - {}".format(self.anyWidget.full_text()))
+
+ def test_suffix(self):
+ self.theme.suffix.return_value = " - "
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], "{} - ".format(self.anyWidget.full_text()))
+
+ def test_bothfix(self):
+ self.theme.prefix.return_value = "*"
+ self.theme.suffix.return_value = " - "
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["full_text"], "*{} - ".format(self.anyWidget.full_text()))
+
+ def test_colors(self):
+ self.theme.fg.return_value = self.anyColor
+ self.theme.bg.return_value = self.anotherColor
+ self.output.draw(self.anyWidget, self.anyModule)
+ self.output.flush()
+ result = json.loads(self.stdout.getvalue())[0]
+ self.assertEquals(result["color"], self.anyColor)
+ self.assertEquals(result["background"], self.anotherColor)
+
+ def test_widget_link(self):
+ self.anyWidget.link_module(self.anyModule)
+ self.assertEquals(self.anyWidget._module, self.anyModule)
+ self.assertEquals(self.anyWidget.module, self.anyModule.name)
+
+ def test_unlinked_widget_state(self):
+ state = self.anyWidget.state()
+ self.assertTrue(type(state) == list)
+
+ def test_linked_widget_state(self):
+ self.anyWidget.link_module(self.anyModule)
+ for lst in [ "samplestate", ["a", "b", "c"], [] ]:
+ self.anyModule.state.return_value = lst
+ state = self.anyWidget.state()
+ self.assertEquals(type(state), list)
+ if type(lst) is not list: lst = [lst]
+ self.assertEquals(state, lst)
+
+ def test_widget_fulltext(self):
+ self.anyWidget.full_text("some text")
+ self.assertEquals(self.anyWidget.full_text(), "some text")
+ self.anyWidget.full_text(lambda x: "callable fulltext")
+ self.assertEquals(self.anyWidget.full_text(), "callable fulltext")
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_module.py b/i3/.config/i3/bar/tests/test_module.py
new file mode 100644
index 0000000..cf663dc
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_module.py
@@ -0,0 +1,89 @@
+# pylint: disable=C0103,C0111,W0703
+
+import unittest
+
+from bumblebee.engine import Module
+from bumblebee.config import Config
+from tests.mocks import MockWidget
+
+class TestModule(unittest.TestCase):
+ def setUp(self):
+ self.widgetName = "foo"
+ self.widget = MockWidget(self.widgetName)
+ self.config = Config()
+ self.anyWidgetName = "random-widget-name"
+ self.noSuchModule = "this-module-does-not-exist"
+ self.moduleWithoutWidgets = Module(engine=None, widgets=None)
+ self.moduleWithOneWidget = Module(engine=None, widgets=self.widget, config={"config": self.config})
+ self.moduleWithMultipleWidgets = Module(engine=None,
+ widgets=[self.widget, self.widget, self.widget]
+ )
+
+ self.anyConfigName = "cfg"
+ self.anotherConfigName = "cfg2"
+ self.anyModule = Module(engine=None, widgets=self.widget, config={
+ "name": self.anyConfigName, "config": self.config
+ })
+ self.anotherModule = Module(engine=None, widgets=self.widget, config={
+ "name": self.anotherConfigName, "config": self.config
+ })
+ self.anyKey = "some-parameter"
+ self.anyValue = "value"
+ self.anotherValue = "another-value"
+ self.emptyKey = "i-do-not-exist"
+ self.config.set("{}.{}".format(self.anyConfigName, self.anyKey), self.anyValue)
+ self.config.set("{}.{}".format(self.anotherConfigName, self.anyKey), self.anotherValue)
+
+ def test_empty_widgets(self):
+ self.assertEquals(self.moduleWithoutWidgets.widgets(), [])
+
+ def test_single_widget(self):
+ self.assertEquals(self.moduleWithOneWidget.widgets(), [self.widget])
+
+ def test_multiple_widgets(self):
+ for widget in self.moduleWithMultipleWidgets.widgets():
+ self.assertEquals(widget, self.widget)
+
+ def test_retrieve_widget_by_name(self):
+ widget = MockWidget(self.anyWidgetName)
+ widget.name = self.anyWidgetName
+ module = Module(engine=None, widgets=[self.widget, widget, self.widget])
+ retrievedWidget = module.widget(self.anyWidgetName)
+ self.assertEquals(retrievedWidget, widget)
+
+ def test_retrieve_widget_by_id(self):
+ widget = MockWidget(self.anyWidgetName)
+ widget.id = self.anyWidgetName
+ module = Module(engine=None, widgets=[self.widget, widget, self.widget])
+ retrievedWidget = module.widget_by_id(self.anyWidgetName)
+ self.assertEquals(retrievedWidget, widget)
+
+ def test_retrieve_missing_widget(self):
+ module = self.moduleWithMultipleWidgets
+
+ widget = module.widget(self.noSuchModule)
+ self.assertEquals(widget, None)
+
+ widget = module.widget_by_id(self.noSuchModule)
+ self.assertEquals(widget, None)
+
+ def test_threshold(self):
+ module = self.moduleWithOneWidget
+ module.name = self.widgetName
+
+ self.config.set("{}.critical".format(self.widgetName), 10.0)
+ self.config.set("{}.warning".format(self.widgetName), 8.0)
+ self.assertEquals("critical", module.threshold_state(10.1, 0, 0))
+ self.assertEquals("warning", module.threshold_state(10.0, 0, 0))
+ self.assertEquals(None, module.threshold_state(8.0, 0, 0))
+ self.assertEquals(None, module.threshold_state(7.9, 0, 0))
+
+ def test_parameters(self):
+ self.assertEquals(self.anyModule.parameter(self.anyKey), self.anyValue)
+ self.assertEquals(self.anotherModule.parameter(self.anyKey), self.anotherValue)
+
+ def test_default_parameters(self):
+ self.assertEquals(self.anyModule.parameter(self.emptyKey), None)
+ self.assertEquals(self.anyModule.parameter(self.emptyKey, self.anyValue), self.anyValue)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_modules.py b/i3/.config/i3/bar/tests/test_modules.py
new file mode 100644
index 0000000..4b280a3
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_modules.py
@@ -0,0 +1,58 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+import importlib
+
+import tests.mocks as mocks
+
+from bumblebee.engine import all_modules
+from bumblebee.output import Widget
+from bumblebee.config import Config
+
+class TestGenericModules(unittest.TestCase):
+ def setUp(self):
+ engine = mock.Mock()
+ engine.input = mock.Mock()
+ config = Config()
+ self.objects = {}
+
+ self.popen = mocks.MockPopen()
+ self.popen.mock.communicate.return_value = (str.encode("1"), "error")
+ self.popen.mock.returncode = 0
+
+ self._platform = mock.patch("bumblebee.modules.kernel.platform")
+ self.platform = self._platform.start()
+ self.platform.release.return_value = "unknown linux v1"
+
+ for mod in all_modules():
+ name = "bumblebee.modules.{}".format(mod["name"])
+ cls = importlib.import_module(name)
+ self.objects[mod["name"]] = getattr(cls, "Module")(engine, {"config": config})
+ for widget in self.objects[mod["name"]].widgets():
+ self.assertEquals(widget.get("variable", None), None)
+
+ def tearDown(self):
+ self._platform.stop()
+ self.popen.cleanup()
+
+ def test_widgets(self):
+ for mod in self.objects:
+ widgets = self.objects[mod].widgets()
+ for widget in widgets:
+ widget.link_module(self.objects[mod])
+ self.assertEquals(widget.module, mod)
+ self.assertTrue(isinstance(widget, Widget))
+ self.assertTrue(hasattr(widget, "full_text"))
+ widget.set("variable", "value")
+ self.assertEquals(widget.get("variable", None), "value")
+ self.assertTrue(isinstance(widget.full_text(), str) or isinstance(widget.full_text(), unicode))
+
+ def test_update(self):
+ for mod in self.objects:
+ widgets = self.objects[mod].widgets()
+ self.objects[mod].update(widgets)
+ self.test_widgets()
+ self.assertEquals(widgets, self.objects[mod].widgets())
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_store.py b/i3/.config/i3/bar/tests/test_store.py
new file mode 100644
index 0000000..faa1efb
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_store.py
@@ -0,0 +1,26 @@
+# pylint: disable=C0103,C0111,W0703
+
+import unittest
+
+from bumblebee.store import Store
+
+class TestStore(unittest.TestCase):
+ def setUp(self):
+ self.store = Store()
+ self.anyKey = "some-key"
+ self.anyValue = "some-value"
+ self.unsetKey = "invalid-key"
+
+ def test_set_value(self):
+ self.store.set(self.anyKey, self.anyValue)
+ self.assertEquals(self.store.get(self.anyKey), self.anyValue)
+
+ def test_get_invalid_value(self):
+ result = self.store.get(self.unsetKey)
+ self.assertEquals(result, None)
+
+ def test_get_invalid_with_default_value(self):
+ result = self.store.get(self.unsetKey, self.anyValue)
+ self.assertEquals(result, self.anyValue)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_theme.py b/i3/.config/i3/bar/tests/test_theme.py
new file mode 100644
index 0000000..747d749
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_theme.py
@@ -0,0 +1,134 @@
+# pylint: disable=C0103,C0111,W0703
+
+import mock
+import unittest
+from bumblebee.theme import Theme
+from bumblebee.error import ThemeLoadError
+from tests.mocks import MockWidget
+
+class TestTheme(unittest.TestCase):
+ def setUp(self):
+ self.nonexistentThemeName = "no-such-theme"
+ self.invalidThemeName = "test_invalid"
+ self.validThemeName = "test"
+ self.validThemeSeparator = " * "
+ self.themedWidget = MockWidget("bla")
+ self.theme = Theme(self.validThemeName)
+ self.cycleTheme = Theme("test_cycle")
+ self.anyModule = mock.Mock()
+ self.anyWidget = MockWidget("bla")
+ self.anotherWidget = MockWidget("blub")
+
+ self.anyModule.state.return_value = "state-default"
+
+ self.anyWidget.link_module(self.anyModule)
+ self.themedWidget.link_module(self.anyModule)
+
+ data = self.theme.data()
+ self.widgetTheme = "test-widget"
+ self.themedWidget.module = self.widgetTheme
+ self.defaultColor = data["defaults"]["fg"]
+ self.defaultBgColor = data["defaults"]["bg"]
+ self.widgetColor = data[self.widgetTheme]["fg"]
+ self.widgetBgColor = data[self.widgetTheme]["bg"]
+ self.defaultPrefix = data["defaults"]["prefix"]
+ self.defaultSuffix = data["defaults"]["suffix"]
+ self.widgetPrefix = data[self.widgetTheme]["prefix"]
+ self.widgetSuffix = data[self.widgetTheme]["suffix"]
+
+ def test_load_valid_theme(self):
+ try:
+ Theme(self.validThemeName)
+ except Exception as e:
+ self.fail(e)
+
+ def test_load_nonexistent_theme(self):
+ with self.assertRaises(ThemeLoadError):
+ Theme(self.nonexistentThemeName)
+
+ def test_load_invalid_theme(self):
+ with self.assertRaises(ThemeLoadError):
+ Theme(self.invalidThemeName)
+
+ def test_default_prefix(self):
+ self.assertEquals(self.theme.prefix(self.anyWidget), self.defaultPrefix)
+
+ def test_default_suffix(self):
+ self.assertEquals(self.theme.suffix(self.anyWidget), self.defaultSuffix)
+
+ def test_widget_prefix(self):
+ self.assertEquals(self.theme.prefix(self.themedWidget), self.widgetPrefix)
+
+ def test_widget_fg(self):
+ self.assertEquals(self.theme.fg(self.anyWidget), self.defaultColor)
+ self.anyWidget.module = self.widgetTheme
+ self.assertEquals(self.theme.fg(self.anyWidget), self.widgetColor)
+
+ def test_widget_bg(self):
+ self.assertEquals(self.theme.bg(self.anyWidget), self.defaultBgColor)
+ self.anyWidget.module = self.widgetTheme
+ self.assertEquals(self.theme.bg(self.anyWidget), self.widgetBgColor)
+
+ def test_absent_cycle(self):
+ theme = self.theme
+ try:
+ theme.fg(self.anyWidget)
+ theme.fg(self.anotherWidget)
+ except Exception as e:
+ self.fail(e)
+
+ def test_reset(self):
+ theme = self.cycleTheme
+ data = theme.data()
+ theme.reset()
+ self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"])
+ self.assertEquals(theme.fg(self.anotherWidget), data["cycle"][1]["fg"])
+ theme.reset()
+ self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"])
+
+ def test_separator_block_width(self):
+ theme = self.theme
+ data = theme.data()
+
+ self.assertEquals(theme.separator_block_width(self.anyWidget),
+ data["defaults"]["separator-block-width"]
+ )
+
+ def test_separator(self):
+ for theme in [self.theme, self.cycleTheme]:
+ theme.reset()
+ prev_bg = theme.bg(self.anyWidget)
+ theme.bg(self.anotherWidget)
+
+ self.assertEquals(theme.separator_fg(self.anotherWidget), theme.bg(self.anotherWidget))
+ self.assertEquals(theme.separator_bg(self.anotherWidget), prev_bg)
+
+ def test_state(self):
+ theme = self.theme
+ data = theme.data()
+
+ self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["fg"])
+ self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["bg"])
+
+ self.anyModule.state.return_value = "critical"
+ self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["critical"]["fg"])
+ self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["critical"]["bg"])
+ self.assertEquals(theme.fg(self.themedWidget), data[self.widgetTheme]["critical"]["fg"])
+ # if elements are missing in the state theme, they are taken from the
+ # widget theme instead (i.e. no fallback to a more general state theme)
+ self.assertEquals(theme.bg(self.themedWidget), data[self.widgetTheme]["bg"])
+
+ def test_separator(self):
+ self.assertEquals(self.validThemeSeparator, self.theme.separator(self.anyWidget))
+
+ def test_list(self):
+ theme = self.theme
+ data = theme.data()[self.widgetTheme]["cycle-test"]["fg"]
+ self.anyModule.state.return_value = "cycle-test"
+ self.assertTrue(len(data) > 1)
+
+ for idx in range(0, len(data)):
+ self.assertEquals(theme.fg(self.themedWidget), data[idx])
+ self.assertEquals(theme.fg(self.themedWidget), data[0])
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/tests/test_util.py b/i3/.config/i3/bar/tests/test_util.py
new file mode 100644
index 0000000..1988756
--- /dev/null
+++ b/i3/.config/i3/bar/tests/test_util.py
@@ -0,0 +1,56 @@
+# pylint: disable=C0103,C0111
+
+import mock
+import unittest
+
+import tests.mocks as mocks
+
+from bumblebee.util import *
+
+class TestUtil(unittest.TestCase):
+ def setUp(self):
+ self.popen = mocks.MockPopen("bumblebee.util")
+ self.some_command_with_args = "sample-command -a -b -c"
+ self.some_utf8 = "some string".encode("utf-8")
+
+ def tearDown(self):
+ self.popen.cleanup()
+
+ def test_bytefmt(self):
+ self.assertEquals(bytefmt(10), "10.00B")
+ self.assertEquals(bytefmt(15*1024), "15.00KiB")
+ self.assertEquals(bytefmt(20*1024*1024), "20.00MiB")
+ self.assertEquals(bytefmt(22*1024*1024*1024), "22.00GiB")
+ self.assertEquals(bytefmt(35*1024*1024*1024*1024), "35840.00GiB")
+
+ def test_durationfmt(self):
+ self.assertEquals(durationfmt(00), "00:00")
+ self.assertEquals(durationfmt(25), "00:25")
+ self.assertEquals(durationfmt(60), "01:00")
+ self.assertEquals(durationfmt(119), "01:59")
+ self.assertEquals(durationfmt(3600), "01:00:00")
+ self.assertEquals(durationfmt(7265), "02:01:05")
+
+ def test_execute(self):
+ execute(self.some_command_with_args)
+ self.assertTrue(self.popen.mock.popen.called)
+ self.popen.mock.popen.assert_call(self.some_command_with_args)
+ self.assertTrue(self.popen.mock.communicate.called)
+
+ def test_execute_nowait(self):
+ execute(self.some_command_with_args, False)
+ self.assertTrue(self.popen.mock.popen.called)
+ self.popen.mock.popen.assert_call(self.some_command_with_args)
+ self.assertFalse(self.popen.mock.communicate.called)
+
+ def test_execute_utf8(self):
+ self.popen.mock.communicate.return_value = [ self.some_utf8, None ]
+ self.test_execute()
+
+ def test_execute_error(self):
+ self.popen.mock.returncode = 1
+
+ with self.assertRaises(RuntimeError):
+ execute(self.some_command_with_args)
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
diff --git a/i3/.config/i3/bar/themes/default.json b/i3/.config/i3/bar/themes/default.json
new file mode 100644
index 0000000..ddda5fe
--- /dev/null
+++ b/i3/.config/i3/bar/themes/default.json
@@ -0,0 +1,7 @@
+{
+ "icons": [ "ascii" ],
+ "defaults": {
+ "urgent": true,
+ "fg": "#aabbcc"
+ }
+}
diff --git a/i3/.config/i3/bar/themes/gruvbox-powerline.json b/i3/.config/i3/bar/themes/gruvbox-powerline.json
new file mode 100644
index 0000000..5cbfdc0
--- /dev/null
+++ b/i3/.config/i3/bar/themes/gruvbox-powerline.json
@@ -0,0 +1,41 @@
+{
+ "icons": [ "paxy97", "awesome-fonts" ],
+ "defaults": {
+ "warning": {
+ "fg": "#1d2021",
+ "bg": "#d79921"
+ },
+ "critical": {
+ "fg": "#fbf1c7",
+ "bg": "#cc241d"
+ },
+ "default-separators": false,
+ "separator-block-width": 0
+ },
+ "cycle": [
+ {
+ "fg": "#ebdbb2",
+ "bg": "#1d2021"
+ },
+ {
+ "fg": "#fbf1c7",
+ "bg": "#282828"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ },
+ "AC": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/gruvbox.json b/i3/.config/i3/bar/themes/gruvbox.json
new file mode 100644
index 0000000..eee93c1
--- /dev/null
+++ b/i3/.config/i3/bar/themes/gruvbox.json
@@ -0,0 +1,41 @@
+{
+ "icons": [ "paxy97", "ascii" ],
+ "defaults": {
+ "warning": {
+ "fg": "#1d2021",
+ "bg": "#d79921"
+ },
+ "critical": {
+ "fg": "#fbf1c7",
+ "bg": "#cc241d"
+ },
+ "default-separators": false,
+ "separator-block-width": 0
+ },
+ "cycle": [
+ {
+ "fg": "#ebdbb2",
+ "bg": "#1d2021"
+ },
+ {
+ "fg": "#fbf1c7",
+ "bg": "#282828"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ },
+ "AC": {
+ "fg": "#1d2021",
+ "bg": "#b8bb26"
+ }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/icons/ascii.json b/i3/.config/i3/bar/themes/icons/ascii.json
new file mode 100644
index 0000000..9b03a46
--- /dev/null
+++ b/i3/.config/i3/bar/themes/icons/ascii.json
@@ -0,0 +1,70 @@
+{
+ "defaults": {
+ "padding": " "
+ },
+ "memory": { "prefix": "ram" },
+ "cpu": { "prefix": "cpu" },
+ "disk": { "prefix": "hdd" },
+ "dnf": { "prefix": "dnf" },
+ "brightness": { "prefix": "o" },
+ "cmus": {
+ "playing": { "prefix": ">" },
+ "paused": { "prefix": "||" },
+ "stopped": { "prefix": "[]" },
+ "prev": { "prefix": "|<" },
+ "next": { "prefix": ">|" },
+ "shuffle-on": { "prefix": "S" },
+ "shuffle-off": { "prefix": "[s]" },
+ "repeat-on": { "prefix": "R" },
+ "repeat-off": { "prefix": "[r]" }
+ },
+ "pasink": {
+ "muted": { "prefix": "audio(mute)" },
+ "unmuted": { "prefix": "audio" }
+ },
+ "amixer": {
+ "muted": { "prefix": "audio(mute)" },
+ "unmuted": { "prefix": "audio" }
+ },
+ "pasource": {
+ "muted": { "prefix": "mic(mute)" },
+ "unmuted": { "prefix": "mic" }
+ },
+ "nic": {
+ "wireless-up": { "prefix": "wifi" },
+ "wireless-down": { "prefix": "wifi" },
+ "wired-up": { "prefix": "lan" },
+ "wired-down": { "prefix": "lan" },
+ "tunnel-up": { "prefix": "tun" },
+ "tunnel-down": { "prefix": "tun" }
+ },
+ "battery": {
+ "charged": { "suffix": "full" },
+ "charging": { "suffix": "chr" },
+ "AC": { "suffix": "ac" },
+ "discharging-10": {
+ "prefix": "!",
+ "suffix": "dis"
+ },
+ "discharging-25": { "suffix": "dis" },
+ "discharging-50": { "suffix": "dis" },
+ "discharging-80": { "suffix": "dis" },
+ "discharging-100": { "suffix": "dis" }
+ },
+ "caffeine": {
+ "activated": {"prefix": "caf-on" }, "deactivated": { "prefix": "caf-off " }
+ },
+ "xrandr": {
+ "on": { "prefix": " off "}, "off": { "prefix": " on "}
+ },
+ "redshift": {
+ "day": { "prefix": "day" }, "night": { "prefix": "night" }, "transition": { "prefix": "trans" }
+ },
+ "sensors": {
+ "prefix": "sensors"
+ },
+ "traffic": {
+ "rx": { "prefix": "down"},
+ "tx": { "prefix": "up"}
+ }
+}
diff --git a/i3/.config/i3/bar/themes/icons/awesome-fonts.json b/i3/.config/i3/bar/themes/icons/awesome-fonts.json
new file mode 100644
index 0000000..4263a19
--- /dev/null
+++ b/i3/.config/i3/bar/themes/icons/awesome-fonts.json
@@ -0,0 +1,86 @@
+{
+ "defaults": {
+ "separator": "", "padding": " ",
+ "unknown": { "prefix": "" }
+ },
+ "date": { "prefix": "" },
+ "time": { "prefix": "" },
+ "memory": { "prefix": "" },
+ "cpu": { "prefix": "" },
+ "disk": { "prefix": "" },
+ "dnf": { "prefix": "" },
+ "pacman": { "prefix": "" },
+ "brightness": { "prefix": "" },
+ "load": { "prefix": "" },
+ "layout": { "prefix": "" },
+ "cmus": {
+ "playing": { "prefix": "" },
+ "paused": { "prefix": "" },
+ "stopped": { "prefix": "" },
+ "prev": { "prefix": "" },
+ "next": { "prefix": "" },
+ "shuffle-on": { "prefix": "" },
+ "shuffle-off": { "prefix": "" },
+ "repeat-on": { "prefix": "" },
+ "repeat-off": { "prefix": "" }
+ },
+ "gpmdp": {
+ "playing": { "prefix": "" },
+ "paused": { "prefix": "" },
+ "stopped": { "prefix": "" },
+ "prev": { "prefix": "" },
+ "next": { "prefix": "" }
+ },
+ "pasink": {
+ "muted": { "prefix": "" },
+ "unmuted": { "prefix": "" }
+ },
+ "amixer": {
+ "muted": { "prefix": "" },
+ "unmuted": { "prefix": "" }
+ },
+ "pasource": {
+ "muted": { "prefix": "" },
+ "unmuted": { "prefix": "" }
+ },
+ "kernel": {
+ "prefix": "\uf17c"
+ },
+ "nic": {
+ "wireless-up": { "prefix": "" },
+ "wireless-down": { "prefix": "" },
+ "wired-up": { "prefix": "" },
+ "wired-down": { "prefix": "" },
+ "tunnel-up": { "prefix": "" },
+ "tunnel-down": { "prefix": "" }
+ },
+ "battery": {
+ "charged": { "prefix": "", "suffix": "" },
+ "AC": { "suffix": "" },
+ "charging": {
+ "prefix": [ "", "", "", "", "" ],
+ "suffix": ""
+ },
+ "discharging-10": { "prefix": "", "suffix": "" },
+ "discharging-25": { "prefix": "", "suffix": "" },
+ "discharging-50": { "prefix": "", "suffix": "" },
+ "discharging-80": { "prefix": "", "suffix": "" },
+ "discharging-100": { "prefix": "", "suffix": "" }
+ },
+ "caffeine": {
+ "activated": {"prefix": " " }, "deactivated": { "prefix": " " }
+ },
+ "xrandr": {
+ "on": { "prefix": " "}, "off": { "prefix": " " }
+ },
+ "redshift": {
+ "day": { "prefix": "" }, "night": { "prefix": "" }, "transition": { "prefix": "" }
+ },
+ "sensors": {
+ "prefix": "🌡"
+ },
+ "traffic":{
+ "rx": { "prefix": "" },
+ "tx": { "prefix": "" }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/icons/paxy97.json b/i3/.config/i3/bar/themes/icons/paxy97.json
new file mode 100644
index 0000000..9a889ab
--- /dev/null
+++ b/i3/.config/i3/bar/themes/icons/paxy97.json
@@ -0,0 +1,5 @@
+{
+ "memory": {
+ "prefix": "  "
+ }
+}
diff --git a/i3/.config/i3/bar/themes/icons/test.json b/i3/.config/i3/bar/themes/icons/test.json
new file mode 100644
index 0000000..ad17178
--- /dev/null
+++ b/i3/.config/i3/bar/themes/icons/test.json
@@ -0,0 +1,6 @@
+{
+ "test-widget": {
+ "prefix": "widget-prefix",
+ "suffix": "widget-suffix"
+ }
+}
diff --git a/i3/.config/i3/bar/themes/kulade.json b/i3/.config/i3/bar/themes/kulade.json
new file mode 100644
index 0000000..40f64e9
--- /dev/null
+++ b/i3/.config/i3/bar/themes/kulade.json
@@ -0,0 +1,46 @@
+{
+ "icons": [ "awesome-fonts" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "warning": {
+ "fg": "#8C001A",
+ "bg": "#ffffff"
+ },
+ "critical": {
+ "fg": "#ffffff",
+ "bg": "#8C001A"
+ },
+ "good": {
+ "fg": "#000000",
+ "bg": "#008800"
+ },
+ "great": {
+ "fg": "#000000",
+ "bg": "#008080"
+ },
+ "mid": {
+ "fg": "#000000",
+ "bg": "#808000"
+ }
+ },
+ "cycle": [
+ { "fg": "#93a1a1", "bg": "#000000" },
+ { "fg": "#eee8d5", "bg": "#333333" }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#000000",
+ "bg": "#008080"
+ },
+ "AC": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/powerline.json b/i3/.config/i3/bar/themes/powerline.json
new file mode 100644
index 0000000..80be8cf
--- /dev/null
+++ b/i3/.config/i3/bar/themes/powerline.json
@@ -0,0 +1,41 @@
+{
+ "icons": [ "awesome-fonts" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "critical": {
+ "fg": "#ffffff",
+ "bg": "#ff0000"
+ },
+ "warning": {
+ "fg": "#d75f00",
+ "bg": "#ffd700"
+ },
+ "default_separators": false
+ },
+ "cycle": [
+ {
+ "fg": "#ffd700",
+ "bg": "#d75f00"
+ },
+ {
+ "fg": "#ffffff",
+ "bg": "#0087af"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#494949",
+ "bg": "#41db00"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#494949",
+ "bg": "#41db00"
+ },
+ "AC": {
+ "fg": "#494949",
+ "bg": "#41db00"
+ }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/solarized-powerline.json b/i3/.config/i3/bar/themes/solarized-powerline.json
new file mode 100644
index 0000000..e1fbe1d
--- /dev/null
+++ b/i3/.config/i3/bar/themes/solarized-powerline.json
@@ -0,0 +1,40 @@
+{
+ "icons": [ "awesome-fonts" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "warning": {
+ "fg": "#002b36",
+ "bg": "#b58900"
+ },
+ "critical": {
+ "fg": "#002b36",
+ "bg": "#dc322f"
+ }
+ },
+ "cycle": [
+ { "fg": "#93a1a1", "bg": "#002b36" },
+ { "fg": "#eee8d5", "bg": "#586e75" }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "pacman": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ },
+ "AC": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/solarized.json b/i3/.config/i3/bar/themes/solarized.json
new file mode 100644
index 0000000..12a71ae
--- /dev/null
+++ b/i3/.config/i3/bar/themes/solarized.json
@@ -0,0 +1,42 @@
+{
+ "icons": [ "ascii" ],
+ "defaults": {
+ "separator-block-width": 0,
+ "critical": {
+ "fg": "#002b36",
+ "bg": "#dc322f"
+ },
+ "warning": {
+ "fg": "#002b36",
+ "bg": "#b58900"
+ },
+ "default_separators": false,
+ "separator": ""
+ },
+ "cycle": [
+ {
+ "fg": "#93a1a1",
+ "bg": "#002b36"
+ },
+ {
+ "fg": "#eee8d5",
+ "bg": "#586e75"
+ }
+ ],
+ "dnf": {
+ "good": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ },
+ "battery": {
+ "charged": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ },
+ "AC": {
+ "fg": "#002b36",
+ "bg": "#859900"
+ }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/test.json b/i3/.config/i3/bar/themes/test.json
new file mode 100644
index 0000000..1d457a7
--- /dev/null
+++ b/i3/.config/i3/bar/themes/test.json
@@ -0,0 +1,25 @@
+{
+ "icons": [ "test" ],
+ "defaults": {
+ "prefix": "default-prefix",
+ "suffix": "default-suffix",
+ "fg": "#000000",
+ "bg": "#111111",
+ "separator": " * ",
+ "separator-block-width": 10,
+ "critical": {
+ "fg": "#ffffff",
+ "bg": "#010101"
+ }
+ },
+ "test-widget": {
+ "fg": "#ababab",
+ "bg": "#222222",
+ "critical": {
+ "fg": "#bababa"
+ },
+ "cycle-test": {
+ "fg": [ "#000000", "#111111" ]
+ }
+ }
+}
diff --git a/i3/.config/i3/bar/themes/test_cycle.json b/i3/.config/i3/bar/themes/test_cycle.json
new file mode 100644
index 0000000..5fd7e1a
--- /dev/null
+++ b/i3/.config/i3/bar/themes/test_cycle.json
@@ -0,0 +1,18 @@
+{
+ "icons": [ "test" ],
+ "defaults": {
+ "prefix": "default-prefix",
+ "suffix": "default-suffix",
+ "fg": "#000000",
+ "bg": "#111111"
+ },
+ "cycle": [
+ { "fg": "#aa0000" },
+ { "fg": "#00aa00" },
+ { "fg": "#0000aa" }
+ ],
+ "test-widget": {
+ "fg": "#ababab",
+ "bg": "#222222"
+ }
+}
diff --git a/i3/.config/i3/bar/themes/test_invalid.json b/i3/.config/i3/bar/themes/test_invalid.json
new file mode 100644
index 0000000..d510f27
--- /dev/null
+++ b/i3/.config/i3/bar/themes/test_invalid.json
@@ -0,0 +1 @@
+this is really not json
diff --git a/i3/.config/i3/config b/i3/.config/i3/config
new file mode 100644
index 0000000..c634a1e
--- /dev/null
+++ b/i3/.config/i3/config
@@ -0,0 +1,560 @@
+# vim: filetype=i3
+
+###---Basic Definitions---###
+#Needed for i3-gaps
+hide_edge_borders none
+for_window [class="^.*"] border pixel 0
+gaps inner 15
+gaps outer 15
+font pango:Inconsolata 9
+set $mod Mod4
+#set $term urxvt
+set $video --no-startup-id bash ~/.config/Scripts/video.sh
+set $stoprec --no-startup-id killall ffmpeg & killall screenkey
+set $flash --no-startup-id bash ~/.config/Scripts/flash_win.sh
+set $browser firefox
+set $term st
+#set $browser qutebrowser
+
+
+###---Starting External Scripts---###
+#Music player daemon:
+exec --no-startup-id mpd
+
+#Wifi applet:
+exec --no-startup-id nm-applet
+
+#Mount other drives:
+exec --no-startup-id sudo mount -a
+
+#Composite manager:
+#exec --no-startup-id compton --xrender-sync-fence
+exec --no-startup-id compton
+
+#Add wallpaper:
+exec --no-startup-id wal -c -i ~/.config/wall1.jpg
+
+#Try to load VGA screen if available:
+exec --no-startup-id ~/.config/Scripts/screen.sh d
+
+#Remaps the caps lock button to escape and uses the US international keyboard with dead keys
+exec_always --no-startup-id setxkbmap -layout us -variant altgr-intl -option caps:escape
+
+#Remap Menu button to Super:
+exec_always --no-startup-id xmodmap -e 'keycode 135 = Super_R'
+bindsym Menu exec --no-startup-id xmodmap -e 'keycode 135 = Super_R'
+
+#Start dropbox
+#exec --no-startup-id dropbox
+
+#Start power manager
+exec --no-startup-id xfce4-power-manager
+
+#Torrent daemon:
+#exec --no-startup-id transmission-daemon
+
+#Refresh bash/ranger shortcuts:
+#exec --no-startup-id python ~/.config/Scripts/shortcuts.py
+
+#Launch Polybar where appropriate:
+exec_always --no-startup-id launch_polybar.sh
+
+#Unclutter makes the mouse invisible after a brief period
+#exec --no-startup-id unclutter
+
+# Turn off touchpad
+exec --no-startup-id exec synclient TouchpadOff=1
+
+# Xflux
+exec --no-startup-id /usr/bin/xflux -l 43.6404 -g -79.3995
+
+# Set Xresources colors:
+set_from_resource $darkblack color0 #000000
+set_from_resource $black color8 #000000
+set_from_resource $darkred color1 #000000
+set_from_resource $red color9 #000000
+set_from_resource $darkgreen color2 #000000
+set_from_resource $green color10 #000000
+set_from_resource $darkyellow color3 #000000
+set_from_resource $yellow color11 #000000
+set_from_resource $darkblue color4 #000000
+set_from_resource $blue color12 #000000
+set_from_resource $darkmagenta color5 #000000
+set_from_resource $magenta color13 #000000
+set_from_resource $darkcyan color6 #000000
+set_from_resource $cyan color14 #000000
+set_from_resource $darkwhite color7 #000000
+set_from_resource $white color15 #000000
+set $transparent #00000000
+
+# BORDER BACKGROUND TEXT INDICATOR CHILD_BORDER
+client.focused $red $red $magenta $darkmagenta $darkblue
+client.unfocused $transparent $blue $white $darkblue $darkblack
+client.focused_inactive $transparent $blue $white $darkblue $darkblack
+client.urgent $darkred $darkred $black $darkred $darkred
+client.background $black
+
+#Start i3bar
+#bar {
+# status_command i3blocks
+# position top
+# mode dock
+# modifier None
+# tray_padding 0
+# font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
+# font pango:Inconsolata
+# #font pango:DejaVu Sans Mono 11
+# colors {
+# background $darkblack
+# statusline $darkwhite
+# separator $cyan
+# focused_workspace $blue $darkblue $darkblack
+# active_workspace $blue $blue $darkwhite
+# inactive_workspace $darkblack $darkblack $white
+# urgent_workspace $darkblack $darkblack $white
+# }
+#}
+
+###---Audio and Music Definitions---###
+##For mpc:
+set $music --no-startup-id $term -e ncmpcpp
+set $pause --no-startup-id mpc toggle
+set $trupause --no-startup-id mpc pause
+set $next --no-startup-id mpc next
+set $prev --no-startup-id mpc prev
+set $lilfor --no-startup-id mpc seek +10
+set $bigfor --no-startup-id mpc seek +120
+set $lilbak --no-startup-id mpc seek -10
+set $bigbak --no-startup-id mpc seek -120
+set $beg --no-startup-id mpc seek 0%
+
+##For moc:
+#set $music --no-startup-id st -e mocp
+#set $pause --no-startup-id mocp -G
+#set $truepause --no-startup-id mocp -P
+#set $next --no-startup-id mocp -f
+#set $prev --no-startup-id mocp -r
+#set $lilfor --no-startup-id mocp -k 10
+#set $bigfor --no-startup-id mocp -k 120
+#set $lilbak --no-startup-id mocp -k -10
+#set $bigbak --no-startup-id mocp -k -120
+#set $beg --no-startup-id mocp -j 0%
+
+##For cmus:
+#set $music --no-startup-id st -e cmus
+#set $pause --no-startup-id cmus-remote -u
+#set $next --no-startup-id cmus-remote -n
+#set $prev --no-startup-id cmus-remote -r
+#set $lilfor --no-startup-id cmus-remote -k +10
+#set $bigfor --no-startup-id cmus-remote -k +120
+#set $lilbak --no-startup-id cmus-remote -k -10
+#set $bigbak --no-startup-id cmus-remote -k -120
+#set $beg --no-startup-id cmus-remote -k 00:00:00
+
+#FOR ALSA/AMIXER
+#set $inc --no-startup-id amixer sset Master 2%+
+#set $biginc --no-startup-id amixer sset Master 5%+
+#set $dec --no-startup-id amixer sset Master 2%-
+#set $bigdec --no-startup-id amixer sset Master 5%-
+#set $mute --no-startup-id amixer sset Master toggle
+#set $truemute --no-startup-id amixer sset Master mute
+#set $screencast --no-startup-id bash ~/.config/Scripts/screencast_alsa.sh
+#set $audio --no-startup-id bash ~/.config/Scripts/audio_alsa.sh
+
+#For PULSEAUDIO/PAMIXER
+set $inc --no-startup-id pamixer --allow-boost -i 5
+set $biginc --no-startup-id pamixer --allow-boost -i 15
+set $dec --no-startup-id pamixer --allow-boost -d 5
+set $bigdec --no-startup-id pamixer --allow-boost -d 15
+set $mute --no-startup-id pamixer --allow-boost -t
+set $micmute --no-startup-id pamixer --allow-boost -t
+set $truemute --no-startup-id pamixer -m
+set $screencast --no-startup-id bash ~/.config/Scripts/screencast_pulse.sh
+set $audio --no-startup-id bash ~/.config/Scripts/audio_pulse.sh
+
+
+###---Dropdown/Scratchpad Windows---###
+# create a misc terminal floating window with tmux
+for_window [instance="dropdown"] floating enable
+for_window [instance="dropdown"] resize set 675 450
+#for_window [instance="dropdown"] move position center
+for_window [instance="dropdown"] move scratchpad
+for_window [instance="dropdown"] border pixel 3
+#exec --no-startup-id $term -name dropdown -e tmux -f ~/.config/tmux/tmux_start.conf attach
+exec --no-startup-id $term -n dropdown -e tmux -f ~/.config/tmux/tmux_start.conf attach
+
+# create a math floating window with R
+for_window [instance="math"] floating enable
+for_window [instance="math"] resize set 600 400
+for_window [instance="math"] move scratchpad
+for_window [instance="math"] border pixel 3
+#exec --no-startup-id $term -name dropdown -e tmux -f ~/.config/tmux/tmux_start.conf attach
+exec --no-startup-id $term -n math -e R
+
+
+###---Basic Bindings---###
+bindsym $mod+Return exec $term
+#bindsym $mod+Shift+Return exec $term -e tmux -f ~/.config/tmux/tmux.conf
+bindsym $mod+Shift+Return exec $term -e tmux_connect.sh
+
+bindsym $mod+Shift+space floating toggle
+bindsym $mod+space focus mode_toggle
+
+bindsym $mod+Escape workspace prev
+#bindsym $mod+Shift+Escape exec i3-msg exit
+set $signout "Are you sure you want to logout?"
+
+mode $signout {
+ bindsym y exec --no-startup-id i3exit logout, mode "default"
+ bindsym n mode default
+ bindsym Escape mode default
+}
+
+bindsym $mod+Shift+Escape mode $signout
+
+bindsym $mod+BackSpace exec $flash
+bindsym $mod+Shift+BackSpace exec sudo reboot
+
+bindsym $mod+grave exec $flash
+#bindsym $mod+asciitilde
+#STOP/HIDE EVERYTHING:
+bindsym $mod+Shift+Delete exec $truemute ; exec $truepause ; workspace lmao ; exec $term -e htop ; exec $term -e ranger
+
+
+###---Letter Key Bindings---###
+bindsym $mod+q kill
+bindsym $mod+Shift+q kill
+
+bindsym $mod+w exec --no-startup-id $browser -private-window
+bindsym $mod+Shift+w exec --no-startup-id $browser
+
+bindsym $mod+e exec $term -n email -e neomutt
+#bindsym $mod+e exec $term -name email -e neomutt
+#bindsym $mod+Shift+e
+
+bindsym $mod+r exec $term -e vifm
+bindsym $mod+Shift+r restart
+
+bindsym $mod+t split toggle
+bindsym $mod+Shift+t gaps inner current set 25; gaps outer current set 25
+
+#bindsym $mod+y exec $term -e R and
+bindsym $mod+y [instance="math"] scratchpad show; move position center
+bindsym $mod+Shift+y resize shrink width 10 px or 10ppt
+
+bindsym $mod+u [instance="dropdown"] scratchpad show; move position center
+bindsym $mod+Shift+u resize shrink height 10 px or 10 ppt
+
+bindsym $mod+i exec $term -e htop
+bindsym $mod+Shift+i resize grow height 10 px or 10 ppt
+
+bindsym $mod+o exec --no-startup-id $beg
+bindsym $mod+Shift+o resize grow width 10 px or 10 ppt
+
+bindsym $mod+p exec $pause
+bindsym $mod+Shift+p exec keepass
+
+#bindsym $mod+a [instance="math"] scratchpad show; move position center
+bindsym $mod+Shift+a exec $term -e alsamixer
+
+bindsym $mod+s gaps inner current plus 5
+bindsym $mod+Shift+s gaps inner current minus 5
+
+bindsym $mod+d exec rofi -show run
+bindsym $mod+Shift+d gaps inner current set 0; gaps outer current set 0
+
+bindsym $mod+f fullscreen toggle
+bindsym $mod+Shift+f exec --no-startup-id cat ~/.emoji | rofi -dmenu -i -lines 10 -p Search | awk '{print $1}' | tr -d '\n' | xclip -selection clipboard && notify-send --urgency=low "Copied to Clipboard"
+#bindsym $mod+Shift+f exec --no-startup-id exec ~/.config/rofi/rofi-fontawesome/fontawesome-menu/fontawesome-menu -f /home/kdam0/.config/rofi/rofi-fontawesome/fontawesome-menu/fa5-icon-list.txt -o '-i -columns 4'
+
+
+bindsym $mod+g layout toggle tabbed split
+#bindsym $mod+Shift+g exec --no-startup-id gimp; workspace $ws5
+
+bindsym $mod+h focus left
+bindsym $mod+Shift+h move left 30
+
+bindsym $mod+j focus down
+bindsym $mod+Shift+j move down 30
+
+bindsym $mod+k focus up
+bindsym $mod+Shift+k move up 30
+
+bindsym $mod+l focus right
+bindsym $mod+Shift+l move right 30
+
+bindsym $mod+z gaps outer current plus 5
+bindsym $mod+Shift+z gaps outer current minus 5
+
+bindsym $mod+x exec --no-startup-id ~/.config/i3/lock.sh
+bindsym $mod+Shift+x exec --no-startup-id i3exit shutdown
+
+#bindsym $mod+c exec --no-startup-id firefox -private-window
+#bindsym $mod+Shift+c
+
+bindsym $mod+v exec $term -e ncmpcpp -s visualizer
+#bindsym $mod+Shift+v
+
+bindsym $mod+b exec $prev
+#bindsym $mod+Shift+b exec --no-startup-id blender; workspace $ws6
+
+bindsym $mod+n exec $next
+#bindsym $mod+Shift+n exec xdotool mousemove 1279 0
+
+bindsym $mod+m exec --no-startup-id $term -e ncmpcpp
+bindsym $mod+Shift+m exec $mute
+
+
+###---Workspace Bindings---###
+bindsym $mod+Home workspace $ws1
+bindsym $mod+Shift+Home move container to workspace $ws1
+bindsym $mod+End workspace $ws10
+bindsym $mod+Shift+End move container to workspace $ws10
+bindsym $mod+Prior workspace prev
+bindsym $mod+Shift+Prior move container to workspace prev
+bindsym $mod+Next workspace next
+bindsym $mod+Shift+Next move container to workspace next
+bindsym $mod+Tab workspace back_and_forth
+bindsym $mod+XF86Back workspace prev
+#bindsym $mod+Shift+XF86Back
+bindsym $mod+XF86Forward workspace next
+#bindsym $mod+Shift+XF86Forward
+bindsym $mod+semicolon workspace next
+bindsym $mod+apostrophe split horizontal ;; exec $term
+bindsym $mod+slash split vertical ;; exec $term
+bindsym $mod+Shift+slash kill
+bindsym $mod+backslash workspace back_and_forth
+
+#set $ws1 "1: "
+#set $ws2 "2: "
+#set $ws3 "3: "
+#set $ws4 "4: "
+#set $ws5 "5: "
+#set $ws6 "6: "
+#set $ws7 "7: "
+#set $ws8 "8: "
+#set $ws9 "9: "
+#set $ws10 "10: "
+
+set $ws1 "1"
+set $ws2 "2"
+set $ws3 "3"
+set $ws4 "4"
+set $ws5 "5"
+set $ws6 "6"
+set $ws7 "7"
+set $ws8 "8"
+set $ws9 "9"
+set $ws10 "10"
+
+# switch to workspace
+bindsym $mod+1 workspace $ws1
+bindsym $mod+2 workspace $ws2
+bindsym $mod+3 workspace $ws3
+bindsym $mod+4 workspace $ws4
+bindsym $mod+5 workspace $ws5
+bindsym $mod+6 workspace $ws6
+bindsym $mod+7 workspace $ws7
+bindsym $mod+8 workspace $ws8
+bindsym $mod+9 workspace $ws9
+bindsym $mod+0 workspace $ws10
+
+# move focused container to workspace
+bindsym $mod+Shift+1 move container to workspace $ws1
+bindsym $mod+Shift+2 move container to workspace $ws2
+bindsym $mod+Shift+3 move container to workspace $ws3
+bindsym $mod+Shift+4 move container to workspace $ws4
+bindsym $mod+Shift+5 move container to workspace $ws5
+bindsym $mod+Shift+6 move container to workspace $ws6
+bindsym $mod+Shift+7 move container to workspace $ws7
+bindsym $mod+Shift+8 move container to workspace $ws8
+bindsym $mod+Shift+9 move container to workspace $ws9
+bindsym $mod+Shift+0 move container to workspace $ws10
+
+for_window [window_role="gimp-dock"] floating disable; move left; resize shrink width 50 px or 50ppt
+for_window [window_role="gimp-toolbox"] floating disable; move right; resize grow width 30 px or 30ppt
+for_window [window_role="GtkFileChooserDialog"] resize shrink height 10 px
+#for_window [class="st"] exec --no-startup-id transset -a --dec .25
+#for_window [class="Firefox"] exec --no-startup-id transset -a --dec .25
+
+assign [class="KeePass2"] $ws4
+assign [title="GIMP Startup"] $ws5
+assign [class="Gimp"] $ws5
+assign [class="Ghetto Skype"] $ws6
+assign [class="messenger for desktop"] $ws7
+assign [class="whats-app-nativefier-7bbd2c"] $ws7
+assign [class="Firefox"] $ws8
+assign [class="qutebrowser"] $ws8
+assign [title="neomutt"] $ws9
+#assign [class="Geary"] $ws9
+
+#workspace $ws8 gaps inner 0
+#workspace $ws8 gaps outer 0
+#workspace $ws7 gaps inner 0
+#workspace $ws7 gaps outer 0
+#workspace $ws8 gaps inner 0
+#workspace $ws8 gaps outer 0
+
+
+###---Function Buttons---###
+#bindsym F1 nop
+#bindsym F2
+#bindsym F3
+#bindsym F4
+#bindsym F5
+#bindsym F6
+#bindsym F7
+#bindsym F8
+#bindsym F9
+#bindsym F10
+bindsym F11 fullscreen toggle
+#bindsym F12
+
+#bindsym $mod+F1 exec --no-startup-id mupdf ~/.config/Scripts/i3_guide.md.pdf
+bindsym $mod+F1 exec --no-startup-id ~/.config/Scripts/get_email.sh
+bindsym $mod+F2 exec --no-startup-id python ~/.config/Scripts/shortcuts.py
+bindsym $mod+F3 exec --no-startup-id arandr
+bindsym $mod+F4 exec --no-startup-id i3exit suspend
+bindsym $mod+F5 exec --no-startup-id sudo sv restart NetworkManager
+bindsym $mod+F6 exec --no-startup-id transmission-gtk
+bindsym $mod+F7 exec --no-startup-id screen.sh l
+bindsym $mod+F8 exec --no-startup-id screen.sh d
+bindsym $mod+Shift+F7 exec transset-df -a --dec .15
+bindsym $mod+Shift+F8 exec transset-df -a --inc .15
+bindsym $mod+F9 exec --no-startup-id sudo mount -a
+#bindsym $mod+F10 exec --no-startup-id ~/.config/Scripts/screen.sh l ;; exec --no-startup-id ~/.config/polybar/launch.sh
+#bindsym $mod+F11 exec --no-startup-id ~/.config/Scripts/screen.sh v ;; exec --no-startup-id ~/.config/polybar/launch.sh
+#bindsym $mod+F12 exec --no-startup-id ~/.config/Scripts/screen.sh d ;; exec --no-startup-id ~/.config/polybar/launch.sh
+
+
+###---Arrow Keys---###
+bindsym $mod+Left focus left
+#bindsym $mod+Down focus down
+#bindsym $mod+Up focus up
+bindsym $mod+Right focus right
+bindsym $mod+Shift+Left move left
+bindsym $mod+Shift+Down move down
+bindsym $mod+Shift+Up move up
+bindsym $mod+Shift+Right move right
+
+
+###---Media Keys---###
+
+#Volume keys
+bindsym $mod+plus exec $inc
+bindsym $mod+Shift+plus exec $biginc
+bindsym $mod+minus exec $dec
+bindsym $mod+Shift+minus exec $bigdec
+bindsym $mod+greater exec $inc
+bindsym $mod+Shift+greater exec $biginc
+bindsym $mod+less exec $dec
+bindsym $mod+Shift+less exec $bigdec
+
+#For advancing forward/backward in an mpd song
+bindsym $mod+bracketleft exec $lilbak
+bindsym $mod+Shift+bracketleft exec $bigbak
+bindsym $mod+bracketright exec $lilfor
+bindsym $mod+Shift+bracketright exec $bigfor
+
+#Print screen takes a screenshot.
+#With shift, it takes one only of the given window.
+bindsym Print exec --no-startup-id scrot
+bindsym Shift+Print exec --no-startup-id scrot -u
+
+#For recording audio/video or stop recording. Also screenkey for visual typing.
+bindsym $mod+Print exec --no-startup-id screenkey
+bindsym $mod+Scroll_Lock exec $audio
+bindsym $mod+Insert exec $screencast
+bindsym $mod+Pause exec $video
+bindsym $mod+Delete exec $stoprec
+bindsym XF86Launch1 exec $stoprec
+
+
+
+###---Extra XF86 Keys---###
+#These are the extra media keys that some keyboards have.
+bindsym XF86AudioMute exec $mute
+bindsym XF86AudioLowerVolume exec $dec
+bindsym XF86AudioRaiseVolume exec $inc
+bindsym XF86PowerOff exec sudo shutdown -h now
+#bindsym XF86Copy exec
+#bindsym XF86Open exec
+#bindsym XF86Paste exec
+#bindsym XF86Cut exec
+#bindsym XF86MenuKB exec
+bindsym XF86Calculator exec $term -e R -q
+bindsym XF86Sleep exec sudo zzz
+#bindsym XF86WakeUp exec
+bindsym XF86Explorer exec $term -e ranger
+#bindsym XF86Send exec
+#bindsym XF86Xfer exec
+bindsym XF86WWW exec --no-startup-id $browser
+#bindsym XF86DOS exec
+bindsym XF86ScreenSaver exec exec --no-startup-id ~/.config/i3/lock.sh
+#bindsym XF86RotateWindows exec ~/.config/Scripts/screen_rotate.sh
+bindsym XF86TaskPane exec ~/.config/Scripts/screen_rotate_clk.sh
+#bindsym XF86Favorites exec
+bindsym XF86MyComputer exec $term -e ranger
+#bindsym XF86Back exec
+#bindsym XF86Forward exec
+bindsym XF86Eject exec sudo umount -R /mnt
+bindsym XF86AudioNext exec $next
+bindsym XF86AudioPlay exec $pause
+bindsym XF86AudioPrev exec $prev
+bindsym XF86AudioStop exec $pause
+bindsym XF86AudioRecord exec $audio
+bindsym XF86AudioRewind exec $lilbak
+bindsym XF86AudioForward exec $lilfor
+#bindsym XF86Phone exec
+#bindsym XF86Tools exec
+bindsym XF86Reload restart
+#bindsym XF86ScrollUp exec
+#bindsym XF86ScrollDown exec
+#bindsym XF86New exec
+#bindsym XF86LaunchA exec
+#bindsym XF86LaunchB exec
+#bindsym XF86Launch2 exec
+#bindsym XF86Launch3 exec
+#bindsym XF86Launch4 exec
+#bindsym XF86Launch5 exec
+#bindsym XF86Launch6 exec
+#bindsym XF86Launch7 exec
+#bindsym XF86Launch8 exec
+#bindsym XF86Launch9 exec
+bindsym XF86AudioMicMute exec $micmute
+#bindsym XF86TouchpadToggle exec
+bindsym XF86TouchpadOn exec --no-startup-id exec synclient TouchpadOff=0
+bindsym XF86TouchpadOff exec --no-startup-id exec synclient TouchpadOff=1
+bindsym XF86Suspend exec --no-startup-id ~/.config/i3/lock.sh
+bindsym XF86Close kill
+bindsym XF86WebCam exec mpv /dev/video0
+bindsym XF86Mail exec $term -e mutt
+bindsym XF86Messenger exec $term -e weechat
+bindsym XF86Search exec $browser https://duckduckgo.com
+#bindsym XF86Go exec
+#bindsym XF86Finance exec
+#bindsym XF86Game exec
+bindsym XF86Shop exec $browser https://ebay.com
+bindsym XF86MonBrightnessDown exec --no-startup-id xbacklight -dec 15
+bindsym XF86MonBrightnessUp exec --no-startup-id xbacklight -inc 15
+bindsym XF86AudioMedia exec $music
+bindsym XF86Display exec --no-startup-id arandr
+#bindsym XF86KbdLightOnOff exec
+bindsym XF86KbdBrightnessDown exec --no-startup-id xbacklight -dec 15
+bindsym XF86KbdBrightnessUp exec --no-startup-id xbacklight -inc 15
+#bindsym XF86Reply exec
+#bindsym XF86MailForward exec
+#bindsym XF86Save exec
+bindsym XF86Documents exec $term -e ranger ~/Documents
+#bindsym XF86Battery exec
+#bindsym XF86Bluetooth exec
+bindsym XF86WLAN exec --no-startup-id sudo sv restart NetworkManager
+
+# auto launch workspace apps
+#exec $term -name email -e neomutt
+exec $term -n email -e neomutt
+exec $browser
+exec --no-startup-id i3-msg 'workspace 1; focus'
+#exec --no-startup-id i3-msg 'workspace 9; exec $term -n email -e neomutt'
+#exec whatsapp-web-desktop
+#exec --no-startup-id i3-msg 'workspace 1; exec $term'
diff --git a/i3/.config/i3/lock.png b/i3/.config/i3/lock.png
new file mode 100644
index 0000000..cd8b228
--- /dev/null
+++ b/i3/.config/i3/lock.png
Binary files differ
diff --git a/i3/.config/i3/lock.sh b/i3/.config/i3/lock.sh
new file mode 100644
index 0000000..6537bef
--- /dev/null
+++ b/i3/.config/i3/lock.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+scrot /tmp/screen.png
+#convert /tmp/screen.png -scale 10% -scale 1000% /tmp/screen.png
+#convert /tmp/screen.png -paint 3 -modulate 80 /tmp/screen.png
+convert /tmp/screen.png -paint 3 /tmp/screen.png
+[[ -f ~/.config/i3/lock.png ]] && convert /tmp/screen.png ~/.config/i3/lock.png -gravity center -composite -matte /tmp/screen.png
+mocp -P
+i3lock -u -e -i /tmp/screen.png
diff --git a/i3/.config/i3blocks/config b/i3/.config/i3blocks/config
new file mode 100644
index 0000000..d285da3
--- /dev/null
+++ b/i3/.config/i3blocks/config
@@ -0,0 +1,70 @@
+command=/usr/lib/i3blocks/$BLOCK_NAME
+separator_block_width=10
+markup=pango
+
+[window title]
+command=xtitle -s
+interval=persist
+min_width=1366
+align=center
+
+[mpd]
+label=🎼
+command=i3mpd
+interval=3
+align=left
+signal=11
+
+[memory]
+label=🍌
+separator=true
+interval=1
+#command=/usr/lib/i3blocks/memory
+#command=free -h | awk '/^Mem:/ {print $3 " / " $2}'
+command=free -h | awk '/^Mem:/ {print $3}'
+color=#dfe3ee
+
+#[weather]
+#command=i3weather
+#interval=1800
+##color=#c9c9ff
+
+[mail]
+label=📬
+command=i3mail
+interval=60
+align=left
+
+[volume]
+interval=1
+command=i3volume
+#command=amixer get Master | grep -E -o '[0-9]{1,3}?%' | head -1
+markup=pango
+color=#f19950
+
+[iface]
+label=
+instance=wlp3s0
+##color=#00FF00
+interval=10
+
+#[wifi]
+#instance=wlp3s0
+#interval=3
+
+#[wifi_strength]
+##instance=wlp3s0
+#command=echo $(expr `cat /proc/net/wireless | awk 'NR==3 {print $3}' | sed 's/\.//'` \* 100 / 70)%
+#label=Wifi:
+#separator=true
+#interval=3
+
+[battery]
+command=acpi | cut -d " " -f3-5 | sed -e "s/,//g;s/Discharging/🔋/;s/Charging/🔌/;s/Full/⚡/;s/ 0*/ /g;s/ :/ /g"
+interval=5
+
+[time]
+label=📅
+command=echo $(date '+%a, %b %d | %H:%M ')
+interval=5
+