From 34c4203b97dc8721947e37eebb7c925a96ce8aa3 Mon Sep 17 00:00:00 2001 From: Luke Date: Fri, 2 Jun 2017 15:49:10 -0400 Subject: [PATCH] First payload --- .Rprofile | 5 + .Xdefaults | 57 + .asoudrc | 13 + .bash_profile | 14 + .config/Scripts/audio.sh | 26 + .config/Scripts/aurinstall.sh | 5 + .config/Scripts/bashrc | 139 + .config/Scripts/clear.sh | 1 + .config/Scripts/configs | 24 + .config/Scripts/drives | 7 + .config/Scripts/extract.sh | 24 + .config/Scripts/flash_win.sh | 13 + .config/Scripts/folders | 10 + .config/Scripts/imapsync.sh | 8 + .config/Scripts/luke_guide.md | 154 + .config/Scripts/luke_guide.pdf | Bin 0 -> 37847 bytes .config/Scripts/mailsyncloop.sh | 12 + .config/Scripts/mount.sh | 16 + .config/Scripts/remaps | 1 + .config/Scripts/resize-font | 169 + .config/Scripts/screencast.sh | 29 + .config/Scripts/shortcuts.py | 41 + .config/Scripts/shrinkvid.sh | 13 + .config/Scripts/speedvid.sh | 7 + .config/Scripts/status.sh | 11 + .config/Scripts/test.sh | 11 + .config/Scripts/tmux.conf | 177 + .config/Scripts/transmission.sh | 7 + .config/Scripts/unmount.sh | 4 + .config/Scripts/video.sh | 26 + .config/Scripts/vimrc | 166 + .config/Scripts/wall_looper.sh | 16 + .config/calcurse/conf | 25 + .config/calcurse/keys | 70 + .config/compton.conf | 56 + .config/feh/keys | 12 + .config/fontconfig/fonts.conf | 24 + .config/i3/bar/.codeclimate.yml | 15 + .config/i3/bar/.coveragerc | 10 + .config/i3/bar/.gitignore | 91 + .config/i3/bar/.travis.yml | 18 + .config/i3/bar/LICENSE | 21 + .config/i3/bar/README.md | 134 + .config/i3/bar/bin/load-i3-bars.sh | 26 + .config/i3/bar/bin/pacman-updates | 22 + .config/i3/bar/bin/toggle-display.sh | 12 + .config/i3/bar/bumblebee-status | 66 + .config/i3/bar/bumblebee/__init__.py | 0 .config/i3/bar/bumblebee/config.py | 99 + .config/i3/bar/bumblebee/engine.py | 166 + .config/i3/bar/bumblebee/error.py | 15 + .config/i3/bar/bumblebee/input.py | 134 + .config/i3/bar/bumblebee/modules/__init__.py | 0 .config/i3/bar/bumblebee/modules/amixer.py | 36 + .config/i3/bar/bumblebee/modules/battery.py | 82 + .../i3/bar/bumblebee/modules/battery.py_ori | 75 + .../i3/bar/bumblebee/modules/brightness.py | 37 + .config/i3/bar/bumblebee/modules/caffeine.py | 48 + .config/i3/bar/bumblebee/modules/cmus.py | 92 + .config/i3/bar/bumblebee/modules/cpu.py | 37 + .config/i3/bar/bumblebee/modules/datetime.py | 35 + .config/i3/bar/bumblebee/modules/disk.py | 47 + .config/i3/bar/bumblebee/modules/disk.py_ori | 45 + .config/i3/bar/bumblebee/modules/dnf.py | 80 + .config/i3/bar/bumblebee/modules/error.py | 26 + .config/i3/bar/bumblebee/modules/gpmdp.py | 58 + .config/i3/bar/bumblebee/modules/kernel.py | 21 + .config/i3/bar/bumblebee/modules/layout.py | 74 + .config/i3/bar/bumblebee/modules/load.py | 41 + .config/i3/bar/bumblebee/modules/memory.py | 47 + .config/i3/bar/bumblebee/modules/nic.py | 83 + .config/i3/bar/bumblebee/modules/nic.py_ori | 88 + .config/i3/bar/bumblebee/modules/pacman.py | 70 + .config/i3/bar/bumblebee/modules/ping.py | 77 + .../i3/bar/bumblebee/modules/pulseaudio.py | 107 + .config/i3/bar/bumblebee/modules/redshift.py | 46 + .config/i3/bar/bumblebee/modules/sensors.py | 45 + .config/i3/bar/bumblebee/modules/spacer.py | 23 + .config/i3/bar/bumblebee/modules/stock.py | 62 + .config/i3/bar/bumblebee/modules/test.py | 16 + .config/i3/bar/bumblebee/modules/traffic.py | 72 + .config/i3/bar/bumblebee/modules/weather.py | 74 + .config/i3/bar/bumblebee/modules/xrandr.py | 74 + .config/i3/bar/bumblebee/output.py | 140 + .config/i3/bar/bumblebee/store.py | 21 + .config/i3/bar/bumblebee/theme.py | 180 + .config/i3/bar/bumblebee/util.py | 47 + .config/i3/bar/runlint.sh | 3 + .config/i3/bar/runtests.sh | 11 + .config/i3/bar/screenshots/amixer.png | Bin 0 -> 1645 bytes .config/i3/bar/screenshots/battery.png | Bin 0 -> 1733 bytes .config/i3/bar/screenshots/brightness.png | Bin 0 -> 1323 bytes .config/i3/bar/screenshots/caffeine.png | Bin 0 -> 1143 bytes .config/i3/bar/screenshots/cmus.png | Bin 0 -> 5031 bytes .config/i3/bar/screenshots/cpu.png | Bin 0 -> 2003 bytes .config/i3/bar/screenshots/date.png | Bin 0 -> 2877 bytes .config/i3/bar/screenshots/datetime.png | Bin 0 -> 4889 bytes .config/i3/bar/screenshots/disk.png | Bin 0 -> 3721 bytes .config/i3/bar/screenshots/dnf.png | Bin 0 -> 1113 bytes .config/i3/bar/screenshots/kernel.png | Bin 0 -> 3125 bytes .config/i3/bar/screenshots/layout.png | Bin 0 -> 1198 bytes .config/i3/bar/screenshots/load.png | Bin 0 -> 2005 bytes .config/i3/bar/screenshots/memory.png | Bin 0 -> 3603 bytes .config/i3/bar/screenshots/nic.png | Bin 0 -> 2474 bytes .config/i3/bar/screenshots/pacman.png | Bin 0 -> 860 bytes .config/i3/bar/screenshots/pasink.png | Bin 0 -> 1486 bytes .config/i3/bar/screenshots/pasource.png | Bin 0 -> 1505 bytes .config/i3/bar/screenshots/ping.png | Bin 0 -> 1655 bytes .config/i3/bar/screenshots/pulseaudio.png | Bin 0 -> 2495 bytes .config/i3/bar/screenshots/redshift.png | Bin 0 -> 1325 bytes .config/i3/bar/screenshots/sensors.png | Bin 0 -> 1054 bytes .config/i3/bar/screenshots/spacer.png | Bin 0 -> 521 bytes .config/i3/bar/screenshots/stock.png | Bin 0 -> 2087 bytes .config/i3/bar/screenshots/themes/default.png | Bin 0 -> 7251 bytes .config/i3/bar/screenshots/themes/gruvbox.png | Bin 0 -> 10310 bytes .../screenshots/themes/powerline-gruvbox.png | Bin 0 -> 11594 bytes .../themes/powerline-solarized.png | Bin 0 -> 11991 bytes .../i3/bar/screenshots/themes/powerline.png | Bin 0 -> 10701 bytes .../i3/bar/screenshots/themes/solarized.png | Bin 0 -> 9612 bytes .config/i3/bar/screenshots/time.png | Bin 0 -> 2456 bytes .config/i3/bar/screenshots/traffic.png | Bin 0 -> 3811 bytes .config/i3/bar/screenshots/weather.png | Bin 0 -> 1111 bytes .config/i3/bar/screenshots/xrandr.png | Bin 0 -> 2693 bytes .config/i3/bar/testjson.sh | 3 + .config/i3/bar/tests/__init__.py | 0 .config/i3/bar/tests/mocks.py | 141 + .config/i3/bar/tests/modules/__init__.py | 0 .config/i3/bar/tests/modules/test_battery.py | 108 + .../i3/bar/tests/modules/test_brightness.py | 47 + .config/i3/bar/tests/modules/test_caffeine.py | 53 + .config/i3/bar/tests/modules/test_cmus.py | 114 + .config/i3/bar/tests/modules/test_cpu.py | 45 + .config/i3/bar/tests/modules/test_disk.py | 47 + .config/i3/bar/tests/modules/test_load.py | 57 + .config/i3/bar/tests/modules/test_memory.py | 52 + .../i3/bar/tests/modules/test_pulseaudio.py | 34 + .config/i3/bar/tests/test_config.py | 77 + .config/i3/bar/tests/test_engine.py | 90 + .config/i3/bar/tests/test_i3barinput.py | 110 + .config/i3/bar/tests/test_i3baroutput.py | 133 + .config/i3/bar/tests/test_module.py | 89 + .config/i3/bar/tests/test_modules.py | 58 + .config/i3/bar/tests/test_store.py | 26 + .config/i3/bar/tests/test_theme.py | 134 + .config/i3/bar/tests/test_util.py | 56 + .config/i3/bar/themes/default.json | 7 + .config/i3/bar/themes/gruvbox-powerline.json | 41 + .config/i3/bar/themes/gruvbox.json | 41 + .config/i3/bar/themes/icons/ascii.json | 70 + .../i3/bar/themes/icons/awesome-fonts.json | 86 + .config/i3/bar/themes/icons/paxy97.json | 5 + .config/i3/bar/themes/icons/test.json | 6 + .config/i3/bar/themes/kulade.json | 46 + .config/i3/bar/themes/powerline.json | 41 + .../i3/bar/themes/solarized-powerline.json | 40 + .config/i3/bar/themes/solarized.json | 42 + .config/i3/bar/themes/test.json | 25 + .config/i3/bar/themes/test_cycle.json | 18 + .config/i3/bar/themes/test_invalid.json | 1 + .config/i3/config | 393 +++ .config/i3/lock.png | Bin 0 -> 5016 bytes .config/i3/lock.sh | 8 + .config/mimeapps.list | 26 + .config/moc/config | 13 + .config/moc/keymap | 188 + .config/moc/themes/theme | 32 + .config/mpv/input.conf | 199 ++ .config/mutt/aliases | 13 + .config/mutt/gmailrc | 28 + .config/mutt/luke_mutt_readme.md | 61 + .config/mutt/mailcap | 11 + .config/mutt/mutt.sh | 4 + .config/mutt/muttcol | 151 + .config/mutt/muttimage.sh | 8 + .config/mutt/muttrc | 42 + .config/neofetch/config | 431 +++ .config/ranger/commands.py | 216 ++ .config/ranger/commands_full.py | 1508 ++++++++ .config/ranger/luke_ranger_readme.md | 38 + .config/ranger/luke_ranger_readme.pdf | Bin 0 -> 21510 bytes .config/ranger/rc.conf.base | 523 +++ .config/ranger/rifle.conf | 221 ++ .config/ranger/scope.sh | 121 + .config/user-dirs.dirs | 1 + .config/wall.png | Bin 0 -> 195327 bytes .msmtprc | 13 + .offlineimaprc | 25 + .../ftplugin/markdown/instant-markdown.vim | 188 + .vim/after/ftplugin/tex.vim | 1 + .vim/autoload/pathogen.vim | 289 ++ .vim/bin/live-latex-check.sh | 14 + .vim/bin/live-latex-update.sh | 99 + .vim/bin/mupdf-launch.sh | 15 + .vim/bin/notInUse/local-install | 8 + .vim/bin/notInUse/local-uninstall | 6 + .vim/bundle/goyo.vim | 1 + .vim/bundle/tabular | 1 + .vim/bundle/vim-live-latex-preview | 1 + .vim/bundle/vim-luna-master/LICENSE.rst | 25 + .vim/bundle/vim-luna-master/README.rst | 47 + .../vim-luna-master/colors/luna-term.vim | 208 ++ .vim/bundle/vim-luna-master/colors/luna.vim | 255 ++ .vim/bundle/vim-multiple-cursors | 1 + .vim/bundle/vim-surround | 1 + .vim/bundle/vimwiki/.gitignore | 22 + .vim/bundle/vimwiki/README-cn.md | 131 + .vim/bundle/vimwiki/README.md | 135 + .vim/bundle/vimwiki/autoload/vimwiki/base.vim | 2026 +++++++++++ .../autoload/vimwiki/customwiki2html.sh | 62 + .../vimwiki/autoload/vimwiki/default.tpl | 11 + .../bundle/vimwiki/autoload/vimwiki/diary.vim | 295 ++ .vim/bundle/vimwiki/autoload/vimwiki/html.vim | 1600 +++++++++ .vim/bundle/vimwiki/autoload/vimwiki/lst.vim | 1538 +++++++++ .../autoload/vimwiki/markdown_base.vim | 231 ++ .vim/bundle/vimwiki/autoload/vimwiki/path.vim | 169 + .../bundle/vimwiki/autoload/vimwiki/style.css | 75 + .vim/bundle/vimwiki/autoload/vimwiki/tags.vim | 342 ++ .vim/bundle/vimwiki/autoload/vimwiki/tbl.vim | 673 ++++ .vim/bundle/vimwiki/autoload/vimwiki/u.vim | 68 + .vim/bundle/vimwiki/doc/screenshot_1.png | Bin 0 -> 29362 bytes .vim/bundle/vimwiki/doc/screenshot_2.png | Bin 0 -> 49959 bytes .vim/bundle/vimwiki/doc/vimwiki.txt | 3025 +++++++++++++++++ .vim/bundle/vimwiki/ftplugin/vimwiki.vim | 674 ++++ .vim/bundle/vimwiki/plugin/vimwiki.vim | 558 +++ .../vimwiki/syntax/omnipresent_syntax.vim | 35 + .vim/bundle/vimwiki/syntax/vimwiki.vim | 616 ++++ .../bundle/vimwiki/syntax/vimwiki_default.vim | 95 + .../vimwiki/syntax/vimwiki_markdown.vim | 91 + .../syntax/vimwiki_markdown_custom.vim | 394 +++ .vim/bundle/vimwiki/syntax/vimwiki_media.vim | 72 + .vim/doc/SyntaxRange.txt | 159 + .vim/doc/live-latex-preview.txt | 109 + .vim/doc/tags | 27 + .vim/ftplugin/tex/live-latex-preview.vim | 221 ++ .vim/syntax/i3.vim | 110 + .vimrc | 1 + .xinitrc | 1 + README.md | 27 +- install_dependencies.sh | 10 + pic1.png | Bin 0 -> 774418 bytes 240 files changed, 25320 insertions(+), 2 deletions(-) create mode 100644 .Rprofile create mode 100644 .Xdefaults create mode 100644 .asoudrc create mode 100644 .bash_profile create mode 100644 .config/Scripts/audio.sh create mode 100644 .config/Scripts/aurinstall.sh create mode 100644 .config/Scripts/bashrc create mode 100755 .config/Scripts/clear.sh create mode 100644 .config/Scripts/configs create mode 100755 .config/Scripts/drives create mode 100755 .config/Scripts/extract.sh create mode 100644 .config/Scripts/flash_win.sh create mode 100755 .config/Scripts/folders create mode 100644 .config/Scripts/imapsync.sh create mode 100644 .config/Scripts/luke_guide.md create mode 100644 .config/Scripts/luke_guide.pdf create mode 100755 .config/Scripts/mailsyncloop.sh create mode 100755 .config/Scripts/mount.sh create mode 100644 .config/Scripts/remaps create mode 100644 .config/Scripts/resize-font create mode 100644 .config/Scripts/screencast.sh create mode 100644 .config/Scripts/shortcuts.py create mode 100755 .config/Scripts/shrinkvid.sh create mode 100755 .config/Scripts/speedvid.sh create mode 100644 .config/Scripts/status.sh create mode 100755 .config/Scripts/test.sh create mode 100644 .config/Scripts/tmux.conf create mode 100755 .config/Scripts/transmission.sh create mode 100755 .config/Scripts/unmount.sh create mode 100644 .config/Scripts/video.sh create mode 100644 .config/Scripts/vimrc create mode 100644 .config/Scripts/wall_looper.sh create mode 100644 .config/calcurse/conf create mode 100644 .config/calcurse/keys create mode 100644 .config/compton.conf create mode 100755 .config/feh/keys create mode 100755 .config/fontconfig/fonts.conf create mode 100644 .config/i3/bar/.codeclimate.yml create mode 100644 .config/i3/bar/.coveragerc create mode 100644 .config/i3/bar/.gitignore create mode 100644 .config/i3/bar/.travis.yml create mode 100644 .config/i3/bar/LICENSE create mode 100644 .config/i3/bar/README.md create mode 100755 .config/i3/bar/bin/load-i3-bars.sh create mode 100755 .config/i3/bar/bin/pacman-updates create mode 100755 .config/i3/bar/bin/toggle-display.sh create mode 100755 .config/i3/bar/bumblebee-status create mode 100644 .config/i3/bar/bumblebee/__init__.py create mode 100644 .config/i3/bar/bumblebee/config.py create mode 100644 .config/i3/bar/bumblebee/engine.py create mode 100644 .config/i3/bar/bumblebee/error.py create mode 100644 .config/i3/bar/bumblebee/input.py create mode 100644 .config/i3/bar/bumblebee/modules/__init__.py create mode 100644 .config/i3/bar/bumblebee/modules/amixer.py create mode 100644 .config/i3/bar/bumblebee/modules/battery.py create mode 100644 .config/i3/bar/bumblebee/modules/battery.py_ori create mode 100644 .config/i3/bar/bumblebee/modules/brightness.py create mode 100644 .config/i3/bar/bumblebee/modules/caffeine.py create mode 100644 .config/i3/bar/bumblebee/modules/cmus.py create mode 100644 .config/i3/bar/bumblebee/modules/cpu.py create mode 100644 .config/i3/bar/bumblebee/modules/datetime.py create mode 100644 .config/i3/bar/bumblebee/modules/disk.py create mode 100644 .config/i3/bar/bumblebee/modules/disk.py_ori create mode 100644 .config/i3/bar/bumblebee/modules/dnf.py create mode 100644 .config/i3/bar/bumblebee/modules/error.py create mode 100644 .config/i3/bar/bumblebee/modules/gpmdp.py create mode 100644 .config/i3/bar/bumblebee/modules/kernel.py create mode 100644 .config/i3/bar/bumblebee/modules/layout.py create mode 100644 .config/i3/bar/bumblebee/modules/load.py create mode 100644 .config/i3/bar/bumblebee/modules/memory.py create mode 100644 .config/i3/bar/bumblebee/modules/nic.py create mode 100644 .config/i3/bar/bumblebee/modules/nic.py_ori create mode 100644 .config/i3/bar/bumblebee/modules/pacman.py create mode 100644 .config/i3/bar/bumblebee/modules/ping.py create mode 100644 .config/i3/bar/bumblebee/modules/pulseaudio.py create mode 100644 .config/i3/bar/bumblebee/modules/redshift.py create mode 100644 .config/i3/bar/bumblebee/modules/sensors.py create mode 100644 .config/i3/bar/bumblebee/modules/spacer.py create mode 100644 .config/i3/bar/bumblebee/modules/stock.py create mode 100644 .config/i3/bar/bumblebee/modules/test.py create mode 100644 .config/i3/bar/bumblebee/modules/traffic.py create mode 100644 .config/i3/bar/bumblebee/modules/weather.py create mode 100644 .config/i3/bar/bumblebee/modules/xrandr.py create mode 100644 .config/i3/bar/bumblebee/output.py create mode 100644 .config/i3/bar/bumblebee/store.py create mode 100644 .config/i3/bar/bumblebee/theme.py create mode 100644 .config/i3/bar/bumblebee/util.py create mode 100755 .config/i3/bar/runlint.sh create mode 100755 .config/i3/bar/runtests.sh create mode 100644 .config/i3/bar/screenshots/amixer.png create mode 100644 .config/i3/bar/screenshots/battery.png create mode 100644 .config/i3/bar/screenshots/brightness.png create mode 100644 .config/i3/bar/screenshots/caffeine.png create mode 100644 .config/i3/bar/screenshots/cmus.png create mode 100644 .config/i3/bar/screenshots/cpu.png create mode 100644 .config/i3/bar/screenshots/date.png create mode 100644 .config/i3/bar/screenshots/datetime.png create mode 100644 .config/i3/bar/screenshots/disk.png create mode 100644 .config/i3/bar/screenshots/dnf.png create mode 100644 .config/i3/bar/screenshots/kernel.png create mode 100644 .config/i3/bar/screenshots/layout.png create mode 100644 .config/i3/bar/screenshots/load.png create mode 100644 .config/i3/bar/screenshots/memory.png create mode 100644 .config/i3/bar/screenshots/nic.png create mode 100644 .config/i3/bar/screenshots/pacman.png create mode 100644 .config/i3/bar/screenshots/pasink.png create mode 100644 .config/i3/bar/screenshots/pasource.png create mode 100644 .config/i3/bar/screenshots/ping.png create mode 100644 .config/i3/bar/screenshots/pulseaudio.png create mode 100644 .config/i3/bar/screenshots/redshift.png create mode 100644 .config/i3/bar/screenshots/sensors.png create mode 100644 .config/i3/bar/screenshots/spacer.png create mode 100644 .config/i3/bar/screenshots/stock.png create mode 100644 .config/i3/bar/screenshots/themes/default.png create mode 100644 .config/i3/bar/screenshots/themes/gruvbox.png create mode 100644 .config/i3/bar/screenshots/themes/powerline-gruvbox.png create mode 100644 .config/i3/bar/screenshots/themes/powerline-solarized.png create mode 100644 .config/i3/bar/screenshots/themes/powerline.png create mode 100644 .config/i3/bar/screenshots/themes/solarized.png create mode 100644 .config/i3/bar/screenshots/time.png create mode 100644 .config/i3/bar/screenshots/traffic.png create mode 100644 .config/i3/bar/screenshots/weather.png create mode 100644 .config/i3/bar/screenshots/xrandr.png create mode 100755 .config/i3/bar/testjson.sh create mode 100644 .config/i3/bar/tests/__init__.py create mode 100644 .config/i3/bar/tests/mocks.py create mode 100644 .config/i3/bar/tests/modules/__init__.py create mode 100644 .config/i3/bar/tests/modules/test_battery.py create mode 100644 .config/i3/bar/tests/modules/test_brightness.py create mode 100644 .config/i3/bar/tests/modules/test_caffeine.py create mode 100644 .config/i3/bar/tests/modules/test_cmus.py create mode 100644 .config/i3/bar/tests/modules/test_cpu.py create mode 100644 .config/i3/bar/tests/modules/test_disk.py create mode 100644 .config/i3/bar/tests/modules/test_load.py create mode 100644 .config/i3/bar/tests/modules/test_memory.py create mode 100644 .config/i3/bar/tests/modules/test_pulseaudio.py create mode 100644 .config/i3/bar/tests/test_config.py create mode 100644 .config/i3/bar/tests/test_engine.py create mode 100644 .config/i3/bar/tests/test_i3barinput.py create mode 100644 .config/i3/bar/tests/test_i3baroutput.py create mode 100644 .config/i3/bar/tests/test_module.py create mode 100644 .config/i3/bar/tests/test_modules.py create mode 100644 .config/i3/bar/tests/test_store.py create mode 100644 .config/i3/bar/tests/test_theme.py create mode 100644 .config/i3/bar/tests/test_util.py create mode 100644 .config/i3/bar/themes/default.json create mode 100644 .config/i3/bar/themes/gruvbox-powerline.json create mode 100644 .config/i3/bar/themes/gruvbox.json create mode 100644 .config/i3/bar/themes/icons/ascii.json create mode 100644 .config/i3/bar/themes/icons/awesome-fonts.json create mode 100644 .config/i3/bar/themes/icons/paxy97.json create mode 100644 .config/i3/bar/themes/icons/test.json create mode 100644 .config/i3/bar/themes/kulade.json create mode 100644 .config/i3/bar/themes/powerline.json create mode 100644 .config/i3/bar/themes/solarized-powerline.json create mode 100644 .config/i3/bar/themes/solarized.json create mode 100644 .config/i3/bar/themes/test.json create mode 100644 .config/i3/bar/themes/test_cycle.json create mode 100644 .config/i3/bar/themes/test_invalid.json create mode 100755 .config/i3/config create mode 100644 .config/i3/lock.png create mode 100755 .config/i3/lock.sh create mode 100644 .config/mimeapps.list create mode 100644 .config/moc/config create mode 100755 .config/moc/keymap create mode 100755 .config/moc/themes/theme create mode 100644 .config/mpv/input.conf create mode 100644 .config/mutt/aliases create mode 100644 .config/mutt/gmailrc create mode 100644 .config/mutt/luke_mutt_readme.md create mode 100755 .config/mutt/mailcap create mode 100644 .config/mutt/mutt.sh create mode 100755 .config/mutt/muttcol create mode 100755 .config/mutt/muttimage.sh create mode 100755 .config/mutt/muttrc create mode 100755 .config/neofetch/config create mode 100755 .config/ranger/commands.py create mode 100755 .config/ranger/commands_full.py create mode 100644 .config/ranger/luke_ranger_readme.md create mode 100644 .config/ranger/luke_ranger_readme.pdf create mode 100644 .config/ranger/rc.conf.base create mode 100644 .config/ranger/rifle.conf create mode 100644 .config/ranger/scope.sh create mode 100755 .config/user-dirs.dirs create mode 100644 .config/wall.png create mode 100644 .msmtprc create mode 100644 .offlineimaprc create mode 100644 .vim/after/ftplugin/markdown/instant-markdown.vim create mode 100644 .vim/after/ftplugin/tex.vim create mode 100644 .vim/autoload/pathogen.vim create mode 100755 .vim/bin/live-latex-check.sh create mode 100755 .vim/bin/live-latex-update.sh create mode 100755 .vim/bin/mupdf-launch.sh create mode 100755 .vim/bin/notInUse/local-install create mode 100755 .vim/bin/notInUse/local-uninstall create mode 160000 .vim/bundle/goyo.vim create mode 160000 .vim/bundle/tabular create mode 160000 .vim/bundle/vim-live-latex-preview create mode 100644 .vim/bundle/vim-luna-master/LICENSE.rst create mode 100644 .vim/bundle/vim-luna-master/README.rst create mode 100644 .vim/bundle/vim-luna-master/colors/luna-term.vim create mode 100644 .vim/bundle/vim-luna-master/colors/luna.vim create mode 160000 .vim/bundle/vim-multiple-cursors create mode 160000 .vim/bundle/vim-surround create mode 100644 .vim/bundle/vimwiki/.gitignore create mode 100644 .vim/bundle/vimwiki/README-cn.md create mode 100644 .vim/bundle/vimwiki/README.md create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/base.vim create mode 100755 .vim/bundle/vimwiki/autoload/vimwiki/customwiki2html.sh create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/default.tpl create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/diary.vim create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/html.vim create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/lst.vim create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/markdown_base.vim create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/path.vim create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/style.css create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/tags.vim create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/tbl.vim create mode 100644 .vim/bundle/vimwiki/autoload/vimwiki/u.vim create mode 100644 .vim/bundle/vimwiki/doc/screenshot_1.png create mode 100644 .vim/bundle/vimwiki/doc/screenshot_2.png create mode 100644 .vim/bundle/vimwiki/doc/vimwiki.txt create mode 100644 .vim/bundle/vimwiki/ftplugin/vimwiki.vim create mode 100644 .vim/bundle/vimwiki/plugin/vimwiki.vim create mode 100644 .vim/bundle/vimwiki/syntax/omnipresent_syntax.vim create mode 100644 .vim/bundle/vimwiki/syntax/vimwiki.vim create mode 100644 .vim/bundle/vimwiki/syntax/vimwiki_default.vim create mode 100644 .vim/bundle/vimwiki/syntax/vimwiki_markdown.vim create mode 100644 .vim/bundle/vimwiki/syntax/vimwiki_markdown_custom.vim create mode 100644 .vim/bundle/vimwiki/syntax/vimwiki_media.vim create mode 100644 .vim/doc/SyntaxRange.txt create mode 100644 .vim/doc/live-latex-preview.txt create mode 100644 .vim/doc/tags create mode 100644 .vim/ftplugin/tex/live-latex-preview.vim create mode 100644 .vim/syntax/i3.vim create mode 100644 .vimrc create mode 100644 .xinitrc create mode 100644 install_dependencies.sh create mode 100644 pic1.png diff --git a/.Rprofile b/.Rprofile new file mode 100644 index 00000000..23e4cbaa --- /dev/null +++ b/.Rprofile @@ -0,0 +1,5 @@ +# The .First function is called after everything else in .Rprofile is executed +#.First <- function() { +#message("Welcome back ", Sys.getenv("USER"),"!\n","working directory is:", getwd()) +#} +options(repos=structure(c(CRAN="http://cran.revolutionanalytics.com/"))) diff --git a/.Xdefaults b/.Xdefaults new file mode 100644 index 00000000..6d1175cb --- /dev/null +++ b/.Xdefaults @@ -0,0 +1,57 @@ +*foreground: #00cc00 +*foreground_bold: #a8a19f +*cursor: #a8a19f +!!background = #1b1918 +!!*background: rgba(0, 0, 0, .7) + +!! black +*color0: #1b1918 +*color8: #766e6b +!! red +*color1: #f22c40 +*color9: #f22c40 +!! green +*color2: #5ab738 +*color10: #5ab738 +!! yellow +*color3: #d5911a +*color11: #d5911a +!! blue +*color4: #407ee7 +*color12: #407ee7 +!! magenta +*color5: #6666ea +*color13: #6666ea +!! cyan +*color6: #00ad9c +*color14: #00ad9c +!! white +*color7: #a8a19f +*color15: #f1efee + +URxvt.intensityStyles: false +URxvt.background: [70]#000000 +URxvt.depth: 32 +URxvt.font: xft:monospace:size=11 +URxvt.scrollBar: false +URxvt.cursorColor: white +URxvt.perl-ext-common: default,matcher,resize-font +URxvt.perl-lib: $HOME/.config/Scripts +URxvt.url-launcher: /usr/bin/xdg-open +URxvt.matcher.button: 1 +URxvt.resize-font.smaller: C-Down +URxvt.resize-font.bigger: C-Up + +URxvt.colorUL: #4682B4 +rofi.color-enabled: true +rofi.color-window: #000, #000, #000 +rofi.color-normal: #111, #819396, #222, #008ed4, #ffffff +rofi.color-active: #002b37, #008ed4, #003643, #008ed4, #66c6ff +rofi.color-urgent: #002b37, #da4281, #003643, #008ed4, #890661 + +rofi.fake-transparency: true +rofi.lines: 3 +rofi.bw: 0 +rofi.opacity: "10" +rofi.hide-scrollbar: true +rofi.width: 30 diff --git a/.asoudrc b/.asoudrc new file mode 100644 index 00000000..3d265408 --- /dev/null +++ b/.asoudrc @@ -0,0 +1,13 @@ +pcm.softvol { + type softvol + slave.pcm "cards.pcm.default" + control { + name "Software" + card 0 + } + max_db 20.0 +} +pcm.!default { + type plug + slave.pcm "softvol" +} diff --git a/.bash_profile b/.bash_profile new file mode 100644 index 00000000..a410cf32 --- /dev/null +++ b/.bash_profile @@ -0,0 +1,14 @@ +# +# ~/.bash_profile +# + +[[ -f ~/.bashrc ]] && . ~/.bashrc + +#if [ -z "$DISPLAY" ] && [ "$(fgconsole)" -eq 1 ]; then + #startx +#fi + +#[ -z "$DISPLAY" -a "$(fgconsole)" -eq 1 ] && exec startx + + +export BROWSER=firefox diff --git a/.config/Scripts/audio.sh b/.config/Scripts/audio.sh new file mode 100644 index 00000000..5e61e20c --- /dev/null +++ b/.config/Scripts/audio.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +#This is the ffmpeg command that the audio shortcut in i3 will run. + +#Picks a file name for the output file based on availability: + +if [[ -f ~/output.flac ]] + then + n=1 + while [[ -f ~/output_$n.flac ]] + do + n=$((n+1)) + done + filename="output_$n.flac" + else + filename="output.flac" +fi + +#The actual ffmpeg command: + +ffmpeg \ +-thread_queue_size 1024 \ + -f alsa -ar 44100 -i hw:1 \ +-af "volume=12" \ + -c:v libx264 -r 30 -c:a flac $filename + #-c:v ffvhuff -r 30 -c:a flac $filename diff --git a/.config/Scripts/aurinstall.sh b/.config/Scripts/aurinstall.sh new file mode 100644 index 00000000..94733882 --- /dev/null +++ b/.config/Scripts/aurinstall.sh @@ -0,0 +1,5 @@ +#!/bin/bash +wget https://aur.archlinux.org/cgit/aur.git/snapshot/$1.tar.gz +tar -xvzf $1.tar.gz +cd $1 && makepkg --no-confirm -si +cd .. && rm -rf $1 $1.tar.gz diff --git a/.config/Scripts/bashrc b/.config/Scripts/bashrc new file mode 100644 index 00000000..648758d6 --- /dev/null +++ b/.config/Scripts/bashrc @@ -0,0 +1,139 @@ +stty -ixon + +export PS1="\[$(tput bold)\]\[$(tput setaf 1)\][\[$(tput setaf 3)\]\u\[$(tput setaf 2)\]@\[$(tput setaf 4)\]\h \[$(tput setaf 5)\]\W\[$(tput setaf 1)\]]\[$(tput setaf 7)\]\\$ \[$(tput sgr0)\]" + +if [[ -f /usr/bin/iceweasel ]]; then + export BROWSER="iceweasel" +else + export BROWSER="firefox" +fi + +shopt -s autocd + +#if [[ -z $DISPLAY ]] && [[ $(tty) = /dev/tty1 ]]; then + #exec startx +#fi + + +#Basic Aliases +alias v="vim" +#alias sv="sudo vim" +alias r="ranger" +alias ls='ls -hN --color=auto --group-directories-first' +alias lsa='ls -A' +g() { cd $1 && ls -a ;} +alias b="cd .. && ls -a" +alias ?="man" +alias q="exit" +alias e="exit" + +alias mutt="mutt -F ~/.config/mutt/muttrc" +alias mocp="mocp --config ~/.config/moc/config" +alias calcurse="calcurse -D ~/.config/calcurse" +alias weechat="weechat -d ~/.config/weechat" +alias syncthing="syncthing -home='$HOME/.syncthing'" +alias tmux="tmux -f ~/.config/Scripts/tmux.conf" +#NEWSBEUTER and W3M + +alias Xi="sudo xbps-install" +alias Xu="sudo xbps-remove -R" +alias Xup="sudo xbps-install -Suv" +alias Xc="xbps-remove -Oo" +alias Xq="xbps-query -Rs" +alias XI="xbps-query -s" + +#Term only +alias mute="pamixer -m" +alias vd="pamixer -d 10" +alias vu="pamixer -i 10" +alias p="mocp -G &> /dev/null" +alias next="mocp -f &> /dev/null" +alias prev="mocp -r &> /dev/null" +alias mnt="sudo bash ~/.config/Scripts/mount.sh" +alias umnt="sudo bash ~/.config/Scripts/unmount.sh" +alias sdn="sudo shutdown now" + +alias screenfetch="screenfetch -t" +alias yt="youtube-dl -ic" +alias yta="youtube-dl -xic" + +alias webedit="ssh -l lukesmith -p 2222 lukesmith.xyz" +alias web="ssh -l lukesmith -p 2222 lukesmith.xyz" +alias desk="ssh -l luke -p 22 10.193.0.46" +serversend() { rsync -avr --rsh='ssh -p2222' $1 lukesmith@lukesmith.xyz:/home1/lukesmith/public_html ;} +alias ein="ssh -l einchan -p 22 104.238.215.7" +alias starwars="telnet towel.blinkenlights.nl" + +alias rcsync="rsync -avr --rsh='ssh -p2222' .bashrc .vimrc .Xdefaults .tmux.conf .muttrc .bash_profile .vim .w3m .moc lukesmith@lukesmith.xyz:/home1/lukesmith/config" + +alias newnet="sudo systemctl restart NetworkManager" +alias atltime="sudo timedatectl set-timezone America/New_York && i3 restart" +alias tuctime="sudo timedatectl set-timezone America/Phoenix && i3 restart" + +bl() { convert $@ -resize 1440x1080\> bl_$@ ;} + +alias pingme="ping lukesmith.xyz" + +alias youtube="youtube-viewer" +alias YT="youtube-viewer" +alias syt="youtube-viewer" + +alias etym="sdcv -u \"English Etymology\"" + +alias Txa="cp ~/Documents/LaTeX/article.tex" +alias Txs="cp ~/Documents/LaTeX/beamer.tex" +alias Txh="cp ~/Documents/LaTeX/handout.tex" +alias TC='find . -maxdepth 1 -regextype gnu-awk -regex "^.*\.(pyc|pyo|bak|swp|aux|log|nav|out|snm|toc|bcf|run\.xml|synctex\.gz|blg|bbl)" -delete' + +getgit() { git clone http://github.com/$1.git ;} + +folder() { echo -e "$1\t$2" >> ~/.config/Scripts/folders && i3 restart ;} +weath() { curl wttr.in/$1 ;} +alias work="mkdir ~/Work && cd ~/Work" + + +longterm() { rsync -avr --rsh='ssh -p2222' $1 lukesmith@lukesmith.xyz:/home1/lukesmith/public_html/longterm ;} +sendmine() { rsync -avr --rsh='ssh -p2222' $1 lukesmith@lukesmith.xyz:/home1/lukesmith/public_html/mine ;} + +alias test='~/.config/bumblebee-status/bumblebee-status -m pasink pasource battery nic pacman disk datetime -p disk.path=/home datetime.format="%a, %b %d, %Y at %I:%M %p" -t kulade' + +getlt() { wget http://lukesmith.xyz/longterm/$1 ;} +getmine() { wget http://lukesmith.xyz/mine/$1 --user=lukesmith --ask-password ;} + +memefind() { find ~/Pictures/Memes/ -iname "*$1*" ;} +CF() { cd ~/.config/$1 && ls ;} + +alias now="vim ~/Documents/Phonology/squib1.tex" + +alias ethspeed="speedometer -r enp0s25" + +note() { echo "$@" >> ~/notes ;} + +alias extract="~/.config/Script/ext.sh" + +alias refresh="python ~/.config/Scripts/shortcuts.py" + +backup() { cp $1 $1.bu ;} + +#alias man="w3mman" + +alias mailsync="bash ~/.config/Scripts/mailsyncloop.sh" + +alias wifispeed="speedometer -r wlp2s0" + +alias trigger="bash ~/Creations/Scripts/trigger.sh" + +alias bbs="python ~/.config/i3/bar/bumblebee-status" + +alias tr="transmission-remote" + +alias servs="ls /etc/sv" + +alias debase="sudo umount /home/Shared/Videos" + +alias makemine="sudo chown -R luke:luke /home/luke/Downloads/*" + +serven() { sudo ln -s /etc/sv/$1 /var/service/ ;} +servdis() { sudo rm /var/service/$1 ;} + +aurinstall() { curl -O https://aur.archlinux.org/cgit/aur.git/snapshot/$1.tar.gz && tar -xvzf $1.tar.gz && cd $1 && makepkg --noconfirm -si && cd .. && rm -rf $1 $1.tar.gz ;} diff --git a/.config/Scripts/clear.sh b/.config/Scripts/clear.sh new file mode 100755 index 00000000..af13a73e --- /dev/null +++ b/.config/Scripts/clear.sh @@ -0,0 +1 @@ +find . -maxdepth 1 -regextype gnu-awk -regex "^.*\.(pyc|p yo|bak|swp|aux|log|lof|nav|out|snm|toc|bcf|run\.xml|synctex\.gz|blg|bbl)" -delete diff --git a/.config/Scripts/configs b/.config/Scripts/configs new file mode 100644 index 00000000..ab70dcf3 --- /dev/null +++ b/.config/Scripts/configs @@ -0,0 +1,24 @@ +cfb ~/.config/Scripts/bashrc +cfs ~/.config/Scripts/status.sh +cfz ~/.zshrc +cfv ~/.config/Scripts/vimrc +cfr ~/.config/ranger/rc.conf.base +cfi ~/.config/i3/config +cfq ~/.config/qutebrowser/keys.conf.base +cfQ ~/.config/qutebrowser/qutebrowser.conf +cfm ~/.config/mutt/muttrc +cfM ~/.config/moc/keymap +cff ~/.config/Scripts/folders +cfc ~/.config/Scripts/configs +cft ~/.config/termite/config +cfT ~/.config/Scripts/tmux.conf +eb ~/Documents/LaTeX/uni.bib +cv ~/Documents/LaTeX/cv.tex +cfl ~/.config/mutt/lukexyz.info +cfx ~/.config/mutt/luxmyth.info +cfk ~/.config/mutt/kulade.cock +cfo ~/.config/mutt/kulade.info +cfa ~/.config/mutt/aliases +cfp ~/.config/polybar/config +cfd ~/.Xdefaults +TO ~/Creations/Videos/todo.md diff --git a/.config/Scripts/drives b/.config/Scripts/drives new file mode 100755 index 00000000..3bf38d49 --- /dev/null +++ b/.config/Scripts/drives @@ -0,0 +1,7 @@ +wd2 1A3FE4E62BC1A399 +sd 9C33-6BBD +king 66faa60c-b24f-46a6-92cd-b064e9dbdc6c +sony4 6C67-A7B4 +black 0ff55b51-5294-4bc2-bbf0-819a804b0503 +archive 0EC4117AC41164ED +blue 8f0d3707-f2a1-4a8f-910a-49d97ba80931 diff --git a/.config/Scripts/extract.sh b/.config/Scripts/extract.sh new file mode 100755 index 00000000..5b70a65c --- /dev/null +++ b/.config/Scripts/extract.sh @@ -0,0 +1,24 @@ +if [ -f $1 ] ; then + # NAME=${1%.*} + # mkdir $NAME && cd $NAME + case $1 in + *.tar.bz2) tar xvjf ../$1 ;; + *.tar.gz) tar xvzf ../$1 ;; + *.tar.xz) tar xvJf ../$1 ;; + *.lzma) unlzma ../$1 ;; + *.bz2) bunzip2 ../$1 ;; + *.rar) unrar x -ad ../$1 ;; + *.gz) gunzip ../$1 ;; + *.tar) tar xvf ../$1 ;; + *.tbz2) tar xvjf ../$1 ;; + *.tgz) tar xvzf ../$1 ;; + *.zip) unzip ../$1 ;; + *.Z) uncompress ../$1 ;; + *.7z) 7z x ../$1 ;; + *.xz) unxz ../$1 ;; + *.exe) cabextract ../$1 ;; + *) echo "extract: '$1' - unknown archive method" ;; + esac +else +echo "$1 - file does not exist" + fi diff --git a/.config/Scripts/flash_win.sh b/.config/Scripts/flash_win.sh new file mode 100644 index 00000000..91d6416c --- /dev/null +++ b/.config/Scripts/flash_win.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +#Flashes the active window. + +#Requires transset-df and a composite manager, like xcompmgr. + +transset -a -m 0 +sleep .1 +transset -a -x 1 +sleep .1 +transset -a -m 0 +sleep .1 +transset -a -x 1 diff --git a/.config/Scripts/folders b/.config/Scripts/folders new file mode 100755 index 00000000..3826f6dd --- /dev/null +++ b/.config/Scripts/folders @@ -0,0 +1,10 @@ +h ~ +d ~/Documents +D ~/Downloads +p ~/Pictures +v ~/Videos +m ~/Music +b ~/Books +s ~/.config/Scripts +/ / +r / diff --git a/.config/Scripts/imapsync.sh b/.config/Scripts/imapsync.sh new file mode 100644 index 00000000..47bc43e9 --- /dev/null +++ b/.config/Scripts/imapsync.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +if [ -f $(pgrep offlineimap) ]; then + offlineimap -o + echo "Sync begun." +else + echo "Sync in progress." +fi diff --git a/.config/Scripts/luke_guide.md b/.config/Scripts/luke_guide.md new file mode 100644 index 00000000..2d311b38 --- /dev/null +++ b/.config/Scripts/luke_guide.md @@ -0,0 +1,154 @@ +# A friendly guide to Luke's i3 rice + + +Use vim keys (h/j/k/l) to navigate this document. Pressing W will fit it to window width. + and - zoom in and out. q to quit. (These are general mupdf shortcuts.) + ++ Mod+u will show this document at any time. + +General questions? Leave a comment on YouTube or email me at luke@lukesmith.xyz. + +## Basic goals and principles of my rice: + ++ Naturalness - I want the number of keypresses I have to make to get what I want as little as possible. ++ Keyboard/vim-centrality - All my terminal apps (and other programs) use vim keys when possible. My hands never need leave the home row or thereabout. ++ Lots of color -- Many rices stick to one general color palatte. I like my system to be very vibrant. + +## General changes + ++ Capslock is now an alternative escape. Makes vim-craft much more efficient. ++ The menu button (usually between the right Alt and Ctrl) is an alternative Super/Mod button. This is to make one-handing on my laptops easier. + +# Shortcut keys + +## Window basics + +Notice the case sensitivity of the shortcuts. + +Be sure you play around with these. Be flexible with the basic commands and the rice will grow on you quick. + ++ Mod+Enter -- Spawn terminal ++ Mod+q or Q -- Close window ++ Mod+d -- rofi (For running commands or programs without shortcuts) ++ Mod+t -- Toggle between spawning vertically and horizontally ++ Mod+f or F11 -- Fullscreen ++ Mod+h/j/k/l -- Move to different windows ++ Mod+H/J/K/L -- Move a window around ++ Mod+Y/U/I/O -- Resize windows ++ Mod+/ -- Spawn vertical terminal ++ Mod+' -- Spawn horizonal terminal ++ Mod+s/S -- Increase/decrease inner gaps ++ Mod+z/Z -- Increase/decrease outer gaps ++ Mod+D -- Reduce gaps to 0 ++ Mod+T -- Restore gaps to default (15) ++ Mod+Shift+Space -- Make a window float (you can still resize and move floating windows with the same keys above) ++ Mod+Space -- Switch from a floating window to a non-floating one (or vice versa) + +## Basic Programs + ++ Mod+r -- ranger (file browser/manager) ++ Mod+e -- mutt (email) ++ Mod+m -- Music on Console Player ++ Mod+a -- qalc (calculator) ++ Mod+i -- htop (system info) ++ Mod+N -- newsbeuter (RSS feed reader) ++ Mod+y -- calcurse (calendar and schedule) ++ Mod+Shift+Enter -- tmux ++ Mod+w -- qutebrowser (web browser) + +## Larger programs + ++ Mod+W -- Firefox ++ Mod+E -- Thunderbird (Not installed by default) ++ Mod+B -- Blender ++ Mod+G -- GIMP ++ Mod+P -- Pinta ++ Mod+A -- Audacity + +## System + ++ Mod+R -- Restart/refresh i3 (renews configs, does not close any programs) ++ Mod+x -- i3lock (Enter password to return) ++ Mod+X -- shutdown now (Be careful with this one!) ++ Mod+Shift+Backspace -- reboot (And this one!) ++ Mod+Shift+Escape -- exit i3 (And this one as well!) ++ Mod+F3 -- arandr (For adding screens/HDMI/VGA connections) ++ Mod+F4 -- Hibernate ++ Mod+F5 -- Reset Network Manager ++ Mod+F7 -- Increase window transparency ++ Mod+F8 -- Decrease window transparency + +## Audio + +I use moc/mocp as a music player. If you prefer cmus, I have commented out shortcuts you can activate for it instead in the i3 config. + ++ Mod+m -- Music on Console Player ++ Mod+n -- Next track ++ Mod+b -- Previous track ++ Mod+o -- Restart track ++ Mod+p -- Pause ++ Mod+M -- Mute all audio ++ Mod+v -- cli-visualizer ++ Mod+V -- projectM visualizer ++ Mod+- -- Decrease volume (holding shift increases amount) ++ Mod++ -- Increase volume (holding shift increases amount) ++ Mod+[ -- Back 10 seconds (holding shift increases amount) ++ Mod+] -- Forward 10 seconds (holding shift increases amount) + +## Workspaces + +There are ten workspaces. They work just like those in vanilla i3 with some additions. + ++ Mod+(Number) -- Go to that number workspace ++ Mod+Shift+(Number) -- Send window to that workspace ++ Mod+Tab -- Go to previous workspace ++ Mod+g or escape -- Go to left workspace ++ Mod+; -- Go to right workspace + +## Recording + ++ Pause -- Begin screencast. Output goes into ~. Will overwrite any previous output. ++ ScrollLock -- Begin audio recording. Same traits as above ++ Mod+either of the above keys -- kills ffmpeg, thus ending recordings ++ ThinkVantage button (on Thinkpads) -- kills ffmpeg, thus ending recordings ++ Print Screen -- Take a scrot screenshot ++ Shift+Print Screen -- Take a scrot screenshot of only selected window + +## Other buttons + +I've mapped those extra buttons that some keyboards have (play and pause buttons, email, webbrowsing buttons, etc.) to what you would expect. + +# Special traits of my rice + +## Easy config access + +Open a terminal and type "cfc." This will open a file where you will see customizable pairs of key shortcuts and config files. Enter any of these shortcuts in bash or ranger to immediately open the file in vim. + +You may add new entries here and they will be refreshed when you refresh i3 (Mod+R). + +## Folder and config shortcuts + +Open a terminal and type "cff." This opens a file when you can keep and create folder shortcuts. There are only a few here now, because I don't know what your folder structure is going to look like, but on my machine, I have 81 and growing. + +Each line has a shortcut key/keys and its target. These can be used in serveral applications. In bash, simply press "d," the shortcut for ~/Documents and you will cd there (and automatically ls -a). + +ranger works similarly. When in ranger, just press "g" then the shortcut of the folder you want to go to. You may also press "t" plus the shortcut to open a new tab there. "m" plus the shortcut moves the selected files to the folder and "Y" copies them there. **Get good at this. It will make management of even the most complex file system easy.** + +Lastly qutebrowser implements these shortcuts as well. When you see a file or image you want to download, press ";" followed by the folder shortcut and qutebrowser will let you select the file with its hint system. The file will then download to the directory you chose. + +## Dynamically constructed configs + +To keep these different shortcuts in sync, my rice will dynamically reconstruct the configs to bash, qutebrowser and ranger each time you refresh i3 (Mod+R). + +Each time i3 starts or restarts, it will run ~/.config/Scripts/shortcuts.py, which reads the entries in the folder shortcut and config shortcut files and then translate them into the approriate syntax of all three programs. + +It then takes that output and appends it to base configs of each program (~/.config/Scripts/bashrc, ~/config/qutebrowser/keys.conf.base, ~/.config/rc.conf.base) and puts the output in the proper places for each program. + +## So what do I need to know? + +Use the files in "cff" and "cfc" to add/change shortcuts. These shortcuts will be synced between bash, ranger and qutebrowser. Press Mod+R to refresh them. If you want to make permanent changes to your bash/ranger/qutebrowser configs, make them to the base files which you can access with "cfb," "cfr," and "cfq," respectively, then press Mod+R. + +# Explore and customize + +Don't like something about the rice? Change it. If you have a problem, try figuring it out yourself, but if you can't, ask on my YouTube or elsewhere. + +Considering how formidable my rice might seem to greener Linux users, I hope between my vids and my documentation, you can tweak exactly what you want from it. diff --git a/.config/Scripts/luke_guide.pdf b/.config/Scripts/luke_guide.pdf new file mode 100644 index 0000000000000000000000000000000000000000..e7857eb6ce43fa8125ed82b4b38ee34ad7871231 GIT binary patch literal 37847 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

  • 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 YS;f%F< +# Copyright (c) 2014 Maarten de Vries +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# Usage: +# Set your font in ~/.Xresources: +# urxvt.font: xft:Inconsolata:pixelsize=12 +# to set it with pixels or +# urxvt.font: xft:Inconsolata:size=12 +# to set it with points. + +# And re-bind some keymappings (if you want, below are the defaults): +# URxvt.keysym.C-minus: resize-font:smaller +# URxvt.keysym.C-plus: resize-font:bigger +# URxvt.keysym.C-equal: resize-font:reset +# URxvt.keysym.C-question: resize-font:show +# + +my @fonts = ( + {'name' => 'font', 'code' => 710}, + {'name' => 'boldFont', 'code' => 711}, + {'name' => 'italicFont', 'code' => 712}, + {'name' => 'boldItalicFont', 'code' => 713}, +); + +my @fixed = qw(4x6 5x7 5x8 6x9 6x10 6x12 6x13 7x13 7x14 8x13 8x16 9x15 9x18 10x20 12x24); + +sub on_start { + my ($self) = @_; + + foreach (@fonts) { + $_->{'default'} = $self->resource($_->{'name'}); + } +} + +sub on_init { + my ($self) = @_; + my $commands = { + "smaller" => "C-minus", + "bigger" => "C-plus", + "reset" => "C-equal", + "show" => "C-question", + }; + bind_hotkeys($self, $commands); + + () +} + +sub bind_hotkeys { + my ($self, $commands) = @_; + + for (keys %$commands) { + my $hotkey = $$commands{$_}; + my $hotkey_bound = $self->{'term'}->x_resource("keysym.$hotkey"); + if (!defined($hotkey_bound) ) { + # Support old-style key bindings + if ($self->x_resource("%.$_")) { + $hotkey = $self->x_resource("%.$_"); + } + + # FIXME If we're bound to a keysym, don't bind the default. + $self->bind_action($hotkey, "%:$_") or + warn "unable to register '$hotkey' as hotkey for $_"; + } + else { + if ($hotkey_bound !~ /^resize-font:/) { + warn "Hotkey $$commands{$_} already bound to $hotkey_bound, not binding to resize-font:$_ by default."; + } + } + } +} + +sub on_action { + my ($self, $string) = @_; + my $regex = qr"(?!pixelsize=)(\d+)"; + + if ($string eq "bigger") { + foreach (@fonts) { + next if not defined($_->{'default'}); + update_font_size($self, $_, +2); + } + } + elsif ($string eq "smaller") { + foreach (@fonts) { + next if not defined($_->{'default'}); + update_font_size($self, $_, -2); + } + } + elsif ($string eq "reset") { + foreach (@fonts) { + next if not defined($_->{'default'}); + set_font($self, $_, $_->{'default'}); + } + } + elsif ($string eq "show") { + + my $term = $self->{'term'}; + $term->{'resize-font'}{'overlay'} = { + ov => $term->overlay_simple(0, -1, format_font_info($self)), + to => urxvt::timer + ->new + ->start(urxvt::NOW + 1) + ->cb(sub { + delete $term->{'resize-font'}{'overlay'}; + }), + }; + } + + () +} + +sub get_font { + my ($self, $name) = @_; + return $self->resource($name); +} + +sub set_font { + my ($self, $font, $new) = @_; + $self->cmd_parse(sprintf("\33]%d;%s\007", $font->{'code'}, $new)); +} + +sub update_font_size { + my ($self, $font, $delta) = @_; + my $regex = qr"(?<=size=)(\d+)"; + my $current = get_font($self, $font->{'name'}); + + my ($index) = grep { $fixed[$_] eq $current } 0..$#fixed; + if ($index or $index eq 0) { + my $inc = $delta / abs($delta); + $index += $inc; + if ($index < 0) { $index = 0; } + if ($index > $#fixed) { $index = $#fixed; } + $current = $fixed[$index]; + } + else { + $current =~ s/$regex/$1+$delta/ge; + } + set_font($self, $font, $current); +} + +sub format_font_info { + my ($self) = @_; + + my $width = 0; + foreach (@fonts) { + my $length = length($_->{'name'}); + $width = $length > $width ? $length : $width; + } + ++$width; + + my $info = ''; + foreach (@fonts) { + $info .= sprintf("%-${width}s %s\n", $_->{'name'} . ':', get_font($self, $_->{'name'})); + } + + return $info; +} diff --git a/.config/Scripts/screencast.sh b/.config/Scripts/screencast.sh new file mode 100644 index 00000000..0401da62 --- /dev/null +++ b/.config/Scripts/screencast.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +#This is the ffmpeg command that the screencast shortcut in i3 will run. + +#Picks a file name for the output file based on availability: + +if [[ -f ~/output.mkv ]] + then + n=1 + while [[ -f $HOME/output_$n.mkv ]] + do + n=$((n+1)) + done + filename="$HOME/output_$n.mkv" + else + filename="$HOME/output.mkv" +fi + +#The actual ffmpeg command: + +ffmpeg -y \ +-f x11grab \ +-s $(xdpyinfo | grep dimensions | awk '{print $2;}') \ +-i :0.0 \ +-thread_queue_size 1024 \ + -f alsa -ar 44100 -i hw:1 \ +-af "volume=12" \ + -c:v libx264 -r 30 -c:a flac $filename + #-c:v ffvhuff -r 30 -c:a flac $filename diff --git a/.config/Scripts/shortcuts.py b/.config/Scripts/shortcuts.py new file mode 100644 index 00000000..154d208d --- /dev/null +++ b/.config/Scripts/shortcuts.py @@ -0,0 +1,41 @@ +import csv + +qute = "" +rang = "" +bash = "" + +with open(".config/qutebrowser/keys.conf.base") as qb: + qute+=qb.read() +with open(".config/ranger/rc.conf.base") as rg: + rang+=rg.read() +with open(".config/Scripts/bashrc") as bsh: + bash+=bsh.read() + +#First we open the list of folder shortcuts and go down each line adding each in the required syntax to each of the three configs: + +with open(".config/Scripts/folders") as fold: + for line in csv.reader(fold, dialect="excel-tab"): + #Adds the qutebrowser downloads commands: + qute+="set storage download-directory "+line[1]+" ;; hint links download\n\t;"+line[0]+"\n" + #Adds the ranger go, tab, move and yank commands: + rang+=("map g"+line[0]+" cd "+line[1]+"\n") + rang+=("map t"+line[0]+" tab_new "+line[1]+"\n") + rang+=("map m"+line[0]+" shell mv %s "+line[1]+"\n") + rang+=("map Y"+line[0]+" shell cp -r %s "+line[1]+"\n") + #Adds the bash shortcuts: + bash+=("alias "+line[0]+"=\"cd "+line[1]+" && ls -a\"\n") + +#Goes thru the config file file and adds the shortcuts to both bash and ranger. + +with open(".config/Scripts/configs") as conf: + for line in csv.reader(conf, dialect="excel-tab"): + bash+=("alias "+line[0]+"=\"vim "+line[1]+"\"\n") + rang+=("map "+line[0]+" shell vim "+line[1]+"\n") + + +with open(".config/ranger/rc.conf", "w") as outrang: + outrang.write(rang) +with open(".config/qutebrowser/keys.conf","w") as outqb: + outqb.write(qute) +with open(".bashrc","w") as outbash: + outbash.write(bash) diff --git a/.config/Scripts/shrinkvid.sh b/.config/Scripts/shrinkvid.sh new file mode 100755 index 00000000..2ac63d34 --- /dev/null +++ b/.config/Scripts/shrinkvid.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +sex=$(ffprobe -i $1 -show_entries format=duration -v quiet -of csv="p=0") +rate=$(qalc -t 1000000000 / $sex) +#rate=$(( 1000000000 / $sex )) + +base=$(basename $1) +ext="${base##*.}" +base="${base%.*}" + +ffmpeg -i $1 -b $rate $base'_min.'$ext +echo "sex is $sex" +echo "rate is $rate" diff --git a/.config/Scripts/speedvid.sh b/.config/Scripts/speedvid.sh new file mode 100755 index 00000000..ba3a4f6c --- /dev/null +++ b/.config/Scripts/speedvid.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +base=$(basename $1) +ext="${base##*.}" +base="${base%.*}" + +ffmpeg -i $1 -vf "setpts=$2*PTS" -an $base'_sped.'$ext diff --git a/.config/Scripts/status.sh b/.config/Scripts/status.sh new file mode 100644 index 00000000..9952f951 --- /dev/null +++ b/.config/Scripts/status.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +tmux -f ~/.config/Scripts/tmux.conf new-session -s "status" -d +tmux split-window -v "htop" +tmux split-window -h "speedometer -r wlp2s0" +tmux split-window -v "speedometer -r enp0s25" +tmux select-pane -t 0 +tmux split-window -h "bash ~/.config/Scripts/mailsyncloop.sh" +tmux select-pane -t 0 +tmux resize-pane -R 30 +tmux -2 attach-session -d diff --git a/.config/Scripts/test.sh b/.config/Scripts/test.sh new file mode 100755 index 00000000..284192ae --- /dev/null +++ b/.config/Scripts/test.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +ffmpeg \ +-f pulse -ac 2 -ar 48000 -i alsa_output.pci-0000_00_1b.0.analog-stereo.monitor \ +-f pulse -ac 1 -ar 44100 -i alsa_input.usb-Blue_Microphones_Yeti_Stereo_Microphone_REV8-00.analog-stereo \ +-filter_complex amix=inputs=2 \ +-f x11grab -r 30 -s 1280x800 -i :0.0+0,0 \ +-vcodec libx264 -preset fast -crf 18 \ +-acodec libmp3lame -ar 44100 -q:a 1 \ +-pix_fmt yuv420p \ +this_output.mkv diff --git a/.config/Scripts/tmux.conf b/.config/Scripts/tmux.conf new file mode 100644 index 00000000..f255afaa --- /dev/null +++ b/.config/Scripts/tmux.conf @@ -0,0 +1,177 @@ +############################## +# _ +# | |_ _ __ ___ _ ___ __ +# | __| '_ ` _ \| | | \ \/ / +# | |_| | | | | | |_| |> < +# \__|_| |_| |_|\__,_/_/\_\ +# +############################# +# +# COPY AND PASTE +# http://robots.thoughtbot.com/tmux-copy-paste-on-os-x-a-better-future +# +# Use vim keybindings in copy mode +setw -g mode-keys vi + +# Setup 'v' to begin selection as in Vim +#bind-key -t vi-copy v begin-selection +#bind-key -t vi-copy y copy-pipe "reattach-to-user-namespace pbcopy" + +# Update default binding of `Enter` to also use copy-pipe +# unbind -t vi-copy Enter +# bind-key -t vi-copy Enter copy-pipe "reattach-to-user-namespace pbcopy" +# +############################################################################ +############################################################################ +# Reset Prefix +############################################################################ +set -g prefix C-a +bind-key a send-prefix # for nested tmux sessions + +############################################################################ +# Global options +############################################################################ +# large history +set-option -g history-limit 10000 + +# colors +setw -g mode-bg black +set-option -g default-terminal "screen-256color" #"xterm-256color" # "screen-256color" +set-option -g pane-active-border-fg green + +# utf8 support +#set-window-option -g utf8 on + + +# basic settings +set-window-option -g xterm-keys on # for vim +set-window-option -g mode-keys vi # vi key +set-window-option -g monitor-activity on +set-window-option -g window-status-current-fg white +setw -g window-status-current-attr reverse + +# Automatically set window title +setw -g automatic-rename + +# use mouse # More on mouse support http://floriancrouzat.net/2010/07/run-tmux-with-mouse-support-in-mac-os-x-terminal-app/ +#setw -g mode-mouse on +#setw -g mouse-resize-pane on +#set -g mouse-select-window on +#set -g mouse-select-pane on +set -g mouse on +set -g history-limit 30000 +set -g terminal-overrides 'xterm*:smcup@:rmcup@' + +# vi movement keys +# set-option -g status-keys vi + +############################################################################ +# Status Bar +############################################################################ +#set-option -g status-utf8 on +set-option -g status-justify right +set-option -g status-bg black # colour213 # pink +set-option -g status-fg cyan +set-option -g status-interval 5 +set-option -g status-left-length 30 +set-option -g status-left '#[fg=magenta]» #[fg=blue,bold]#T#[default]' +set-option -g status-right '#[fg=red,bold][[ #(git branch) branch ]] #[fg=cyan]»» #[fg=blue,bold]###S #[fg=magenta]%R %m-%d#(acpi | cut -d ',' -f 2)#[default]' +set-option -g visual-activity on + +# Titles (window number, program name, active (or not) +set-option -g set-titles on +set-option -g set-titles-string '#H:#S.#I.#P #W #T' + + +############################################################################ +# Unbindings +############################################################################ +#unbind [ # copy mode bound to escape key +unbind j +unbind C-b # unbind default leader key +unbind '"' # unbind horizontal split +unbind % # unbind vertical split + + +############################################################################ +# Bindings +############################################################################ +# reload tmux conf +bind-key r source-file ~/.tmux.conf + +#bind Escape copy-mode + +# new split in current pane (horizontal / vertical) +bind-key c split-window -v # split pane horizontally +bind-key v split-window -h # split pane vertically + +# list panes +bind-key Space list-panes + +# break-pane +bind-key Enter break-pane + +# join-pane [-dhv] [-l size | -p percentage] [-s src-pane] +# [-t:dst-window.dst-pane] (destination window (dot) destination pane +# (alias: joinp) +# +#bind C-j command-prompt "joinp" +#bind C-j command-prompt "join-pane" +#bind-key j command-prompt "join-pane -s '%%'" +#bind-key j command-prompt "joinp -t:0" +bind-key Space command-prompt "joinp -t:%%" # %% = prompt for window.pane [-V|H] # vert|hor split + +#previous pane +bind-key -n C-up prev +bind-key -n C-left prev + +#next pane +bind-key -n C-right next +bind-key -n C-down next + +############################################################################ +# windows +############################################################################ +set-window-option -g window-status-current-bg red +bind C-j previous-window +bind C-k next-window +bind-key C-a last-window # C-a C-a for last active window +bind A command-prompt "rename-window %%" +# By default, all windows in a session are constrained to the size of the +# smallest client connected to that session, +# even if both clients are looking at different windows. +# It seems that in this particular case, Screen has the better default +# where a window is only constrained in size if a smaller client +# is actively looking at it. +setw -g aggressive-resize on + +############################################################################ +# panes +############################################################################ +# Navigation --------------------------------------------------------------- +# use the vim motion keys to move between panes +bind-key h select-pane -L +bind-key j select-pane -D +bind-key k select-pane -U +bind-key l select-pane -R + +# Resizing --------------------------------------------------------------- +bind-key C-h resize-pane -L +bind-key C-j resize-pane -D +bind-key C-k resize-pane -U +bind-key C-l resize-pane -R + +# use vim motion keys while in copy mode +setw -g mode-keys vi + +############################################################################ +# layouts +############################################################################ +bind o select-layout "active-only" +bind M-- select-layout "even-vertical" +bind M-| select-layout "even-horizontal" +bind M-r rotate-window + + +# focus on first window +# select-window -t 0 diff --git a/.config/Scripts/transmission.sh b/.config/Scripts/transmission.sh new file mode 100755 index 00000000..794f6bb6 --- /dev/null +++ b/.config/Scripts/transmission.sh @@ -0,0 +1,7 @@ +#!/bin/bash +if [ -f $(pgrep transmission) ]; + then + urxvt -e transmission-remote-cli + else + transmission-daemon && urxvt -e transmission-remote-cli +fi diff --git a/.config/Scripts/unmount.sh b/.config/Scripts/unmount.sh new file mode 100755 index 00000000..e43053af --- /dev/null +++ b/.config/Scripts/unmount.sh @@ -0,0 +1,4 @@ +#!/bin/bash +sync +umount /mnt/* 2> /dev/null +rm -d /mnt/* 2> /dev/null diff --git a/.config/Scripts/video.sh b/.config/Scripts/video.sh new file mode 100644 index 00000000..6141b407 --- /dev/null +++ b/.config/Scripts/video.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +#This is the ffmpeg command that the screencast shortcut in i3 will run. + +#Picks a file name for the output file based on availability: + +if [[ -f ~/output.mkv ]] + then + n=1 + while [[ -f ~/output_$n.mkv ]] + do + n=$((n+1)) + done + filename="output_$n.mkv" + else + filename="output.mkv" +fi + +#The actual ffmpeg command: + +ffmpeg -y \ +-f x11grab \ +-s $(xdpyinfo | grep dimensions | awk '{print $2;}') \ +-i :0.0 \ + -c:v libx264 -qp 0 -r 30 $filename + #-c:v ffvhuff -r 30 -c:a flac $filename diff --git a/.config/Scripts/vimrc b/.config/Scripts/vimrc new file mode 100644 index 00000000..f12d2f65 --- /dev/null +++ b/.config/Scripts/vimrc @@ -0,0 +1,166 @@ +execute pathogen#infect() +set number +set relativenumber +set so=10 + + +"""BASIC TOOLS +"Navigating with guides +inoremap /<++>"_c4l +vnoremap /<++>"_c4l +map /<++>"_c4l +inoremap ;gui <++> +"For normal mode when in terminals (in X I have caps mapped to esc, this replaces it when I don't have X) +inoremap jw +inoremap wj +inoremap +"For split navigation +map h +map j +map k +map l + +set nocompatible +filetype plugin on + +"""LATEX +autocmd FileType tex inoremap ;fr \begin{frame}\frametitle{}<++>\end{frame}<++>6kf}i +autocmd FileType tex inoremap ;fi \begin{fitch}\end{fitch}<++>3kA +autocmd FileType tex inoremap ;exe \begin{exe}\ex\end{exe}<++>3kA +autocmd FileType tex inoremap ;em \emph{}<++>T{i +autocmd FileType tex inoremap ;bf \textbf{}<++>T{i +autocmd FileType tex inoremap ;it \textit{}<++>T{i +autocmd FileType tex inoremap ;ct \textcite{}<++>T{i +autocmd FileType tex inoremap ;cp \parencite{}<++>T{i +autocmd FileType tex inoremap ;glos {\gll<++>\\<++>\\\trans{``<++>''}}2k2bcw +autocmd FileType tex inoremap ;x \begin{xlist}\ex\end{xlist}kA +autocmd FileType tex inoremap ;ol \begin{enumerate}\end{enumerate}<++>3kA\item +autocmd FileType tex inoremap ;ul \begin{itemize}\end{itemize}<++>3kA\item +autocmd FileType tex inoremap ;li \item +autocmd FileType tex inoremap ;ref \ref{}<++>T{i +autocmd FileType tex inoremap ;tab \begin{tabular}<++>\end{tabular}<++>4kA{}i +autocmd FileType tex inoremap ;ot \begin{tableau}\inp{<++>}\const{<++>}<++><++>\end{tableau}<++>5kA{}i +autocmd FileType tex inoremap ;can \cand{}<++>T{i +autocmd FileType tex inoremap ;con \const{}<++>T{i +autocmd FileType tex inoremap ;v \vio{}<++>T{i +autocmd FileType tex inoremap ;a \href{}{<++>}<++>2T{i +autocmd FileType tex inoremap ;sc \textsc{}<++>T{i +autocmd FileType tex inoremap ;sec \section{}<++>2kf}i +autocmd FileType tex inoremap ;ssec \subsection{}<++>2kf}i +autocmd FileType tex inoremap ;sssec \subsubsection{}<++>2kf}i +autocmd FileType tex inoremap ;st F{i*f}i +autocmd FileType tex inoremap ;beg \begin{%DELRN%}<++>\end{%DELRN%}<++>4kfR:MultipleCursorsFind%DELRN%c +"autocmd FileType tex inoremap ;up \usepackage{}i +autocmd FileType tex inoremap ;up /usepackageo\usepackage{}i +autocmd FileType tex nnoremap ;up /usepackageo\usepackage{}i +autocmd FileType tex inoremap ;tt \texttt{}<++>T{i +autocmd FileType tex inoremap ;bt {\blindtext} +autocmd FileType tex inoremap ;nu $\varnothing$ +autocmd FileType tex inoremap ;col \begin{columns}[T]\begin{column}{.5\textwidth}\end{column}\begin{column}{.5\textwidth}<++>\end{column}\end{columns}5kA +autocmd FileType tex inoremap ;rn (\ref{})<++>F}i +"""END + +"""Logical Symbols +autocmd FileType tex inoremap ;m $$<++>2T$i +autocmd FileType tex inoremap ;M $$$$<++>2k$hi +autocmd FileType tex inoremap ;neg {\neg} +autocmd FileType tex inoremap ;V {\vee} +autocmd FileType tex inoremap ;or {\vee} +autocmd FileType tex inoremap ;L {\wedge} +autocmd FileType tex inoremap ;and {\wedge} +autocmd FileType tex inoremap ;ra {\rightarrow} +autocmd FileType tex inoremap ;la {\leftarrow} +autocmd FileType tex inoremap ;lra {\leftrightarrow} +autocmd FileType tex inoremap ;fa {\forall} +autocmd FileType tex inoremap ;ex {\exists} +autocmd FileType tex inoremap ;dia {\Diamond} +autocmd FileType tex inoremap ;box {\Box} +"""END + +autocmd Filetype tex inoremap ;nom {\textsc{nom}} +autocmd FileType tex inoremap ;acc {\textsc{acc}} +autocmd FileType tex inoremap ;dat {\textsc{dat}} +autocmd FileType tex inoremap ;gen {\textsc{gen}} +autocmd FileType tex inoremap ;abl {\textsc{abl}} +autocmd FileType tex inoremap ;voc {\textsc{voc}} +autocmd FileType tex inoremap ;loc {\textsc{loc}} +autocmd Filetype tex inoremap ;inst {\textsc{inst}} +"autocmd FileType tex inoremap ; + +"""IPA +autocmd FileType tex inoremap ;tipa \textipa{}<++>T{i +autocmd FileType tex inoremap ;ae {\ae} +autocmd FileType tex inoremap ;A {\textscripta} +autocmd FileType tex inoremap ;dh {\dh} +autocmd FileType tex inoremap ;yogh {\textyogh} +autocmd FileType tex inoremap ;j {\textdyoghlig} +autocmd FileType tex inoremap ;uh {\textschwa} +autocmd FileType tex inoremap ;eps {\textepsilon} +autocmd FileType tex inoremap ;gam {\textgamma} +autocmd FileType tex inoremap ;I {\textsci} +autocmd FileType tex inoremap ;sh {\textesh} +autocmd FileType tex inoremap ;th {\texttheta} +autocmd FileType tex inoremap ;Th {\textthorn} +autocmd FileType tex inoremap ;TH {\textthorn} +autocmd FileType tex inoremap ;ups {\textupsilon} +autocmd FileType tex inoremap ;ph {\textphi} +autocmd FileType tex inoremap ;om {\textomega} +autocmd FileType tex inoremap ;sig {\textsigma} +autocmd FileType tex inoremap ;oe {\oe} +autocmd FileType tex inoremap ;ng {\ng} +autocmd FileType tex inoremap ;au {\textopeno} +autocmd FileType tex inoremap ;O {\textopeno} +autocmd FileType tex inoremap ;glot {\textglotstop} +autocmd FileType tex inoremap ;ch {\textteshlig} +"""END + +"""HTML +autocmd FileType html inoremap ;b <++>FbT>i +autocmd FileType html inoremap ;i <++>FeT>i +autocmd FileType html inoremap ;1

    <++>2kf<++>2kf<++>2kf

    <++>02kf>a +autocmd FileType html inoremap ;a href=""><++>
    <++>F"i +autocmd FileType html inoremap ;ul
    <++>03kfo
  • F>a +autocmd FileType html inoremap ;ol
    <++>03kfauthor="<++>",year="<++>",title="<++>",journal="<++>",volume="<++>",pages="<++>",}<++>8kA,i +autocmd FileType bib inoremap ;b @book{author="<++>",year="<++>",title="<++>",publisher="<++>",}<++>6kA,i +autocmd FileType bib inoremap ;c @incollection{author="<++>",title="<++>",booktitle="<++>",editor="<++>",year="<++>",publisher="<++>",}<++>8kA,i +"""END + +let g:instant_markdown_autostart = 0 + +autocmd Filetype markdown inoremap ;n --- +autocmd Filetype markdown inoremap ;b ****<++>F*hi +autocmd Filetype markdown inoremap ;s ~~~~<++>F~hi +autocmd Filetype markdown inoremap ;e **<++>F*i +autocmd Filetype markdown inoremap ;h ====<++>F=hi +autocmd Filetype markdown inoremap ;i ![](<++>)<++>F[a +autocmd Filetype markdown inoremap ;a [](<++>)<++>F[a +autocmd Filetype markdown inoremap ;1 #<++>kA +autocmd Filetype markdown inoremap ;2 ##<++>kA +autocmd Filetype markdown inoremap ;3 ###<++>kA +autocmd Filetype markdown inoremap ;l -------- +autocmd Filetype markdown nnoremap :!pandoc-tbeamer-s%-o%pdf-Vtheme:Copenhagen--latex-engine=xelatex + + +syntax on + +"au BufWinLeave * mkview +"au BufWinEnter * silent loadview + +colorscheme slate + +let g:vimwiki_ext2syntax = {'.md': 'markdown', '.markdown': 'markdown', '.mdown': 'markdown'} +"Spell-check set to F6 +map :setlocal spell! spelllang=en_us +map :Goyo + +set wildmode=longest,list,full +set wildmenu diff --git a/.config/Scripts/wall_looper.sh b/.config/Scripts/wall_looper.sh new file mode 100644 index 00000000..a1a300b5 --- /dev/null +++ b/.config/Scripts/wall_looper.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +#Feed this script two arguments, $1 for a folder directory and $2 for a time interval in seconds. + +#Will loop thru the files in the directory, using feh to change the background every $2 seconds. + +while [ 1==1 ] + +do +for file in $1*; +do +sleep $2 +feh --bg-scale "$file" +cp "$file" ~/.config/wall.png +done +done diff --git a/.config/calcurse/conf b/.config/calcurse/conf new file mode 100644 index 00000000..e829390b --- /dev/null +++ b/.config/calcurse/conf @@ -0,0 +1,25 @@ +appearance.calendarview=monthly +appearance.compactpanels=no +appearance.defaultpanel=calendar +appearance.layout=1 +appearance.notifybar=yes +appearance.sidebarwidth=1 +appearance.theme=red on default +appearance.todoview=show-completed +daemon.enable=no +daemon.log=no +format.inputdate=1 +format.notifydate=%a %F +format.notifytime=%T +format.outputdate=%D +general.autogc=no +general.autosave=yes +general.confirmdelete=yes +general.confirmquit=no +general.firstdayofweek=sunday +general.periodicsave=0 +general.progressbar=yes +general.systemdialogs=no +notification.command=printf '\a' +notification.notifyall=flagged-only +notification.warning=300 diff --git a/.config/calcurse/keys b/.config/calcurse/keys new file mode 100644 index 00000000..f188702a --- /dev/null +++ b/.config/calcurse/keys @@ -0,0 +1,70 @@ +# +# Calcurse keys configuration file +# +# This file sets the keybindings used by Calcurse. +# Lines beginning with "#" are comments, and ignored by Calcurse. +# To assign a keybinding to an action, this file must contain a line +# with the following syntax: +# +# ACTION KEY1 KEY2 ... KEYn +# +# Where ACTION is what will be performed when KEY1, KEY2, ..., or KEYn +# will be pressed. +# +# To define bindings which use the CONTROL key, prefix the key with 'C-'. +# The escape, space bar and horizontal Tab key can be specified using +# the 'ESC', 'SPC' and 'TAB' keyword, respectively. +# Arrow keys can also be specified with the UP, DWN, LFT, RGT keywords. +# Last, Home and End keys can be assigned using 'KEY_HOME' and 'KEY_END' +# keywords. +# +# A description of what each ACTION keyword is used for is available +# from calcurse online configuration menu. + +generic-cancel ESC +generic-select SPC +generic-credits @ +generic-help ? +generic-quit q Q +generic-save s S ^S +generic-reload R +generic-copy c +generic-paste p ^V +generic-change-view TAB +generic-import i I +generic-export x X +generic-goto g G +generic-other-cmd o O +generic-config-menu C +generic-redraw ^R +generic-add-appt ^A +generic-add-todo ^T +generic-prev-day T ^H +generic-next-day t ^L +generic-prev-week W ^K +generic-next-week w ^J +generic-prev-month M +generic-next-month m +generic-prev-year Y +generic-next-year y +generic-scroll-down ^N +generic-scroll-up ^P +generic-goto-today ^G +generic-command : +move-right l L RGT +move-left h H LFT +move-down j J DWN +move-up k K UP +start-of-week 0 +end-of-week $ +add-item a A +del-item d D +edit-item e E +view-item v V +pipe-item | +flag-item ! +repeat r +edit-note n N +view-note > +raise-priority + +lower-priority - diff --git a/.config/compton.conf b/.config/compton.conf new file mode 100644 index 00000000..8ad5733c --- /dev/null +++ b/.config/compton.conf @@ -0,0 +1,56 @@ +# vim: filetype=conf +backend = "glx"; +paint-on-overlay = true; +glx-no-stencil = true; +glx-no-rebind-pixmap = true; +vsync = "opengl-swc"; + +# These are important. The first one enables the opengl backend. The last one is the vsync method. Depending on the driver you might need to use a different method. +# The other options are smaller performance tweaks that work well in most cases. +# You can find the rest of the options here: https://github.com/chjj/compton/wiki/perf-guide, and here: https://github.com/chjj/compton/wiki/vsync-guide + + +# Shadow +shadow = true; # Enabled client-side shadows on windows. +no-dock-shadow = true; # Avoid drawing shadows on dock/panel windows. +no-dnd-shadow = true; # Don't draw shadows on DND windows. +clear-shadow = true; # Zero the part of the shadow's mask behind the window (experimental). +shadow-radius = 7; # The blur radius for shadows. (default 12) +shadow-offset-x = -7; # The left offset for shadows. (default -15) +shadow-offset-y = -7; # The top offset for shadows. (default -15) +shadow-exclude = [ + "! name~=''", + "n:e:Notification", + "n:e:Plank", + "n:e:Docky", + "g:e:Synapse", + "g:e:Kupfer", + "g:e:WC_Cronograph", + "g:e:Conky", + "n:w:*Firefox*", + "n:w:*Chrome*", + "n:w:*Chromium*", + "class_g ?= 'Notify-osd'", + "class_g ?= 'Cairo-dock'", + "class_g ?= 'Xfce4-notifyd'", + "class_g ?= 'Xfce4-power-manager'" +]; + +# The shadow exclude options are helpful if you have shadows enabled. Due to the way compton draws its shadows, certain applications will have visual glitches +# (most applications are fine, only apps that do weird things with xshapes or argb are affected). +# This list includes all the affected apps I found in my testing. The "! name~=''" part excludes shadows on any "Unknown" windows, this prevents a visual glitch with the XFWM alt tab switcher. + +# Fading +fading = false; # Fade windows during opacity changes. +fade-delta = 4; # The time between steps in a fade in milliseconds. (default 10). +fade-in-step = 0.03; # Opacity change between steps while fading in. (default 0.028). +fade-out-step = 0.03; # Opacity change between steps while fading out. (default 0.03). +#no-fading-openclose = true; # Fade windows in/out when opening/closing + +detect-client-opacity = true; # This prevents opacity being ignored for some apps. For example without this enabled my xfce4-notifyd is 100% opacity no matter what. + +# Window type settings +wintypes: +{ + tooltip = { fade = true; shadow = false; }; +}; diff --git a/.config/feh/keys b/.config/feh/keys new file mode 100755 index 00000000..b5e38725 --- /dev/null +++ b/.config/feh/keys @@ -0,0 +1,12 @@ +zoom_in K XF86Forward +zoom_out J XF86Back +next_img L space +prev_img BackSpace H + +scroll_up k Up +scroll_down j Down +scroll_right l Right +scroll_left h Left + +toggle_fullscreen f +save_filelist F diff --git a/.config/fontconfig/fonts.conf b/.config/fontconfig/fonts.conf new file mode 100755 index 00000000..759bba48 --- /dev/null +++ b/.config/fontconfig/fonts.conf @@ -0,0 +1,24 @@ + + + + + + serif + Tinos + + + sans-serif + Arimo + + + sans + Arimo + + + monospace + Tamsyn + + + + + diff --git a/.config/i3/bar/.codeclimate.yml b/.config/i3/bar/.codeclimate.yml new file mode 100644 index 00000000..ec808744 --- /dev/null +++ b/.config/i3/bar/.codeclimate.yml @@ -0,0 +1,15 @@ +engines: + duplication: + enabled: true + config: + languages: + - python + fixme: + enabled: true + radon: + enabled: true +ratings: + paths: + - "**.py" +exclude_paths: +- tests/ diff --git a/.config/i3/bar/.coveragerc b/.config/i3/bar/.coveragerc new file mode 100644 index 00000000..e8097148 --- /dev/null +++ b/.config/i3/bar/.coveragerc @@ -0,0 +1,10 @@ +[run] +omit = + tests/* + *mock* + *funcsigs* + *pbr* + *six* + /usr/lib* + +[report] diff --git a/.config/i3/bar/.gitignore b/.config/i3/bar/.gitignore new file mode 100644 index 00000000..d7d9fd4a --- /dev/null +++ b/.config/i3/bar/.gitignore @@ -0,0 +1,91 @@ +*swp + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover +.hypothesis/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# IPython Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# dotenv +.env + +# virtualenv +venv/ +ENV/ + +# Spyder project settings +.spyderproject + +# Rope project settings +.ropeproject diff --git a/.config/i3/bar/.travis.yml b/.config/i3/bar/.travis.yml new file mode 100644 index 00000000..0e198adb --- /dev/null +++ b/.config/i3/bar/.travis.yml @@ -0,0 +1,18 @@ +language: python +python: + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" +install: + - pip install psutil + - pip install netifaces + - pip install -U coverage==4.3 + - pip install codeclimate-test-reporter +script: + - nosetests -v --with-coverage --cover-erase tests/ + - CODECLIMATE_REPO_TOKEN=40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf codeclimate-test-reporter +addons: + code_climate: + repo_token: 40cb00907f7a10e04868e856570bb997ab9c42fd3b63d980f2b2269433195fdf diff --git a/.config/i3/bar/LICENSE b/.config/i3/bar/LICENSE new file mode 100644 index 00000000..b472f2e8 --- /dev/null +++ b/.config/i3/bar/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016 tobi-wan-kenobi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/.config/i3/bar/README.md b/.config/i3/bar/README.md new file mode 100644 index 00000000..3570c337 --- /dev/null +++ b/.config/i3/bar/README.md @@ -0,0 +1,134 @@ +# bumblebee-status + +[![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 = -m -p -t +} +``` + +You can retrieve a list of modules and themes by entering: +``` +$ cd bumblebee-status +$ ./bumblebee-status -l themes +$ ./bumblebee-status -l modules +``` + +To change the update interval, use: +``` +$ ./bumblebee-status -m -p interval= +``` + +As a simple example, this is what my i3 configuration looks like: + +``` +bar { + font pango:Inconsolata 10 + position top + tray_output none + status_command ~/.i3/bumblebee-status/bumblebee-status -m nic disk:root cpu memory battery date time pasink pasource dnf -p root.path=/ time.format="%H:%M CW %V" date.format="%a, %b %d %Y" -t solarized-powerline +} + +``` + +Restart i3wm and - that's it! + +## Errors +If errors occur, you should see them in the i3bar itself. If that does not work, or you need more information for troubleshooting, you can activate a debug log using the `-d` or `--debug` switch: + +``` +$ ./bumblebee-status -d -m +``` + +This will create a file called `debug.log` in the same directory as the executable `bumblebee-status`. + +# Required Modules + +Modules and commandline utilities are only required for modules, the core itself has no external dependencies at all. + +* psutil (for the modules 'cpu', 'memory', 'traffic') +* netifaces (for the module 'nic', 'traffic') +* requests (for the module 'weather') + +# Required commandline utilities + +* xbacklight (for the module 'brightness') +* xset (for the module 'caffeine') +* notify-send (for the module 'caffeine') +* cmus-remote (for the module 'cmus') +* dnf (for the module 'dnf') +* gpmdp-remote (for the module 'gpmdp') +* setxkbmap (for the module 'layout') +* fakeroot (for the module 'pacman') +* pacman (for the module 'pacman') +* pactl (for the module 'pulseaudio') +* ping (for the module 'ping') +* redshift (for the module 'redshift') +* xrandr (for the module 'xrandr') +* sensors (for the module 'sensors') + +# Examples +Here are some screenshots for all themes that currently exist: + +:exclamation: Some themes (all 'Powerline' themes) require [Font Awesome](http://fontawesome.io/) and a powerline-compatible font ([powerline-fonts](https://github.com/powerline/fonts), for example) to display all icons correctly. + +Gruvbox Powerline (`-t gruvbox-powerline`) (contributed by [@paxy97](https://github.com/paxy97)): + +![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/.config/i3/bar/bin/load-i3-bars.sh b/.config/i3/bar/bin/load-i3-bars.sh new file mode 100755 index 00000000..cbe0e952 --- /dev/null +++ b/.config/i3/bar/bin/load-i3-bars.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +if [ ! -f ~/.i3/config.template ]; then + cp ~/.i3/config ~/.i3/config.template +else + cp ~/.i3/config.template ~/.i3/config +fi + +screens=$(xrandr -q|grep ' connected'| grep -P '\d+x\d+' |cut -d' ' -f1) + +echo "screens: $screens" + +while read -r line; do + screen=$(echo $line | cut -d' ' -f1) + others=$(echo $screens|tr ' ' '\n'|grep -v $screen|tr '\n' '-'|sed 's/.$//') + + if [ -f ~/.i3/config.$screen-$others ]; then + cat ~/.i3/config.$screen-$others >> ~/.i3/config + else + if [ -f ~/.i3/config.$screen ]; then + cat ~/.i3/config.$screen >> ~/.i3/config + fi + fi +done <<< "$screens" + +i3-msg restart diff --git a/.config/i3/bar/bin/pacman-updates b/.config/i3/bar/bin/pacman-updates new file mode 100755 index 00000000..baf0b93b --- /dev/null +++ b/.config/i3/bar/bin/pacman-updates @@ -0,0 +1,22 @@ +#!/usr/bin/bash + +if ! type -P fakeroot >/dev/null; then + error 'Cannot find the fakeroot binary.' + exit 1 +fi + +if [[ -z $CHECKUPDATES_DB ]]; then + CHECKUPDATES_DB="${TMPDIR:-/tmp}/checkup-db-${USER}/" +fi + +trap 'rm -f $CHECKUPDATES_DB/db.lck' INT TERM EXIT + +DBPath="${DBPath:-/var/lib/pacman/}" +eval $(awk -F' *= *' '$1 ~ /DBPath/ { print $1 "=" $2 }' /etc/pacman.conf) + +mkdir -p "$CHECKUPDATES_DB" +ln -s "${DBPath}/local" "$CHECKUPDATES_DB" &> /dev/null +fakeroot -- pacman -Sy --dbpath "$CHECKUPDATES_DB" --logfile /dev/null &> /dev/null +fakeroot pacman -Su -p --dbpath "$CHECKUPDATES_DB" + +exit 0 diff --git a/.config/i3/bar/bin/toggle-display.sh b/.config/i3/bar/bin/toggle-display.sh new file mode 100755 index 00000000..bd13a298 --- /dev/null +++ b/.config/i3/bar/bin/toggle-display.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +echo $(dirname $(readlink -f "$0")) + +i3bar_update=$(dirname $(readlink -f "$0"))/load-i3-bars.sh + +xrandr "$@" + +if [ -f $i3bar_update ]; then + sleep 1 + $i3bar_update +fi diff --git a/.config/i3/bar/bumblebee-status b/.config/i3/bar/bumblebee-status new file mode 100755 index 00000000..582f3148 --- /dev/null +++ b/.config/i3/bar/bumblebee-status @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +import os +import sys +import logging +import bumblebee.theme +import bumblebee.engine +import bumblebee.config +import bumblebee.output +import bumblebee.input +import bumblebee.modules.error + +def main(): + config = bumblebee.config.Config(sys.argv[1:]) + + if config.debug(): + logging.basicConfig( + level=logging.DEBUG, + format="[%(asctime)s] %(levelname)-8s %(message)s", + filename=os.path.expanduser(config.logfile()) + ) + else: + logging.basicConfig( + level=logging.DEBUG, + format="[%(asctime)s] %(levelname)-8s %(message)s", + stream=sys.stderr + ) + + theme = bumblebee.theme.Theme(config.theme()) + output = bumblebee.output.I3BarOutput(theme=theme) + inp = bumblebee.input.I3BarInput() + engine = None + + try: + engine = bumblebee.engine.Engine( + config=config, + output=output, + inp=inp, + ) + engine.run() + except KeyboardInterrupt as error: + inp.stop() + sys.exit(0) + except BaseException as e: + logging.exception(e) + if output.started(): + output.flush() + output.end() + else: + output.start() + import time + while True: + output.begin() + error = bumblebee.modules.error.Module(engine, config) + error.set("exception occurred: {}".format(e)) + widget = error.widgets()[0] + widget.link_module(error) + output.draw(widget, error) + output.flush() + output.end() + time.sleep(1) + +if __name__ == "__main__": + main() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/__init__.py b/.config/i3/bar/bumblebee/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.config/i3/bar/bumblebee/config.py b/.config/i3/bar/bumblebee/config.py new file mode 100644 index 00000000..4e746bd6 --- /dev/null +++ b/.config/i3/bar/bumblebee/config.py @@ -0,0 +1,99 @@ +"""Configuration handling + +This module provides configuration information (loaded modules, +module parameters, etc.) to all other components +""" + +import os +import sys +import logging +import argparse +import textwrap +import importlib +import bumblebee.store + +MODULE_HELP = "Specify a space-separated list of modules to load. The order of the list determines their order in the i3bar (from left to right). Use : to provide an alias in case you want to load the same module multiple times, but specify different parameters." +THEME_HELP = "Specify the theme to use for drawing modules" +PARAMETER_HELP = "Provide configuration parameters in the form of .=" +LIST_HELP = "Display a list of either available themes or available modules along with their parameters." +DEBUG_HELP = "Enable debug log ('debug.log' located in the same directory as the bumblebee-status executable)" + +class print_usage(argparse.Action): + def __init__(self, option_strings, dest, nargs=None, **kwargs): + argparse.Action.__init__(self, option_strings, dest, nargs, **kwargs) + self._indent = " "*2 + + def __call__(self, parser, namespace, value, option_string=None): + if value == "modules": + self.print_modules() + elif value == "themes": + self.print_themes() + sys.exit(0) + + def print_themes(self): + print(textwrap.fill(", ".join(bumblebee.theme.themes()), + 80, initial_indent = self._indent, subsequent_indent = self._indent + )) + + def print_modules(self): + for m in bumblebee.engine.all_modules(): + mod = importlib.import_module("bumblebee.modules.{}".format(m["name"])) + print(textwrap.fill("{}:".format(m["name"]), 80, + initial_indent=self._indent*2, subsequent_indent=self._indent*2)) + for line in mod.__doc__.split("\n"): + print(textwrap.fill(line, 80, + initial_indent=self._indent*3, subsequent_indent=self._indent*6)) + +def create_parser(): + """Create the argument parser""" + parser = argparse.ArgumentParser(description="display system data in the i3bar") + parser.add_argument("-m", "--modules", nargs="+", default=[], + help=MODULE_HELP) + parser.add_argument("-t", "--theme", default="default", help=THEME_HELP) + parser.add_argument("-p", "--parameters", nargs="+", default=[], + help=PARAMETER_HELP) + parser.add_argument("-l", "--list", choices=["modules", "themes"], action=print_usage, + help=LIST_HELP) + parser.add_argument("-d", "--debug", action="store_true", + help=DEBUG_HELP) + parser.add_argument("-f", "--logfile", default="~/bumblebee-status-debug.log", + help="Location of the debug log file") + + return parser + +class Config(bumblebee.store.Store): + """Top-level configuration class + + Parses commandline arguments and provides non-module + specific configuration information. + """ + def __init__(self, args=None): + super(Config, self).__init__() + parser = create_parser() + self._args = parser.parse_args(args if args else []) + + if not self._args.debug: + logger = logging.getLogger().disabled = True + + for param in self._args.parameters: + key, value = param.split("=") + self.set(key, value) + + def modules(self): + """Return a list of all activated modules""" + return [{ + "module": x.split(":")[0], + "name": x if not ":" in x else x.split(":")[1], + } for x in self._args.modules] + + def theme(self): + """Return the name of the selected theme""" + return self._args.theme + + def debug(self): + return self._args.debug + + def logfile(self): + return self._args.logfile + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/engine.py b/.config/i3/bar/bumblebee/engine.py new file mode 100644 index 00000000..c0ea9cc6 --- /dev/null +++ b/.config/i3/bar/bumblebee/engine.py @@ -0,0 +1,166 @@ +"""Core application engine""" + +import os +import time +import pkgutil +import importlib +import bumblebee.error +import bumblebee.modules + +def all_modules(): + """Return a list of available modules""" + result = [] + path = os.path.dirname(bumblebee.modules.__file__) + for mod in [name for _, name, _ in pkgutil.iter_modules([path])]: + result.append({ + "name": mod + }) + return result + +class Module(object): + """Module instance base class + + Objects of this type represent the modules that + the user configures. Concrete module implementations + (e.g. CPU utilization, disk usage, etc.) derive from + this base class. + """ + def __init__(self, engine, config={}, widgets=[]): + self.name = config.get("name", self.__module__.split(".")[-1]) + self._config = config + self.id = self.name + self._widgets = [] + if widgets: + self._widgets = widgets if isinstance(widgets, list) else [widgets] + + def widgets(self): + """Return the widgets to draw for this module""" + return self._widgets + + def widget(self, name): + for widget in self._widgets: + if widget.name == name: + return widget + + def widget_by_id(self, uid): + for widget in self._widgets: + if widget.id == uid: + return widget + return None + + def update(self, widgets): + """By default, update() is a NOP""" + pass + + def update_all(self): + self.update(self._widgets) + + def parameter(self, name, default=None): + """Return the config parameter 'name' for this module""" + name = "{}.{}".format(self.name, name) + return self._config["config"].get(name, default) + + def threshold_state(self, value, warn, crit): + if value > float(self.parameter("critical", crit)): + return "critical" + if value > float(self.parameter("warning", warn)): + return "warning" + return None + +class Engine(object): + """Engine for driving the application + + This class connects input/output, instantiates all + required modules and drives the "event loop" + """ + def __init__(self, config, output=None, inp=None): + self._output = output + self._config = config + self._running = True + self._modules = [] + self.input = inp + self._aliases = self._read_aliases() + self.load_modules(config.modules()) + + self.input.register_callback(None, bumblebee.input.WHEEL_UP, + "i3-msg workspace prev_on_output") + self.input.register_callback(None, bumblebee.input.WHEEL_DOWN, + "i3-msg workspace next_on_output") + + self.input.start() + + def modules(self): + return self._modules + + def load_modules(self, modules): + """Load specified modules and return them as list""" + for module in modules: + mod = self._load_module(module["module"], module["name"]) + self._modules.append(mod) + self._register_module_callbacks(mod) + return self._modules + + def _register_module_callbacks(self, module): + buttons = [ + { "name": "left-click", "id": bumblebee.input.LEFT_MOUSE }, + { "name": "middle-click", "id": bumblebee.input.MIDDLE_MOUSE }, + { "name": "right-click", "id": bumblebee.input.RIGHT_MOUSE }, + { "name": "wheel-up", "id": bumblebee.input.WHEEL_UP }, + { "name": "wheel-down", "id": bumblebee.input.WHEEL_DOWN }, + ] + for button in buttons: + if module.parameter(button["name"], None): + self.input.register_callback(obj=module, + button=button["id"], cmd=module.parameter(button["name"])) + + def _read_aliases(self): + result = {} + for module in all_modules(): + mod = importlib.import_module("bumblebee.modules.{}".format(module["name"])) + for alias in getattr(mod, "ALIASES", []): + result[alias] = module["name"] + return result + + def _load_module(self, module_name, config_name=None): + """Load specified module and return it as object""" + if module_name in self._aliases: + config_name is config_name if config_name else module_name + module_name = self._aliases[module_name] + if config_name is None: + config_name = module_name + try: + module = importlib.import_module("bumblebee.modules.{}".format(module_name)) + except ImportError as error: + raise bumblebee.error.ModuleLoadError(error) + return getattr(module, "Module")(self, { + "name": config_name, + "config": self._config + }) + + def running(self): + """Check whether the event loop is running""" + return self._running + + def stop(self): + """Stop the event loop""" + self._running = False + + def run(self): + """Start the event loop""" + self._output.start() + while self.running(): + self._output.begin() + for module in self._modules: + module.update(module.widgets()) + for widget in module.widgets(): + widget.link_module(module) + self._output.draw(widget=widget, module=module, engine=self) + self._output.flush() + self._output.end() + if self.running(): + self.input.wait(float(self._config.get("interval", 1))) + + self._output.stop() + self.input.stop() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/error.py b/.config/i3/bar/bumblebee/error.py new file mode 100644 index 00000000..129f02d7 --- /dev/null +++ b/.config/i3/bar/bumblebee/error.py @@ -0,0 +1,15 @@ +"""Collection of all exceptions raised by this tool""" + +class BaseError(Exception): + """Base class for all exceptions generated by this tool""" + pass + +class ModuleLoadError(BaseError): + """Raised whenever loading a module fails""" + pass + +class ThemeLoadError(BaseError): + """Raised whenever loading a theme fails""" + pass + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/input.py b/.config/i3/bar/bumblebee/input.py new file mode 100644 index 00000000..1678b2ef --- /dev/null +++ b/.config/i3/bar/bumblebee/input.py @@ -0,0 +1,134 @@ +"""Input classes""" + +import sys +import json +import uuid +import time +import select +import threading +import bumblebee.util + +LEFT_MOUSE = 1 +MIDDLE_MOUSE = 2 +RIGHT_MOUSE = 3 +WHEEL_UP = 4 +WHEEL_DOWN = 5 + +def is_terminated(): + for thread in threading.enumerate(): + if thread.name == "MainThread" and not thread.is_alive(): + return True + return False + +def read_input(inp): + """Read i3bar input and execute callbacks""" + epoll = select.epoll() + epoll.register(sys.stdin.fileno(), select.EPOLLIN) + while inp.running: + if is_terminated(): + return + + events = epoll.poll(1) + for fileno, event in events: + line = "[" + while "[" in line: + line = sys.stdin.readline().strip(",").strip() + inp.has_event = True + try: + event = json.loads(line) + if "instance" in event: + inp.callback(event) + inp.redraw() + except ValueError: + pass + epoll.unregister(sys.stdin.fileno()) + epoll.close() + inp.has_event = True + inp.clean_exit = True + +class I3BarInput(object): + """Process incoming events from the i3bar""" + def __init__(self): + self.running = True + self._callbacks = {} + self.clean_exit = False + self.global_id = str(uuid.uuid4()) + self.need_event = False + self.has_event = False + self._condition = threading.Condition() + + def start(self): + """Start asynchronous input processing""" + self.has_event = False + self.running = True + self._condition.acquire() + self._thread = threading.Thread(target=read_input, args=(self,)) + self._thread.start() + + def redraw(self): + self._condition.acquire() + self._condition.notify() + self._condition.release() + + def alive(self): + """Check whether the input processing is still active""" + return self._thread.is_alive() + + def wait(self, timeout): + self._condition.wait(timeout) + + def _wait(self): + while not self.has_event: + time.sleep(0.1) + self.has_event = False + + def stop(self): + """Stop asynchronous input processing""" + self._condition.release() + if self.need_event: + self._wait() + self.running = False + self._thread.join() + return self.clean_exit + + def _uuidstr(self, name, button): + return "{}::{}".format(name, button) + + def _uid(self, obj, button): + uid = self.global_id + if obj: + uid = obj.id + return self._uuidstr(uid, button) + + def deregister_callbacks(self, obj): + to_delete = [] + uid = obj.id if obj else self.global_id + for key in self._callbacks: + if uid in key: + to_delete.append(key) + for key in to_delete: + del self._callbacks[key] + + def register_callback(self, obj, button, cmd): + """Register a callback function or system call""" + uid = self._uid(obj, button) + if uid not in self._callbacks: + self._callbacks[uid] = {} + self._callbacks[uid] = cmd + + def callback(self, event): + """Execute callback action for an incoming event""" + button = event["button"] + + cmd = self._callbacks.get(self._uuidstr(self.global_id, button), None) + cmd = self._callbacks.get(self._uuidstr(event["name"], button), cmd) + cmd = self._callbacks.get(self._uuidstr(event["instance"], button), cmd) + + if cmd is None: + return + if callable(cmd): + cmd(event) + else: + bumblebee.util.execute(cmd, False) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/__init__.py b/.config/i3/bar/bumblebee/modules/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.config/i3/bar/bumblebee/modules/amixer.py b/.config/i3/bar/bumblebee/modules/amixer.py new file mode 100644 index 00000000..fa08ec33 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/amixer.py @@ -0,0 +1,36 @@ +"""get volume level + +""" +import re + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.volume) + ) + self._level = "0" + self._muted = True + device = self.parameter("device", "Master,0") + self._cmdString = "amixer get {}".format(device) + + def volume(self, widget): + m = re.search(r'([\d]+)\%', self._level) + self._muted = True + if m: + if m.group(1) != "0": + self._muted = False + return "{}%".format(m.group(1)) + else: + return "0%" + + def update(self, widgets): + self._level = bumblebee.util.execute(self._cmdString) + + def state(self, widget): + if self._muted: + return [ "warning", "muted" ] + return [ "unmuted" ] diff --git a/.config/i3/bar/bumblebee/modules/battery.py b/.config/i3/bar/bumblebee/modules/battery.py new file mode 100644 index 00000000..f00c603a --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/battery.py @@ -0,0 +1,82 @@ +# pylint: disable=C0111,R0903 + +"""Displays battery status, remaining percentage and charging information. + +Parameters: + * battery.device : The device to read information from (defaults to BAT0) + * battery.warning : Warning threshold in % of remaining charge (defaults to 20) + * battery.critical: Critical threshold in % of remaining charge (defaults to 10) +""" + +import os + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.capacity) + ) + battery = self.parameter("device", "BAT0") + self._path = "/sys/class/power_supply/{}".format(battery) + self._capacity = 100 + self._ac = False + + def capacity(self, widget): + if self._ac: + return "ac" + if self._capacity == -1: + return "n/a" + return "{:03d}%".format(self._capacity) + + def update(self, widgets): + widget = widgets[0] + self._ac = False + if not os.path.exists(self._path): + self._ac = True + self._capacity = 100 + return + + try: + with open(self._path + "/capacity") as f: + self._capacity = int(f.read()) + except IOError: + self._capacity = -1 + self._capacity = self._capacity if self._capacity < 100 else 100 + + def state(self, widget): + state = [] + + if self._capacity < 0: + return ["critical", "unknown"] + + if self._capacity < int(self.parameter("critical", 10)): + state.append("critical") + elif self._capacity > int(self.parameter("great", 90)): + state.append("great") + elif self._capacity > int(self.parameter("good", 80)): + state.append("good") + elif self._capacity < int(self.parameter("warning", 20)): + state.append("warning") + elif self._capacity < int(self.parameter("mid", 80)): + state.append("mid") + + if self._ac: + state.append("AC") + else: + charge = "" + with open(self._path + "/status") as f: + charge = f.read().strip() + if charge == "Discharging": + state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity)))) + else: + if self._capacity > 95: + state.append("charged") + else: + state.append("charging") + + return state + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/battery.py_ori b/.config/i3/bar/bumblebee/modules/battery.py_ori new file mode 100644 index 00000000..5384716d --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/battery.py_ori @@ -0,0 +1,75 @@ +# pylint: disable=C0111,R0903 + +"""Displays battery status, remaining percentage and charging information. + +Parameters: + * battery.device : The device to read information from (defaults to BAT0) + * battery.warning : Warning threshold in % of remaining charge (defaults to 20) + * battery.critical: Critical threshold in % of remaining charge (defaults to 10) +""" + +import os + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.capacity) + ) + battery = self.parameter("device", "BAT0") + self._path = "/sys/class/power_supply/{}".format(battery) + self._capacity = 100 + self._ac = False + + def capacity(self, widget): + if self._ac: + return "ac" + if self._capacity == -1: + return "n/a" + return "{:03d}%".format(self._capacity) + + def update(self, widgets): + self._ac = False + if not os.path.exists(self._path): + self._ac = True + self._capacity = 100 + return + + try: + with open(self._path + "/capacity") as f: + self._capacity = int(f.read()) + except IOError: + self._capacity = -1 + self._capacity = self._capacity if self._capacity < 100 else 100 + + def state(self, widget): + state = [] + + if self._capacity < 0: + return ["critical", "unknown"] + + if self._capacity < int(self.parameter("critical", 10)): + state.append("critical") + elif self._capacity < int(self.parameter("warning", 20)): + state.append("warning") + + if self._ac: + state.append("AC") + else: + charge = "" + with open(self._path + "/status") as f: + charge = f.read().strip() + if charge == "Discharging": + state.append("discharging-{}".format(min([10, 25, 50, 80, 100] , key=lambda i:abs(i-self._capacity)))) + else: + if self._capacity > 95: + state.append("charged") + else: + state.append("charging") + + return state + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/brightness.py b/.config/i3/bar/bumblebee/modules/brightness.py new file mode 100644 index 00000000..716a734f --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/brightness.py @@ -0,0 +1,37 @@ +# pylint: disable=C0111,R0903 + +"""Displays the brightness of a display + +Requires the following executable: + * xbacklight + +Parameters: + * brightness.step: The amount of increase/decrease on scroll in % (defaults to 2) + +""" + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.brightness) + ) + self._brightness = 0 + + step = self.parameter("step", 2) + + engine.input.register_callback(self, button=bumblebee.input.WHEEL_UP, + cmd="xbacklight +{}%".format(step)) + engine.input.register_callback(self, button=bumblebee.input.WHEEL_DOWN, + cmd="xbacklight -{}%".format(step)) + + def brightness(self, widget): + return "{:03.0f}%".format(self._brightness) + + def update(self, widgets): + self._brightness = float(bumblebee.util.execute("xbacklight -get")) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/caffeine.py b/.config/i3/bar/bumblebee/modules/caffeine.py new file mode 100644 index 00000000..2354ed0f --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/caffeine.py @@ -0,0 +1,48 @@ +# pylint: disable=C0111,R0903 + +"""Enable/disable automatic screen locking. + +Requires the following executables: + * xset + * notify-send +""" + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.caffeine) + ) + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, + cmd=self._toggle + ) + + def caffeine(self, widget): + return "" + + def state(self, widget): + if self._active(): + return "activated" + return "deactivated" + + def _active(self): + for line in bumblebee.util.execute("xset q").split("\n"): + if "timeout" in line: + timeout = int(line.split(" ")[4]) + if timeout == 0: + return True + return False + return False + + def _toggle(self, widget): + if self._active(): + bumblebee.util.execute("xset s default") + bumblebee.util.execute("notify-send \"Out of coffee\"") + else: + bumblebee.util.execute("xset s off") + bumblebee.util.execute("notify-send \"Consuming caffeine\"") + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/cmus.py b/.config/i3/bar/bumblebee/modules/cmus.py new file mode 100644 index 00000000..4bc0e56e --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/cmus.py @@ -0,0 +1,92 @@ +# pylint: disable=C0111,R0903 +# -*- coding: utf-8 -*- + +"""Displays information about the current song in cmus. + +Requires the following executable: + * cmus-remote + +Parameters: + * cmus.format: Format string for the song information. Tag values can be put in curly brackets (i.e. {artist}) +""" + +from collections import defaultdict + +import string + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +from bumblebee.output import scrollable + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widgets = [ + bumblebee.output.Widget(name="cmus.prev"), + bumblebee.output.Widget(name="cmus.main", full_text=self.description), + bumblebee.output.Widget(name="cmus.next"), + bumblebee.output.Widget(name="cmus.shuffle"), + bumblebee.output.Widget(name="cmus.repeat"), + ] + super(Module, self).__init__(engine, config, widgets) + + engine.input.register_callback(widgets[0], button=bumblebee.input.LEFT_MOUSE, + cmd="cmus-remote -r") + engine.input.register_callback(widgets[1], button=bumblebee.input.LEFT_MOUSE, + cmd="cmus-remote -u") + engine.input.register_callback(widgets[2], button=bumblebee.input.LEFT_MOUSE, + cmd="cmus-remote -n") + engine.input.register_callback(widgets[3], button=bumblebee.input.LEFT_MOUSE, + cmd="cmus-remote -S") + engine.input.register_callback(widgets[4], button=bumblebee.input.LEFT_MOUSE, + cmd="cmus-remote -R") + + self._fmt = self.parameter("format", "{artist} - {title} {position}/{duration}") + self._status = None + self._shuffle = False + self._repeat = False + self._tags = defaultdict(lambda: '') + + @scrollable + def description(self, widget): + return string.Formatter().vformat(self._fmt, (), self._tags) + + def update(self, widgets): + self._load_song() + + def state(self, widget): + if widget.name == "cmus.shuffle": + return "shuffle-on" if self._shuffle else "shuffle-off" + if widget.name == "cmus.repeat": + return "repeat-on" if self._repeat else "repeat-off" + if widget.name == "cmus.prev": + return "prev" + if widget.name == "cmus.next": + return "next" + return self._status + + def _load_song(self): + info = "" + try: + info = bumblebee.util.execute("cmus-remote -Q") + except RuntimeError: + pass + self._tags = defaultdict(lambda: '') + for line in info.split("\n"): + if line.startswith("status"): + self._status = line.split(" ", 2)[1] + if line.startswith("tag"): + key, value = line.split(" ", 2)[1:] + self._tags.update({ key: value }) + for key in ["duration", "position"]: + if line.startswith(key): + dur = int(line.split(" ")[1]) + self._tags.update({key:bumblebee.util.durationfmt(dur)}) + if line.startswith("set repeat "): + self._repeat = False if "false" in line else True + if line.startswith("set shuffle "): + self._shuffle = False if "false" in line else True + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/cpu.py b/.config/i3/bar/bumblebee/modules/cpu.py new file mode 100644 index 00000000..6a24bdc9 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/cpu.py @@ -0,0 +1,37 @@ +# pylint: disable=C0111,R0903 + +"""Displays CPU utilization across all CPUs. + +Parameters: + * cpu.warning : Warning threshold in % of CPU usage (defaults to 70%) + * cpu.critical: Critical threshold in % of CPU usage (defaults to 80%) +""" + +try: + import psutil +except ImportError: + pass + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.utilization) + ) + self._utilization = psutil.cpu_percent(percpu=False) + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, + cmd="gnome-system-monitor") + + def utilization(self, widget): + return "{:06.02f}%".format(self._utilization) + + def update(self, widgets): + self._utilization = psutil.cpu_percent(percpu=False) + + def state(self, widget): + return self.threshold_state(self._utilization, 70, 80) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/datetime.py b/.config/i3/bar/bumblebee/modules/datetime.py new file mode 100644 index 00000000..4141a53f --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/datetime.py @@ -0,0 +1,35 @@ +# pylint: disable=C0111,R0903 + +"""Displays the current date and time. + +Parameters: + * datetime.format: strftime()-compatible formatting string + * date.format : alias for datetime.format + * time.format : alias for datetime.format +""" + +from __future__ import absolute_import +import datetime +import bumblebee.engine + +ALIASES = [ "date", "time" ] + +def default_format(module): + default = "%x %X" + if module == "date": + default = "%x" + if module == "time": + default = "%X" + return default + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.get_time) + ) + self._fmt = self.parameter("format", default_format(self.name)) + + def get_time(self, widget): + return datetime.datetime.now().strftime(self._fmt) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/disk.py b/.config/i3/bar/bumblebee/modules/disk.py new file mode 100644 index 00000000..1f53bda3 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/disk.py @@ -0,0 +1,47 @@ +# pylint: disable=C0111,R0903 + +"""Shows free diskspace, total diskspace and the percentage of free disk space. + +Parameters: + * disk.warning: Warning threshold in % of disk space (defaults to 80%) + * disk.critical: Critical threshold in % of disk space (defaults ot 90%) + * disk.path: Path to calculate disk usage from (defaults to /) +""" + +import os + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.diskspace) + ) + self._path = self.parameter("path", "/") + self._perc = 0 + self._used = 0 + self._size = 0 + + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, + cmd="termite -e ranger {}".format(self._path)) + + def diskspace(self, widget): + #return "{} {}/{} ({:05.02f}%)".format(self._path, + #bumblebee.util.bytefmt(self._used), + #bumblebee.util.bytefmt(self._size), self._perc + #) + #return "{} {:05.02f}%".format(self._path, self._perc) + return "{}".format(bumblebee.util.bytefmt(self._size - self._used)) + + def update(self, widgets): + st = os.statvfs(self._path) + self._size = st.f_frsize*st.f_blocks + self._used = self._size - st.f_frsize*st.f_bavail + self._perc = 100.0*self._used/self._size + + def state(self, widget): + return self.threshold_state(self._perc, 90, 95) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/disk.py_ori b/.config/i3/bar/bumblebee/modules/disk.py_ori new file mode 100644 index 00000000..aee40ac5 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/disk.py_ori @@ -0,0 +1,45 @@ +# pylint: disable=C0111,R0903 + +"""Shows free diskspace, total diskspace and the percentage of free disk space. + +Parameters: + * disk.warning: Warning threshold in % of disk space (defaults to 80%) + * disk.critical: Critical threshold in % of disk space (defaults ot 90%) + * disk.path: Path to calculate disk usage from (defaults to /) +""" + +import os + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.diskspace) + ) + self._path = self.parameter("path", "/") + self._perc = 0 + self._used = 0 + self._size = 0 + + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, + cmd="nautilus {}".format(self._path)) + + def diskspace(self, widget): + return "{} {}/{} ({:05.02f}%)".format(self._path, + bumblebee.util.bytefmt(self._used), + bumblebee.util.bytefmt(self._size), self._perc + ) + + def update(self, widgets): + st = os.statvfs(self._path) + self._size = st.f_blocks * st.f_frsize + self._used = (st.f_blocks - st.f_bfree) * st.f_frsize + self._perc = 100.0*self._used/self._size + + def state(self, widget): + return self.threshold_state(self._perc, 80, 90) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/dnf.py b/.config/i3/bar/bumblebee/modules/dnf.py new file mode 100644 index 00000000..f36dfa9a --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/dnf.py @@ -0,0 +1,80 @@ +# pylint: disable=C0111,R0903 + +"""Displays DNF package update information (///) + +Requires the following executable: + * dnf + +Parameters: + * dnf.interval: Time in seconds between two consecutive update checks (defaulst to 1800) + +""" + +import time +import threading + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +def get_dnf_info(widget): + try: + res = bumblebee.util.execute("dnf updateinfo") + except RuntimeError: + pass + + security = 0 + bugfixes = 0 + enhancements = 0 + other = 0 + for line in res.split("\n"): + + if not line.startswith(" "): continue + elif "ecurity" in line: + for s in line.split(): + if s.isdigit(): security += int(s) + elif "ugfix" in line: + for s in line.split(): + if s.isdigit(): bugfixes += int(s) + elif "hancement" in line: + for s in line.split(): + if s.isdigit(): enhancements += int(s) + else: + for s in line.split(): + if s.isdigit(): other += int(s) + + widget.set("security", security) + widget.set("bugfixes", bugfixes) + widget.set("enhancements", enhancements) + widget.set("other", other) + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widget = bumblebee.output.Widget(full_text=self.updates) + super(Module, self).__init__(engine, config, widget) + self._next_check = 0 + + def updates(self, widget): + result = [] + for t in ["security", "bugfixes", "enhancements", "other"]: + result.append(str(widget.get(t, 0))) + return "/".join(result) + + def update(self, widgets): + if int(time.time()) < self._next_check: + return + thread = threading.Thread(target=get_dnf_info, args=(widgets[0],)) + thread.start() + self._next_check = int(time.time()) + self.parameter("interval", 30*60) + + def state(self, widget): + cnt = 0 + for t in ["security", "bugfixes", "enhancements", "other"]: + cnt += widget.get(t, 0) + if cnt == 0: + return "good" + if cnt > 50 or widget.get("security", 0) > 0: + return "critical" + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/error.py b/.config/i3/bar/bumblebee/modules/error.py new file mode 100644 index 00000000..16d3834f --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/error.py @@ -0,0 +1,26 @@ +# pylint: disable=C0111,R0903 + +"""Draws an error widget. +""" + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.text) + ) + self._text = "" + + def set(self, text): + self._text = text + + def text(self, widget): + return self._text + + def state(self, widget): + return ["critical"] + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/gpmdp.py b/.config/i3/bar/bumblebee/modules/gpmdp.py new file mode 100644 index 00000000..36988e86 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/gpmdp.py @@ -0,0 +1,58 @@ +# pylint: disable=C0111,R0903 + +"""Displays information about the current song in Google Play music player. + +Requires the following executable: + * gpmdp-remote +""" + +import string + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widgets = [ + bumblebee.output.Widget(name="gpmdp.prev"), + bumblebee.output.Widget(name="gpmdp.main", full_text=self.description), + bumblebee.output.Widget(name="gpmdp.next"), + ] + super(Module, self).__init__(engine, config, widgets) + + engine.input.register_callback(widgets[0], button=bumblebee.input.LEFT_MOUSE, + cmd="playerctl previous") + engine.input.register_callback(widgets[1], button=bumblebee.input.LEFT_MOUSE, + cmd="playerctl play-pause") + engine.input.register_callback(widgets[2], button=bumblebee.input.LEFT_MOUSE, + cmd="playerctl next") + + self._status = None + self._tags = None + + def description(self, widget): + return self._tags if self._tags else "n/a" + + def update(self, widgets): + self._load_song() + + def state(self, widget): + if widget.name == "gpmdp.prev": + return "prev" + if widget.name == "gpmdp.next": + return "next" + return self._status + + def _load_song(self): + info = "" + try: + info = bumblebee.util.execute("gpmdp-remote current") + status = bumblebee.util.execute("gpmdp-remote status") + except RuntimeError: + pass + self._status = status.split("\n")[0].lower() + self._tags = info.split("\n")[0] + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/kernel.py b/.config/i3/bar/bumblebee/modules/kernel.py new file mode 100644 index 00000000..761fa8c0 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/kernel.py @@ -0,0 +1,21 @@ +# pylint: disable=C0111,R0903 + +"""Shows Linux kernel version information""" + +import platform + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.output) + ) + self._release_name = platform.release() + + def output(self, widget): + return self._release_name + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/layout.py b/.config/i3/bar/bumblebee/modules/layout.py new file mode 100644 index 00000000..d9daadbb --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/layout.py @@ -0,0 +1,74 @@ +# pylint: disable=C0111,R0903 + +"""Displays and changes the current keyboard layout + +Requires the following executable: + * setxkbmap + +Parameters: + * layout.lang: pipe-separated list of languages to cycle through (e.g. us|rs|de). Default: en +""" + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.layout) + ) + self._languages = self.parameter("lang", "us").split("|") + self._idx = 0 + + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, + cmd=self._next_keymap) + engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, + cmd=self._prev_keymap) + + def _next_keymap(self, event): + self._idx = (self._idx + 1) % len(self._languages) + self._set_keymap() + + def _prev_keymap(self, event): + self._idx = self._idx - 1 if self._idx > 0 else len(self._languages) - 1 + self._set_keymap() + + def _set_keymap(self): + tmp = self._languages[self._idx].split(":") + layout = tmp[0] + variant = "" + if len(tmp) > 1: + variant = "-variant {}".format(tmp[1]) + try: + bumblebee.util.execute("setxkbmap -layout {} {}".format(layout, variant)) + except RuntimeError: + pass + + def layout(self, widget): + try: + res = bumblebee.util.execute("setxkbmap -query") + except RuntimeError: + return "n/a" + layout = "" + variant = None + for line in res.split("\n"): + if not line: + continue + if "layout" in line: + layout = line.split(":")[1].strip() + if "variant" in line: + variant = line.split(":")[1].strip() + if variant: + layout += ":" + variant + + if layout in self._languages: + self._idx = self._languages.index(layout) + else: + self._languages.append(layout) + self._idx = len(self._languages) - 1 + + return layout + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/load.py b/.config/i3/bar/bumblebee/modules/load.py new file mode 100644 index 00000000..4d94ee18 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/load.py @@ -0,0 +1,41 @@ +# pylint: disable=C0111,R0903 + +"""Displays system load. + +Parameters: + * load.warning : Warning threshold for the one-minute load average (defaults to 70% of the number of CPUs) + * load.critical: Critical threshold for the one-minute load average (defaults to 80% of the number of CPUs) +""" + +import os +import multiprocessing + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.load) + ) + self._load = [0, 0, 0] + try: + self._cpus = multiprocessing.cpu_count() + except NotImplementedError as e: + self._cpus = 1 + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, + cmd="gnome-system-monitor") + + def load(self, widget): + return "{:.02f}/{:.02f}/{:.02f}".format( + self._load[0], self._load[1], self._load[2] + ) + + def update(self, widgets): + self._load = os.getloadavg() + + def state(self, widget): + return self.threshold_state(self._load[0], self._cpus*0.7, self._cpus*0.8) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/memory.py b/.config/i3/bar/bumblebee/modules/memory.py new file mode 100644 index 00000000..0534f7da --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/memory.py @@ -0,0 +1,47 @@ +# pylint: disable=C0111,R0903 + +"""Displays available RAM, total amount of RAM and percentage available. + +Parameters: + * cpu.warning : Warning threshold in % of memory used (defaults to 80%) + * cpu.critical: Critical threshold in % of memory used (defaults to 90%) +""" + +try: + import psutil +except ImportError: + pass + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.memory_usage) + ) + self._mem = psutil.virtual_memory() + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, + cmd="gnome-system-monitor") + + def memory_usage(self, widget): + used = self._mem.total - self._mem.available + return "{}/{} ({:05.02f}%)".format( + bumblebee.util.bytefmt(used), + bumblebee.util.bytefmt(self._mem.total), + self._mem.percent + ) + + def update(self, widgets): + self._mem = psutil.virtual_memory() + + def state(self, widget): + if self._mem.percent > float(self.parameter("critical", 90)): + return "critical" + if self._mem.percent > float(self.parameter("warning", 80)): + return "warning" + return None + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/nic.py b/.config/i3/bar/bumblebee/modules/nic.py new file mode 100644 index 00000000..4fd8e550 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/nic.py @@ -0,0 +1,83 @@ +#pylint: disable=C0111,R0903 + +"""Displays the name, IP address(es) and status of each available network interface. + +Parameters: + * nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth") +""" + +import netifaces + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widgets = [] + super(Module, self).__init__(engine, config, widgets) + self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(","))) + self._update_widgets(widgets) + + def update(self, widgets): + self._update_widgets(widgets) + + def state(self, widget): + states = [] + + if widget.get("state") == "down": + states.append("critical") + elif widget.get("state") != "up": + states.append("warning") + else: + states.append("good") + + intf = widget.get("intf") + iftype = "wireless" if self._iswlan(intf) else "wired" + iftype = "tunnel" if self._istunnel(intf) else iftype + + states.append("{}-{}".format(iftype, widget.get("state"))) + + return states + + def _iswlan(self, intf): + # wifi, wlan, wlp, seems to work for me + if intf.startswith("w"): return True + return False + + def _istunnel(self, intf): + return intf.startswith("tun") + + def _update_widgets(self, widgets): + interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ] + + for widget in widgets: + widget.set("visited", False) + + for intf in interfaces: + addr = [] + state = "down" + try: + if netifaces.AF_INET in netifaces.ifaddresses(intf): + for ip in netifaces.ifaddresses(intf)[netifaces.AF_INET]: + if "addr" in ip and ip["addr"] != "": + addr.append(ip["addr"]) + state = "up" + except Exception as e: + addr = [] + widget = self.widget(intf) + if not widget: + widget = bumblebee.output.Widget(name=intf) + widgets.append(widget) + widget.full_text("{}".format(",".join(addr))) + #widget.full_text("{} {} {}".format(intf, state, ", ".join(addr))) + widget.set("intf", intf) + widget.set("state", state) + widget.set("visited", True) + + for widget in widgets: + if widget.get("visited") == False: + widgets.remove(widget) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/nic.py_ori b/.config/i3/bar/bumblebee/modules/nic.py_ori new file mode 100644 index 00000000..2ca87a7e --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/nic.py_ori @@ -0,0 +1,88 @@ +#pylint: disable=C0111,R0903 + +"""Displays the name, IP address(es) and status of each available network interface. + +Parameters: + * nic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth") +""" + +try: + import netifaces +except ImportError: + pass + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widgets = [] + super(Module, self).__init__(engine, config, widgets) + self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(","))) + self._update_widgets(widgets) + + def update(self, widgets): + self._update_widgets(widgets) + + def state(self, widget): + states = [] + + if widget.get("state") == "down": + states.append("critical") + elif widget.get("state") != "up": + states.append("warning") + + intf = widget.get("intf") + iftype = "wireless" if self._iswlan(intf) else "wired" + iftype = "tunnel" if self._istunnel(intf) else iftype + + states.append("{}-{}".format(iftype, widget.get("state"))) + + return states + + def _iswlan(self, intf): + # wifi, wlan, wlp, seems to work for me + if intf.startswith("w"): return True + return False + + def _istunnel(self, intf): + return intf.startswith("tun") + + def get_addresses(self, intf): + retval = [] + try: + for ip in netifaces.ifaddresses(intf).get(netifaces.AF_INET, []): + if ip.get("addr", "") != "": + retval.append(ip.get("addr")) + except Exception: + return [] + return retval + + def _update_widgets(self, widgets): + interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ] + + for widget in widgets: + widget.set("visited", False) + + for intf in interfaces: + addr = [] + state = "down" + for ip in self.get_addresses(intf): + addr.append(ip) + state = "up" + widget = self.widget(intf) + if not widget: + widget = bumblebee.output.Widget(name=intf) + widgets.append(widget) + widget.full_text("{} {} {}".format(intf, state, ", ".join(addr))) + widget.set("intf", intf) + widget.set("state", state) + widget.set("visited", True) + + for widget in widgets: + if widget.get("visited") == False: + widgets.remove(widget) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/pacman.py b/.config/i3/bar/bumblebee/modules/pacman.py new file mode 100644 index 00000000..a7780a8b --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/pacman.py @@ -0,0 +1,70 @@ +# pylint: disable=C0111,R0903 + +"""Displays update information per repository for pacman." + +Requires the following executables: + * fakeroot + * pacman +""" + +import os +import threading + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +#list of repositories. +#the last one sould always be other +repos = ["core", "extra", "community", "multilib", "testing", "other"] + +def get_pacman_info(widget, path): + try: + result = bumblebee.util.execute("{}/../../bin/pacman-updates".format(path)) + except: + pass + + count = len(repos)*[0] + + for line in result.splitlines(): + if line.startswith(("http", "rsync")): + for i in range(len(repos)-1): + if "/" + repos[i] + "/" in line: + count[i] += 1 + break + else: + result[-1] += 1 + + for i in range(len(repos)): + widget.set(repos[i], count[i]) + + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.updates) + ) + self._count = 0 + + def updates(self, widget): + return '/'.join(map(lambda x: str(widget.get(x,0)), repos)) + + def update(self, widgets): + path = os.path.dirname(os.path.abspath(__file__)) + if self._count == 0: + thread = threading.Thread(target=get_pacman_info, args=(widgets[0],path)) + thread.start() + + # TODO: improve this waiting mechanism a bit + self._count += 1 + self._count = 0 if self._count > 300 else self._count + + def state(self, widget): + weightedCount = sum(map(lambda x: (len(repos)-x[0]) * widget.get(x[1],0), enumerate(repos))) + + if weightedCount < 10: + return "good" + + return self.threshold_state(weightedCount, 100, 150) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/ping.py b/.config/i3/bar/bumblebee/modules/ping.py new file mode 100644 index 00000000..469871ae --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/ping.py @@ -0,0 +1,77 @@ +# pylint: disable=C0111,R0903 + +"""Periodically checks the RTT of a configurable host using ICMP echos + +Requires the following executable: + * ping + +Parameters: + * ping.interval: Time in seconds between two RTT checks (defaults to 60) + * ping.address : IP address to check + * ping.timeout : Timeout for waiting for a reply (defaults to 5.0) + * ping.probes : Number of probes to send (defaults to 5) + * ping.warning : Threshold for warning state, in seconds (defaults to 1.0) + * ping.critical: Threshold for critical state, in seconds (defaults to 2.0) +""" + +import re +import time +import threading + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +def get_rtt(module, widget): + try: + widget.set("rtt-unreachable", False) + res = bumblebee.util.execute("ping -n -q -c {} -W {} {}".format( + widget.get("rtt-probes"), widget.get("rtt-timeout"), widget.get("address") + )) + + for line in res.split("\n"): + if not line.startswith("rtt"): continue + m = re.search(r'([0-9\.]+)/([0-9\.]+)/([0-9\.]+)/([0-9\.]+)\s+(\S+)', line) + + widget.set("rtt-min", float(m.group(1))) + widget.set("rtt-avg", float(m.group(2))) + widget.set("rtt-max", float(m.group(3))) + widget.set("rtt-unit", m.group(5)) + except Exception as e: + widget.set("rtt-unreachable", True) + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widget = bumblebee.output.Widget(full_text=self.rtt) + super(Module, self).__init__(engine, config, widget) + + widget.set("address", self.parameter("address", "8.8.8.8")) + widget.set("interval", self.parameter("interval", 60)) + widget.set("rtt-probes", self.parameter("probes", 5)) + widget.set("rtt-timeout", self.parameter("timeout", 5.0)) + widget.set("rtt-avg", 0.0) + widget.set("rtt-unit", "") + + self._next_check = 0 + + def rtt(self, widget): + if widget.get("rtt-unreachable"): + return "{}: unreachable".format(widget.get("address")) + return "{}: {:.1f}{}".format( + widget.get("address"), + widget.get("rtt-avg"), + widget.get("rtt-unit") + ) + + def state(self, widget): + if widget.get("rtt-unreachable"): return ["critical"] + return self.threshold_state(widget.get("rtt-avg"), 1000.0, 2000.0) + + def update(self, widgets): + if int(time.time()) < self._next_check: + return + thread = threading.Thread(target=get_rtt, args=(self,widgets[0],)) + thread.start() + self._next_check = int(time.time()) + int(widgets[0].get("interval")) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/pulseaudio.py b/.config/i3/bar/bumblebee/modules/pulseaudio.py new file mode 100644 index 00000000..0bf88f4c --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/pulseaudio.py @@ -0,0 +1,107 @@ +# pylint: disable=C0111,R0903 + +"""Displays volume and mute status of PulseAudio devices. + +Aliases: pasink, pasource + +Requires the following executable: + * pactl +""" + +import re + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +ALIASES = [ "pasink", "pasource" ] + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.volume) + ) + + self._left = 0 + self._right = 0 + self._mono = 0 + self._mute = False + channel = "sink" if self.name == "pasink" else "source" + + self._patterns = [ + { "expr": "Name:", "callback": (lambda line: False) }, + { "expr": "Mute:", "callback": (lambda line: self.mute(False if " no" in line.lower() else True)) }, + { "expr": "Volume:", "callback": self.getvolume }, + ] + + engine.input.register_callback(self, button=bumblebee.input.RIGHT_MOUSE, cmd="pavucontrol") + + events = [ + { "type": "mute", "action": "toggle", "button": bumblebee.input.LEFT_MOUSE }, + { "type": "volume", "action": "+2%", "button": bumblebee.input.WHEEL_UP }, + { "type": "volume", "action": "-2%", "button": bumblebee.input.WHEEL_DOWN }, + ] + + for event in events: + engine.input.register_callback(self, button=event["button"], + cmd="pactl set-{}-{} @DEFAULT_{}@ {}".format(channel, event["type"], + channel.upper(), event["action"])) + + def mute(self, value): + self._mute = value + + def getvolume(self, line): + if "mono" in line: + m = re.search(r'mono:.*\s*\/\s*(\d+)%', line) + if m: + self._mono = m.group(1) + else: + m = re.search(r'left:.*\s*\/\s*(\d+)%.*right:.*\s*\/\s*(\d+)%', line) + if m: + self._left = m.group(1) + self._right = m.group(2) + return True + + def _default_device(self): + output = bumblebee.util.execute("pactl info") + pattern = "Default Sink: " if self.name == "pasink" else "Default Source: " + for line in output.split("\n"): + if line.startswith(pattern): + return line.replace(pattern, "") + return "n/a" + + def volume(self, widget): + if int(self._mono) > 0: + return "{}%".format(self._mono) + elif self._left == self._right: + return "{}%".format(self._left) + else: + return "{}%/{}%".format(self._left, self._right) + return "n/a" + + def update(self, widgets): + channel = "sinks" if self.name == "pasink" else "sources" + device = self._default_device() + + result = bumblebee.util.execute("pactl list {}".format(channel)) + found = False + + for line in result.split("\n"): + if device in line: + found = True + continue + if found == False: + continue + for pattern in self._patterns: + if not pattern["expr"] in line: + continue + if pattern["callback"](line) == False and found == True: + return + + def state(self, widget): + if self._mute: + return [ "warning", "muted" ] + return [ "unmuted" ] + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/redshift.py b/.config/i3/bar/bumblebee/modules/redshift.py new file mode 100644 index 00000000..2fd1dfbb --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/redshift.py @@ -0,0 +1,46 @@ +# pylint: disable=C0111,R0903 + +"""Displays the current color temperature of redshift + +Requires the following executable: + * redshift +""" + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.text) + ) + self._text = "" + self._state = "transition" + + def text(self, widget): + return "{}".format(self._text) + + def update(self, widgets): + result = bumblebee.util.execute("redshift -p") + + temp = "" + transition = "" + for line in result.split("\n"): + if "temperature" in line.lower(): + temp = line.split(" ")[2] + if "period" in line.lower(): + state = line.split(" ")[1].lower() + if "day" in state: + self._state = "day" + elif "night" in state: + self._state = "night" + else: + self._state = "transition" + transition = " ".join(line.split(" ")[2:]) + self._text = "{} {}".format(temp, transition) + + def state(self, widget): + return self._state + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/sensors.py b/.config/i3/bar/bumblebee/modules/sensors.py new file mode 100644 index 00000000..36dabbd7 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/sensors.py @@ -0,0 +1,45 @@ +"""Displays sensor temperature + +Requires the following executable: + * sensors + +Parameters: + * sensors.match: What line in the output of `sensors -u` should be matched against (default: temp1_input) + * sensors.match_number: which of the matches you want (default -1: last match). +""" + +import os +import re + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.temperature) + ) + self._temperature = "unknown" + pattern = self.parameter("match", "temp1_input") + pattern_string = r"^\s*{}:\s*([\d.]+)$".format(pattern) + self._match_number = int(self.parameter("match_number", "-1")) + self._pattern = re.compile(pattern_string, re.MULTILINE) + engine.input.register_callback(self, button=bumblebee.input.LEFT_MOUSE, cmd="xsensors") + + def get_temp(self): + temperatures = bumblebee.util.execute("sensors -u") + matching_temp = self._pattern.findall(temperatures) + temperature = "unknown" + if matching_temp: + temperature = int(float(matching_temp[self._match_number])) + + return temperature + + def temperature(self, widget): + return self._temperature + + def update(self, widgets): + self._temperature = self.get_temp() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/spacer.py b/.config/i3/bar/bumblebee/modules/spacer.py new file mode 100644 index 00000000..b89c2334 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/spacer.py @@ -0,0 +1,23 @@ +# pylint: disable=C0111,R0903 + +"""Draws a widget with configurable text content. + +Parameters: + * spacer.text: Widget contents (defaults to empty string) +""" + +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.text) + ) + self._text = self.parameter("text", "") + + def text(self, widget): + return self._text + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/stock.py b/.config/i3/bar/bumblebee/modules/stock.py new file mode 100644 index 00000000..2297c3a8 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/stock.py @@ -0,0 +1,62 @@ +# -*- coding: UTF-8 -*- +# pylint: disable=C0111,R0903 + +"""Display a stock quote from yahoo finance. + +Requires the following python packages: + * requests + +Parameters: + * stock.symbols : Comma-separated list of symbols to fetch + * stock.change : Should we fetch change in stock value (defaults to True) + * stock.currencies : List of symbols to go with the values (default $) +""" + +import bumblebee.input +import bumblebee.output +import bumblebee.engine +import requests +from requests.exceptions import RequestException + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.value) + ) + self._symbols = self.parameter("symbols", "") + self._change = self.parameter("change", True) + self._currencies = self.parameter('currencies', None) + self._baseurl = 'http://download.finance.yahoo.com/d/quotes.csv' + self._value = self.fetch() + + if not self._currencies: + self._currencies = '$' * len(self._symbols) + + # The currencies could be unicode, like the € symbol. Convert to a unicode object. + if hasattr(self._currencies, "decode"): + self._currencies = self._currencies.decode("utf-8", "ignore") + + def value(self, widget): + results = [] + for i, val in enumerate(self._value.split('\n')): + try: + currency_symbol = self._currencies[i] + except: + currency_symbol = '$' + results.append('%s%s' % (currency_symbol, val)) + return u' '.join(results) + + def fetch(self): + if self._symbols: + url = self._baseurl + url += '?s=%s&f=l1' % self._symbols + if self._change: + url += 'c1' + return requests.get(url).text.strip() + else: + return '' + + def update(self, widgets): + self._value = self.fetch() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/test.py b/.config/i3/bar/bumblebee/modules/test.py new file mode 100644 index 00000000..e4099c65 --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/test.py @@ -0,0 +1,16 @@ +# pylint: disable=C0111,R0903 + +"""Test module +""" + +import bumblebee.engine + +ALIASES = [ "test-alias" ] + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text="test") + ) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/traffic.py b/.config/i3/bar/bumblebee/modules/traffic.py new file mode 100644 index 00000000..0da9fd1f --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/traffic.py @@ -0,0 +1,72 @@ +# pylint: disable=C0111,R0903 + +"""Displays network IO for interfaces. + +Parameters: + * traffic.exclude: Comma-separated list of interface prefixes to exclude (defaults to "lo,virbr,docker,vboxnet,veth") +""" + +import re +import psutil +import netifaces + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widgets = [] + super(Module, self).__init__(engine, config, widgets) + self._exclude = tuple(filter(len, self.parameter("exclude", "lo,virbr,docker,vboxnet,veth").split(","))) + self._update_widgets(widgets) + self._status = "" + + def state(self, widget): + if "traffic.rx" in widget.name: + return "rx" + if "traffic.tx" in widget.name: + return "tx" + return self._status + + def update(self, widgets): + self._update_widgets(widgets) + + def create_widget(self, widgets, name, txt=None, attributes={}): + widget = self.widget(name) + if widget: return widget + + widget = bumblebee.output.Widget(name=name) + widget.full_text(txt) + widgets.append(widget) + + for key in attributes: + widget.set(key, attributes[key]) + + return widget + + def _update_widgets(self, widgets): + interfaces = [ i for i in netifaces.interfaces() if not i.startswith(self._exclude) ] + + counters = psutil.net_io_counters(pernic=True) + for interface in interfaces: + if not interface: interface = "lo" + data = { + "rx": counters[interface].bytes_recv, + "tx": counters[interface].bytes_sent, + } + + name = "traffic-{}".format(interface) + + self.create_widget(widgets, name, interface) + + for direction in ["rx", "tx"]: + name = "traffic.{}-{}".format(direction, interface) + widget = self.create_widget(widgets, name, attributes={"theme.minwidth": "1000.00MB"}) + prev = widget.get(direction, 0) + speed = bumblebee.util.bytefmt(int(data[direction]) - int(prev)) + widget.full_text(speed) + widget.set(direction, data[direction]) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/weather.py b/.config/i3/bar/bumblebee/modules/weather.py new file mode 100644 index 00000000..683f61df --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/weather.py @@ -0,0 +1,74 @@ +# -*- coding: UTF-8 -*- +# pylint: disable=C0111,R0903 + +"""Displays the temperature on the current location based on the ip + +Requires the following python packages: + * requests + +Parameters: + * weather.interval: Interval (in minutes) for updating weather information + * weather.location: Set location (ISO 3166 country code), defaults to 'auto' for getting location from http://ipinfo.io + * weather.unit: metric (default), kelvin, imperial + * weather.apikey: API key from http://api.openweathermap.org +""" + +import bumblebee.input +import bumblebee.output +import bumblebee.engine +import json +import time +try: + import requests + from requests.exceptions import RequestException +except ImportError: + pass + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + super(Module, self).__init__(engine, config, + bumblebee.output.Widget(full_text=self.temperature) + ) + self._temperature = 0 + self._apikey = self.parameter("apikey", "af7bfe22287c652d032a3064ffa44088") + self._location = self.parameter("location", "auto") + self._interval = int(self.parameter("interval", "15")) + self._unit = self.parameter("unit", "metric") + self._nextcheck = 0 + self._valid = False + + def _unit_suffix(self): + if self._unit == "metric": + return "C" + if self._unit == "kelvin": + return "K" + if self._unit == "imperial": + return "F" + return "" + + def temperature(self, widget): + if not self._valid: + return u"?" + return u"{}°{}".format(self._temperature, self._unit_suffix()) + + def update(self, widgets): + timestamp = int(time.time()) + if self._nextcheck < int(time.time()): + try: + self._nextcheck = int(time.time()) + self._interval*60 + weather_url = "http://api.openweathermap.org/data/2.5/weather?appid={}".format(self._apikey) + weather_url = "{}&units={}".format(weather_url, self._unit) + if self._location == "auto": + location_url = "http://ipinfo.io/json" + location = json.loads(requests.get(location_url).text) + coord = location["loc"].split(",") + weather_url = "{url}&lat={lat}&lon={lon}".format(url=weather_url, lat=coord[0], lon=coord[1]) + else: + weather_url = "{url}&q={city}".format(url=weather_url, city=self._location) + weather = json.loads(requests.get(weather_url).text) + self._temperature = int(weather['main']['temp']) + self._valid = True + except RequestException: + self._valid = False + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/modules/xrandr.py b/.config/i3/bar/bumblebee/modules/xrandr.py new file mode 100644 index 00000000..a1db224f --- /dev/null +++ b/.config/i3/bar/bumblebee/modules/xrandr.py @@ -0,0 +1,74 @@ +# pylint: disable=C0111,R0903 + +"""Shows a widget for each connected screen and allows the user to enable/disable screens. + +Requires the following executable: + * xrandr +""" + +import os +import re +import sys + +import bumblebee.util +import bumblebee.input +import bumblebee.output +import bumblebee.engine + +class Module(bumblebee.engine.Module): + def __init__(self, engine, config): + widgets = [] + self._engine = engine + super(Module, self).__init__(engine, config, widgets) + self.update_widgets(widgets) + + def update_widgets(self, widgets): + new_widgets = [] + for line in bumblebee.util.execute("xrandr -q").split("\n"): + if not " connected" in line: + continue + display = line.split(" ", 2)[0] + m = re.search(r'\d+x\d+\+(\d+)\+\d+', line) + + widget = self.widget(display) + if not widget: + widget = bumblebee.output.Widget(full_text=display, name=display) + self._engine.input.register_callback(widget, button=1, cmd=self._toggle) + self._engine.input.register_callback(widget, button=3, cmd=self._toggle) + new_widgets.append(widget) + widget.set("state", "on" if m else "off") + widget.set("pos", int(m.group(1)) if m else sys.maxsize) + + while len(widgets) > 0: + del widgets[0] + for widget in new_widgets: + widgets.append(widget) + + def update(self, widgets): + self.update_widgets(widgets) + + def state(self, widget): + return widget.get("state", "off") + + def _toggle(self, event): + path = os.path.dirname(os.path.abspath(__file__)) + toggle_cmd = "{}/../../bin/toggle-display.sh".format(path) + + widget = self.widget_by_id(event["instance"]) + + if widget.get("state") == "on": + bumblebee.util.execute("{} --output {} --off".format(toggle_cmd, widget.name)) + else: + first_neighbor = next((widget for widget in self.widgets() if widget.get("state") == "on"), None) + last_neighbor = next((widget for widget in reversed(self.widgets()) if widget.get("state") == "on"), None) + + neighbor = first_neighbor if event["button"] == bumblebee.input.LEFT_MOUSE else last_neighbor + + if neighbor == None: + bumblebee.util.execute("{} --output {} --auto".format(toggle_cmd, widget.name)) + else: + bumblebee.util.execute("{} --output {} --auto --{}-of {}".format(toggle_cmd, widget.name, + "left" if event.get("button") == bumblebee.input.LEFT_MOUSE else "right", + neighbor.name)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/output.py b/.config/i3/bar/bumblebee/output.py new file mode 100644 index 00000000..992eec6e --- /dev/null +++ b/.config/i3/bar/bumblebee/output.py @@ -0,0 +1,140 @@ +# pylint: disable=R0201 + +"""Output classes""" + +import sys +import json +import uuid + +import bumblebee.store + +def scrollable(func): + def wrapper(module, widget): + text = func(module, widget) + width = widget.get("theme.width", module.parameter("width", 30)) + widget.set("theme.minwidth", "A"*width) + if len(text) <= width: + return text + # we need to shorten + start = widget.get("scrolling.start", -1) + direction = widget.get("scrolling.direction", "right") + start += 1 if direction == "right" else -1 + widget.set("scrolling.start", start) + if width + start >= len(text): + widget.set("scrolling.direction", "left") + if start <= 0: + widget.set("scrolling.direction", "right") + text = text[start:width+start] + + return text + return wrapper + +class Widget(bumblebee.store.Store): + """Represents a single visible block in the status bar""" + def __init__(self, full_text="", name=""): + super(Widget, self).__init__() + self._full_text = full_text + self.module = None + self._module = None + self.name = name + self.id = str(uuid.uuid4()) + + def link_module(self, module): + """Set the module that spawned this widget + + This is done outside the constructor to avoid having to + pass in the module name in every concrete module implementation""" + self.module = module.name + self._module = module + + def cls(self): + if not self._module: + return None + return self._module.__module__.replace("bumblebee.modules.", "") + + def state(self): + """Return the widget's state""" + if self._module and hasattr(self._module, "state"): + states = self._module.state(self) + if not isinstance(states, list): + return [states] + return states + return [] + + def full_text(self, value=None): + """Set or retrieve the full text to display in the widget""" + if value: + self._full_text = value + else: + if callable(self._full_text): + return self._full_text(self) + else: + return self._full_text + +class I3BarOutput(object): + """Manage output according to the i3bar protocol""" + def __init__(self, theme): + self._theme = theme + self._widgets = [] + self._started = False + + def started(self): + return self._started + + def start(self): + """Print start preamble for i3bar protocol""" + self._started = True + sys.stdout.write(json.dumps({"version": 1, "click_events": True}) + "\n[\n") + + def stop(self): + """Finish i3bar protocol""" + sys.stdout.write("]\n") + + def draw(self, widget, module=None, engine=None): + """Draw a single widget""" + full_text = widget.full_text() + padding = self._theme.padding(widget) + prefix = self._theme.prefix(widget, padding) + suffix = self._theme.suffix(widget, padding) + minwidth = self._theme.minwidth(widget) + if prefix: + full_text = u"{}{}".format(prefix, full_text) + if suffix: + full_text = u"{}{}".format(full_text, suffix) + separator = self._theme.separator(widget) + if separator: + self._widgets.append({ + u"full_text": separator, + "separator": False, + "color": self._theme.separator_fg(widget), + "background": self._theme.separator_bg(widget), + "separator_block_width": self._theme.separator_block_width(widget), + }) + width = self._theme.minwidth(widget) + self._widgets.append({ + u"full_text": full_text, + "color": self._theme.fg(widget), + "background": self._theme.bg(widget), + "separator_block_width": self._theme.separator_block_width(widget), + "separator": True if separator is None else False, + "min_width": width + "A"*(len(prefix) + len(suffix)) if width else None, + "align": self._theme.align(widget), + "instance": widget.id, + "name": module.id, + }) + + def begin(self): + """Start one output iteration""" + self._widgets = [] + self._theme.reset() + + def flush(self): + """Flushes output""" + sys.stdout.write(json.dumps(self._widgets)) + + def end(self): + """Finalizes output""" + sys.stdout.write(",\n") + sys.stdout.flush() + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/store.py b/.config/i3/bar/bumblebee/store.py new file mode 100644 index 00000000..8fac71ba --- /dev/null +++ b/.config/i3/bar/bumblebee/store.py @@ -0,0 +1,21 @@ +"""Store interface + +Allows arbitrary classes to offer a simple get/set +store interface by deriving from the Store class in +this module +""" + +class Store(object): + """Interface for storing and retrieving simple values""" + def __init__(self): + self._data = {} + + def set(self, key, value): + """Set 'key' to 'value', overwriting 'key' if it exists already""" + self._data[key] = value + + def get(self, key, default=None): + """Return the current value of 'key', or 'default' if 'key' is not set""" + return self._data.get(key, default) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/theme.py b/.config/i3/bar/bumblebee/theme.py new file mode 100644 index 00000000..e16fbb65 --- /dev/null +++ b/.config/i3/bar/bumblebee/theme.py @@ -0,0 +1,180 @@ +# pylint: disable=C0103 + +"""Theme support""" + +import os +import glob +import copy +import json +import io + +import bumblebee.error + +def theme_path(): + """Return the path of the theme directory""" + return os.path.dirname("{}/../themes/".format(os.path.dirname(os.path.realpath(__file__)))) + +def themes(): + result = [] + + for filename in glob.iglob("{}/*.json".format(theme_path())): + if "test" not in filename: + result.append(os.path.basename(filename).replace(".json", "")) + return result + +class Theme(object): + """Represents a collection of icons and colors""" + def __init__(self, name): + self._init(self.load(name)) + self._widget = None + self._cycle_idx = 0 + self._cycle = {} + self._prevbg = None + + def _init(self, data): + """Initialize theme from data structure""" + for iconset in data.get("icons", []): + self._merge(data, self._load_icons(iconset)) + self._theme = data + self._defaults = data.get("defaults", {}) + self._cycles = self._theme.get("cycle", []) + self.reset() + + def data(self): + """Return the raw theme data""" + return self._theme + + def reset(self): + """Reset theme to initial state""" + self._cycle = self._cycles[0] if len(self._cycles) > 0 else {} + self._cycle_idx = 0 + self._widget = None + self._prevbg = None + + def padding(self, widget): + """Return padding for widget""" + return self._get(widget, "padding", "") + + def prefix(self, widget, default=None): + """Return the theme prefix for a widget's full text""" + padding = self.padding(widget) + pre = self._get(widget, "prefix", None) + return u"{}{}{}".format(padding, pre, padding) if pre else default + + def suffix(self, widget, default=None): + """Return the theme suffix for a widget's full text""" + padding = self._get(widget, "padding", "") + suf = self._get(widget, "suffix", None) + return u"{}{}{}".format(padding, suf, padding) if suf else default + + def fg(self, widget): + """Return the foreground color for this widget""" + return self._get(widget, "fg", None) + + def bg(self, widget): + """Return the background color for this widget""" + return self._get(widget, "bg", None) + + def align(self, widget): + """Return the widget alignment""" + return self._get(widget, "align", None) + + def minwidth(self, widget): + """Return the minimum width string for this widget""" + return self._get(widget, "minwidth", "") + + def separator(self, widget): + """Return the separator between widgets""" + return self._get(widget, "separator", None) + + def separator_fg(self, widget): + """Return the separator's foreground/text color""" + return self.bg(widget) + + def separator_bg(self, widget): + """Return the separator's background color""" + return self._prevbg + + def separator_block_width(self, widget): + """Return the SBW""" + return self._get(widget, "separator-block-width", None) + + def _load_icons(self, name): + """Load icons for a theme""" + path = "{}/icons/".format(theme_path()) + return self.load(name, path=path) + + def load(self, name, path=theme_path()): + """Load and parse a theme file""" + themefile = "{}/{}.json".format(path, name) + + if os.path.isfile(themefile): + try: + with io.open(themefile,encoding="utf-8") as data: + return json.load(data) + except ValueError as exception: + raise bumblebee.error.ThemeLoadError("JSON error: {}".format(exception)) + else: + raise bumblebee.error.ThemeLoadError("no such theme: {}".format(name)) + + def _get(self, widget, name, default=None): + """Return the config value 'name' for 'widget'""" + + if not self._widget: + self._widget = widget + + if self._widget != widget: + self._prevbg = self.bg(self._widget) + self._widget = widget + if len(self._cycles) > 0: + self._cycle_idx = (self._cycle_idx + 1) % len(self._cycles) + self._cycle = self._cycles[self._cycle_idx] + + module_theme = self._theme.get(widget.module, {}) + class_theme = self._theme.get(widget.cls(), {}) + + state_themes = [] + # avoid infinite recursion + states = widget.state() + if name not in states: + for state in states: + state_themes.append(self._get(widget, state, {})) + + value = self._defaults.get(name, default) + value = widget.get("theme.{}".format(name), value) + value = self._cycle.get(name, value) + value = class_theme.get(name, value) + value = module_theme.get(name, value) + + for theme in state_themes: + value = theme.get(name, value) + + if isinstance(value, list): + key = "{}-idx".format(name) + idx = widget.get(key, 0) + widget.set(key, (idx + 1) % len(value)) + value = value[idx] + + return value + + # algorithm copied from + # http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts + # nicely done :) + def _merge(self, target, *args): + """Merge two arbitrarily nested data structures""" + if len(args) > 1: + for item in args: + self._merge(item) + return target + + item = args[0] + if not isinstance(item, dict): + return item + for key, value in item.items(): + if key in target and isinstance(target[key], dict): + self._merge(target[key], value) + else: + target[key] = copy.deepcopy(value) + return target + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/bumblebee/util.py b/.config/i3/bar/bumblebee/util.py new file mode 100644 index 00000000..9efa146f --- /dev/null +++ b/.config/i3/bar/bumblebee/util.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +import shlex +import logging +import subprocess + +try: + from exceptions import RuntimeError +except ImportError: + # Python3 doesn't require this anymore + pass + +def execute(cmd, wait=True): + logging.info("executing command '{}'".format(cmd)) + args = shlex.split(cmd) + proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + rv = None + + if wait: + out, _ = proc.communicate() + if proc.returncode != 0: + raise RuntimeError("{} exited with {}".format(cmd, proc.returncode)) + + if hasattr(out, "decode"): + rv = out.decode("utf-8") + else: + rv = out + + logging.info(u"command returned '{}'".format("" if not rv else rv)) + return rv + +def bytefmt(num): + for unit in [ "", "Ki", "Mi", "Gi" ]: + if num < 1024.0: + return "{:.2f}{}B".format(num, unit) + num /= 1024.0 + return "{:.2f}GiB".format(num*1024.0) + +def durationfmt(duration): + minutes, seconds = divmod(duration, 60) + hours, minutes = divmod(minutes, 60) + res = "{:02d}:{:02d}".format(minutes, seconds) + if hours > 0: res = "{:02d}:{}".format(hours, res) + + return res + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/runlint.sh b/.config/i3/bar/runlint.sh new file mode 100755 index 00000000..6902ce9e --- /dev/null +++ b/.config/i3/bar/runlint.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +find . -name "*.py"|xargs pylint --disable=R0903,R0201,C0330 diff --git a/.config/i3/bar/runtests.sh b/.config/i3/bar/runtests.sh new file mode 100755 index 00000000..60f058eb --- /dev/null +++ b/.config/i3/bar/runtests.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +echo "testing with $(python2 -V 2>&1)" +python2 $(which nosetests) --rednose -v --with-coverage --cover-erase tests/ + +if [ $? == 0 ]; then + echo + + echo "testing with $(python3 -V 2>&1)" + python3 $(which nosetests-3) --rednose -v --with-coverage --cover-erase tests/ +fi diff --git a/.config/i3/bar/screenshots/amixer.png b/.config/i3/bar/screenshots/amixer.png new file mode 100644 index 0000000000000000000000000000000000000000..0da57c509dc21c9491caabf7481508b4e9cb9adb GIT binary patch literal 1645 zcmV-z29o)SP)X0ssI2|AFK?00003b3#c}2nYz< z;ZNWI000nlMObuGZ)S9NVRB^vQ)qQ`bY*g5g3t*700s$3L_t(o!{u06OjBtT{_pL+ z(UrDzgO){FimZx)EW$7Yii|R1fH)JQW12#wep= za6wsQk)0L_2*@T3Sm{ES+uq)`^H9WEYD?y6kn{Ba=Y03v`~Cm<&pH2vK+dq6cg=>iP4lZ4q@JRTP#5NvglI2dIzbz#`TFr1s5WOF#TG*uXK%VhPaD*^!U z&Pav;*sDquaM)X#Dg=2Yk_I|$c8&SFaOpeZq=Lw>?np#D1VO&>x#|y(6lM?!`2P)6 zAQCn)BgWr^G~kOj;?#<0WwgvUHd?G~20f^|C?4zl$@2MS!z+^o2Sd8)#VmE~{#~k` zk>_rYXCq!Fzp9(;v)DE6Yn& zDMXrQb6zhGxzjRm>TrG(kL5GbDde5;vOjJ= z`R3R96|?p1u@7)K*!PN*1nI@gjZ_jTO&Np3!Q}<%Hy-zTz22~&$K?zP`0asbIAAUn zL}n(&hwvg1a1vy~o$=DT<0T9#c`bFraD8*X4RM}1T6pzdXHu+~&t(QY7#+n0UeMY% zhPtr+v6%(EF;=#g?X8I8`>XdS=9UbrwmAF9@HCFDI`L?Ob7sA(8vN##KN*6Shav*81{Mq0N@|JNz7+c$V8c#w_>uC z=A_(w+!vNAjXo!pG`vnGZ#(xtw|i8HbSfnP5pZtH^3nnTy(7lfwtZMQCb78DY};;X>bjg|6G zl;{3%?ZJ1K?p*r2rTRd&I_|A#|M{<`x(k;Z+6HT@izDb%f77A7)b@c1!>VmFY+V>_ zdZrS?Xkj850DxlHve6VR5a3`qXe2-C(KU8x9(C&!Qa+bS_eJu+_^i$0JX2ZNubX+H zHy+4IxzgB)VQ;NSW%2Hb*`@B`DFDFoikU_xspG^wBhURYZjWbrQLmJX2n5{pf_^>u ze_W99TYdAPywsY~9J!dMoqXZ(gi6b9H`!*3X0b*g$Q`Bv87jiceM}*f{K*_H%)Hm3 ziQ+NUani4Uxq*jq**oLI?l9uu&&*bMy|_>n$R{t2=8oa9&}Gd>xNmgY7hn&&hRs&{ zmnX`G#^;)QM>pc14o~e%+mV$V%Vy9rRf_$Y$}Y{6&y=ZB)K(SC#r#M%owu(jb2z$>$PtKa6mUSPM2 rL*4k^OW9s*q3!jDl|-X&AGZGj5@Bj4aW{yP00000NkvXXu0mjf(k>WM literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/battery.png b/.config/i3/bar/screenshots/battery.png new file mode 100644 index 0000000000000000000000000000000000000000..bdbafee42fd9ce6df799d0cd95888d09fe707a75 GIT binary patch literal 1733 zcmV;$20HnPP){x2YX&>x1cG^C)uYGHMs84m;QM9dEwaQWj zI+R5j*<66Ih9!X{k^~aWNNmr1O*S`K?S)Ad5L^eO}2%%1K;#?4r+iZ5Eca({V9Uej7c~z=65`xqK01hYo z^-mF~ue~$yJov7Qw8Z$mg2M3T2VB1#g#9L#}HTsI*~ps;xLru{B=rZg1-3O)97qvc0@` z0teB4DWkcI`|)uJ#O99GhQ~pa5bErbc);a~)lp1K;HR3qc%6N0zgnBQkpNv4(`IOBm{@1 ziF9}F#Q*?uGt{Mf*3fI*H<>o2%CUwbod+DJ-~|LnDR1b005WN+bY#P;`D0qei9EyVL4e?4182~Uf6Yc?*?*UiWCg|vm z$`z{*=gWPcl1Le~EwKTng%En!#q$ks6^&zA#_j=@$r7^eusEJkz%}X&gd2_VZx&7X zcFpQY-pv9b5b_PvGvRJ_4BEni4VhF@;&?`rB}A(yaw07N05;PyGe=smBMLQnr&!(4 z&aW&R!5IF1qs$Q*&HIx~avaE0c)+y^I{Kmj0I^)7?;{h_)ZZHuP;{M{J#n!=TZtg( z=B8v0%W~%3KIv4%igqHwG4G>(a5&Vv7&MCgK%U}KP2%63yz@7<=8DyPjtKz3qA%>q zoH_Sf8jo$BohOQ7o^e?gjBX5!Ez9&~E57P>(t;iFf;&!n@aQL zzto9bC}h(YQiVFN&XTJrHyYzly)hicvRSj>qrFW z>PE8_4*)3HGnJj@M+Q*B7Ec}>{_%QB;Ow-t;JRmos_G=09oZ~K26JdjH$;H?H@~KGaKQj_ zmCLf6I4liN23Bh0NhS4Ky|AK0y1M4#AZoVaKdZ~pw=+|HwkqRa zoGTDl@$P9qp^gJabLjb-TM>w+2=xbdEh!jr(wx%?+j=Ag^4S000ocp%GHl)o>Uw*zE@Zk4iyK=C9+TqA03*q)AGk{_oRB#LhP_ zz}}}II|~>CU{tHKyWm{|(WulHDoXY(Cjr9R8kh1iJ=-fgET-lbO?D@)KY@^!nMNQG zAP7>Pk)M-Smlfs%0B97-@l@Xa;e;U2A~T8wF5I&^yKG!ATVjMrOrXB^`Z<4a!)S99 z=42rVqHL6x7v>AIGfg(<=hwUN9LXfo=~78!<*BX#)ueVV9@UFA6@siZw=d9fcK}7v zQX#*oru@_PP7K3#MXj%}NQ)e}ZE4YBoz)xS`DQ4Jsx|XB`UZXe0FTWq7ZnubWW})O zBp-QKsc^z>2#QyAEQKu*u}HT=og*cOh!_vkWZzMBOPIn_mlR{FMqn@ z4{pS%I?85x`~d&}?DigIvuaDkF1JUkU)hyYEO52T4YSBjzs3A}zIx*q^Yf=2#jwrLR`deFQLV1Q>D&|=g_4!Z8=9I?$f~~T z>fMzyH`DWK<5FbpY-e>=Z$MErc9ij1oyX^Q!fw6Q7S&)efCu~~l`OlJIM`Aj}6L?Uj56951;;zBx&7CVYB5WGD&VqIIuGjO;b2RAow z_Wu#jaf(LTcKAeGZtAse;bw@4i~6L& z))h$zgy;`|N$vd41H*c&?X@O36mjsAXbCNKQnhxzy}N%McJCFcLRR(rs5+I)5$9!p z{LPOig$k_V@$Wdhb14SPq55d&0@jOiSgkRMQ#&Krj?S5X9~E5%AjU^P;yLx%C@=N7LAO z-Ou21PYJ_n-F%Ed#re6yZOVzAm)Rvi~# zX$Bb9qZ*6TU9-fx^JliAetX73%1a+avC|v<8ym|JbA>`8HP=aJ7WHPk^TRjWcx=|t zgP9)Hm?$@!#Ynn8IlbR5{q+@A=8?^^LMH%#D3;blTJTs*Duo=0ilC^@V1hkf1VNY0 zs~J3Q$K8R@R(N&Ig*R{iTQOQ0{vf$b{@Arvd7+6`QM}Nc} hU^zsn(Bb(x`VRs*2Jffwcy<5)002ovPDHLkV1oRXbDIDF literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/caffeine.png b/.config/i3/bar/screenshots/caffeine.png new file mode 100644 index 0000000000000000000000000000000000000000..54447779afbf96b58344d131d7c390685fd770b4 GIT binary patch literal 1143 zcmV--1c>{IP)6c$KL}; zVOS9n4u}XiRHV{rp@wQZjWsr|-I8V5VzMpEk|n!Xvb!bQUGI0hCEG66rA?D&YqQkV z8SU&O6q&1E6Qod)4>=q~D2RL&1*gPXn3J|W@cdEWD$^Lx+xf1c-s#yp12`r&c5$F^K?Bd7F~|bhY|ax191jKtT@J%lkn;Jw(Xz&2ojCeDG#c%K=2Rk; z{?}7J2nWDyvj@VFqt62Ya89j)8+cVV-jc)V3k1n#8PuNU*K^6Du|(49bfcj#^{X&z zY9Od|U;LTP{a2(j9ZD%IsG=pS%e#*FDP|ebYfo}spcruZeD95)XBKQv%0n$8;b@<( zMI@}N<&;N5yU`6~Yr;4miNz1X)72_#YnD_=4^BU7b$kBzHhA#+vSHcs(VMTmH8Qa5 z*vRFIR(HA~XC+@Fp zc&VSIlr(iIMO z-G_d4`<}y#y!YxQ2!e9C9I_J{H<+)DPrdWX#fqPC-=wfWr)ncj7=^z;WY=u7KNHDH z1bhI%UIHUJhkJV_jPp;UdqsH&$M4S033>H0v2fpM_xRSFp8l8AWb?zk`g3X(RSx`H zLT*n!pFbo_yR7+x*GF~g_LV2DI97xt7YiMm6(pL?WW zQkhH@Bmh7yn?2C2sbSEmk?zbGcfz|^CR6lFr|ll!!xbypUDLlUj`p1uHq;k?4d{)b z^Jpl%NhpIFrMyEXHIYvR0ElClVZ}mL$DooWjQKQNRrT@ z6zoEG*Oiy`_ve@F?#*}-lfaF`y*(T@`5G2FW>g`{Q<^HC|hD&|A7Ll;A ztpCX4`1aF?3qdDMkMr|ja=?=Imw2i(E}kJQ+p1P{sQQ|s$03vvpfI*002ov JPDHLkV1iwhDMA1M literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/cmus.png b/.config/i3/bar/screenshots/cmus.png new file mode 100644 index 0000000000000000000000000000000000000000..52c0b5159e2d2eb8dece597c177b523109319651 GIT binary patch literal 5031 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 M{86v z$))REJztsgvIN$JCI9v$n22nCHtU2@-+zBJf*?t_vbgMeed!-Q z_*h@8L=a@j<@w>M=RWDVyX=NBRhDOqbOrg*uWql8mCi_(I&6+*)5J99b&Dk8^KZ1C zFzO{j{=Jd$-@JDb_WIOv>FdpnKYRI9CL?XfsB*O;91gel4SCo69RsFx2K}%`f$8Eh zGxb{KpW3cl?G6|0{iyS{(}Vrwm0IQO{NnAeAGp1~Yd8DC;f*p?QF1N;o4LB?2LQNX zuYk=m98|mDRm<#SOs*UOuS`*ptU|_pcep)+mI=)3D)MsPIDKq%#=5!UP!vR0*0jyO z9t!{b`==Q+T4G?SLOwV$u{BAn2`JFYrI;?IEZ4f=h|MH;AmA_;oUU&RJBm)s*`y+2 za;^oZJDbO)k;%D2{tCR>P*M8nok46O7E`ZOV6-Cfsd?6lAV~6wV!_!*Abw_3hTvDwzb2(1Boxl}?XsRnzIT(^wQ|Rq7z<;mmQJI}b3`vU9%-m3-8A|Ffj?gP3_;OzC+c5oZX^&0 z#RW1kpBop;pi$S?Lt+8%WNqbzwlD2Y7mY%}bkV3}U12^Eg5JCK#lY~`>n9s`tn6_x zS*ElFvLcBX0HBfQRTZm9TXH1f>iO6G^MAh7(LZF+soML7@VwFfQHw7S*xSyKN+#DH z)O4H7!BA+o7;S-Uv!3X+%BXQ-)+QHcK?FjfL>xQv$&xcRc0C*x@ij{TBf6t$$Qv>GiWrUPSf*^IlQrRyT)T?UTp7jxZJ;J zJri9rFlyyI%T^ad2eZWSIg0a5B=Dx5`=)m)Gy7 zQl37;g43lfkm8za!HP0+1G}MC+EU?e_-FhRR&1$MD&k^$Onny8R!&47K z;c#nH9haH;j5JqYtQ>q`iM?!nMd|N;{3@538Eed7&`%zz5^`ACeD1ju4T~$T$t|Ii zaLIE-LJq6XJO%&=ghG)>1orx%opeaVk{ccvo!D_}v-5`K+Z(0RsbsRo^QONCghoZ5`vFi7)he9GQCvL++X!+?clShut&aDN5kL-(4 z3yn-Z|K=-n8Z~*~@bse#pLG0xo_MIF5;~PC+iKf!2@L>n!f?0A9BVm1B9e$0N383i z5Q5`;;C~g|%*>-@x;-5HZ^pz~!W)s~RX7DF4C6f}P2oTJYd}hZ{rW*5^>>4P0f|Cq lq*U0C86EN4QY!2N{tbN4*5EiO503x<002ovPDHLkV1oAH?MDCr literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/date.png b/.config/i3/bar/screenshots/date.png new file mode 100644 index 0000000000000000000000000000000000000000..36621e7485fd2123d327c56a95d5cc48ccf97d98 GIT binary patch literal 2877 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 LNkvXXu0mjfW)Onx literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/disk.png b/.config/i3/bar/screenshots/disk.png new file mode 100644 index 0000000000000000000000000000000000000000..7a659b8fcdfeb1468d05e49f29f1c98fe3098c53 GIT binary patch literal 3721 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 literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/dnf.png b/.config/i3/bar/screenshots/dnf.png new file mode 100644 index 0000000000000000000000000000000000000000..49924168f31d1a081be9a94ab4063993d73d31ed GIT binary patch literal 1113 zcmV-f1g86mP)*HlGt&bp1w4y`s&)?Z*BoZ%eLrdRf6Mp1HxTG@04|M= z%mP7l`s~YB?n&6=U>RYzCI!DZ#pa_#ObuUniDhBICQgYM8z5?EaHZXt5j;Blc!VHu z(G}|MNU_5e0-Ahe^1eV3d1dhQdbcI@uY6NF10!`6nUvryRjZF~N7#8X#tHwjV0hxy z0RRfylG~H*x({$o!#3MTXJvP$&7p|GXvklFd-bTYNEgH*wX-L7 z#u;W|z8$x2B#<50-kIFFdTgm2E&~8OoHsmP)K^mk34J$VZPDgG{b;F0o4OUvk~uK3RV#Lvel0G_WlIC}Q29#{r`7HGUN{FJv;P2=&5BPd`6 zbrzT2{IT=;kWYb>nSimW);ZQyWAv*x0019f_P4d{t372hGMYrDo-})0A-x(8hL6ak zqlT#GKxTk19POlk}MX3SPBG4{iIXIDo@X5T*_VA=wGeq(lYWcGlG0013| z-@SkAcpGN7B>@0f7DiS7R29XNcxFlGIu+q9_1bk$B#zv=+d;6Ve7zc{eczDEMAJ~h)EiPby*PK6Z$dc3H2^=6NvYPy32xzMF%@UB%+SDVO~G_({DzKg07tl z>=thGK5Zmc?->YI_YF@dCQZ-0at~rZe{?${N0r6C6Y&Gv!3JfKb~jxipos^q&Xb!4 zhKWzB<&cUbq0X8o(SJ{IonW fP%Zzt1aaywgMS}|9}1IZ00000NkvXXu0mjfah(r0 literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/kernel.png b/.config/i3/bar/screenshots/kernel.png new file mode 100644 index 0000000000000000000000000000000000000000..9d20cdc9aed5a357b288e68226ae9d794fe36d40 GIT binary patch literal 3125 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 P00000NkvXXu0mjfu95r# literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/layout.png b/.config/i3/bar/screenshots/layout.png new file mode 100644 index 0000000000000000000000000000000000000000..d09250e9638fc5df618c340e912420423ff36e72 GIT binary patch literal 1198 zcmV;f1X25mP)X0ssI23CLli00003b3#c}2nYz< z;ZNWI000nlMObuGZ)S9NVRB^vQ)qQ`bY*g5g3t*700c@&L_t(o!|hmIOj~6b{?0i) zr>FFX_LTmtg~4c9S2|$8V91b(f@pSuS(YqDO^n_d6K^n^H(r?C7`-tu(HLV4I=SRx z1C-6ND40<@p`}27p$nx@T25){?>X%`UUY7wEg-N2h<=yP_rBkG-uJxU^PcxWz|ahi z?1BbA-NE8w-{B1p21j{uw%JS2$moLfNV2Ch(Di#&a(?PCvoBmZ3=4feu9;dEt6De ztCTB2@*vswThxPg`8Ga0F1NML&{}7(I2Vd=?B_pB59XZvAyI^L#R9%>_hIL| z;;V^@ZLGV_H4Kd#1EFvZt|}o^CB&=eo5=767ekFR_Fa+S^YG<8)!P&H<`vOrV;Xkb ze+N zZr^{9-FoX{XR|>E0648Z(bduj0Ql(3@1pU0DvEde!Ve#xA)D<#u?jh zlU4u#!!UbM%xmo~rC?`umrKNJ>k*2g$EU1Wgi5EaZqKC`+h!LQ3%aBzdSG7~`6Y1UC)dHf%JV;P3QFitd)WEf^w)Hmf`!lSQu*?Nbo*OTZr4oiVGIU%7l8AD}- z)$IWQm}cxO3vT{?U&JqZxuv0CE4a{DTYpNcluBR-hGE$La8sNZheM|``&GOg#j(;;SluS|iE|FMtOvv_W1mVXgYx)pqEASaf+sMYUBUO@W00%I?{hcj&|<<21H-Oxm^CSv;Y7A M07*qoM6N<$g3G@`@Bjb+ literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/load.png b/.config/i3/bar/screenshots/load.png new file mode 100644 index 0000000000000000000000000000000000000000..e136e7e9e1f0e88f5d84e7f4fc2a1b98686f19d8 GIT binary patch literal 2005 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 literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/nic.png b/.config/i3/bar/screenshots/nic.png new file mode 100644 index 0000000000000000000000000000000000000000..d1512c0772b4b3bc2f09f274af9a215af3b366ec GIT binary patch literal 2474 zcmV;b303xqP)X0ssI2ND$sx00003b3#c}2nYz< z;ZNWI000nlMObuGZ)S9NVRB^vQ)qQ`bY*g5g3t*700~A(L_t(|+U;9wP*Z0b{?5t$ zN-pF=Aaad>fFK}(h+suPTM^wZXx(*f-R*Q{-Py6T({*=$bY}l_W@oxH+t$^+^kTPL zt1zn~R76w+xkT;+!X-dRVnRqDAtVqIAZLHXVv2GSLN}#5=b7{4e3$o}=X>&=?|aYt zoj@QV17N_giGjx?z_8hHe1EQI5ylk3fX$;AF!=0_cwB@)fcYqfO$r!>bCMF~796YV zF=;St20#F0ZHp6d*c)2wAqWx+cspXFVZ9RyN#w!o3_gc7F=eW1XdN8a_&ACag?Uk-A>%szmv_@Q1e5iJ(I zeey>l9v1*$G+Sz=op)Q>Yo%TC(Q&iYCg8A1L_$P}AT?gnJ3O}Fbo%~lX^qo0&25kS z2lu5X*LU>T9X{E{h(k}kl3!8RSk=&qAjnS+zSOT&&D!SCt#B~>tK%gj8tvukIu4V5 zcyDHNkKBbI0d`Ps5c4;uUZs*rECx+Jrq&s!H^?xMRw&BTsnnO!lYf8V(|eD*+{cJU z=W@Bc|C*tq6iM_-(QLJ`8T9q}=oTUWY-urxNN~Aa*B>-}UR|fuPR!WmTrStdw0TJN zq_I;*r%<*C`BX9~F($HWXvDF&zS)#QCaudyXRJ1vVqB@yFE|&+b(3L29yf?Npj4w< zWhTXof;pF~>YR&`*qV}ckyW6Zb2~T zQbiRyLln3WWUYwEKZb#{LQ$SdA~_t+bv>>EHY@Af4*KGRl-C};3gyM}Te>OJp5%nW ztTY&gJ(C6DA%gG_!GT?=U)9#X|F3cYfPTv4X|C2AQsX7=c;(#5WAA->b<$*}QOHKq zjMrOdZBO{?wVS#r6OSGA%hF;BnN(5NxT+6{NJy4MUAx=hE(*EqH;M}{T&q-QbSwt_ z)%={)_?U)H832IKWgE;h0D!FR+a%#(<#i2*_hxQzQuKwP@9>XdAgxf8r;?3)*-}~e%yJK~7OEYN{a##dBJ|ctS51j7J=+zZoZm9;Aq8=XRS zIGkYuUP<2W53XE?V3CYKRty`p`gw8+ai68Jf2i;E&2_t2h<^124C zaXMNoa{3ZCLy+GL*Bz?*#W0XoXv(Xe{OR&F_j!+E>`|vIH#G^}M;DyV`Yu_1dJ36H zTr(#X7qN~UzEV4(o-mv{d2IXEC@Pr*0AMlb*(nKUN{Sl0`^;7V@IQLaHB0 z!}wfLFvm1&^(CP8^bZ^i0RUF4UQiK3-vAoh#Tm(U`KWO^Hh0p4wT_S$)?_NKi z8Ygi#O&Cq32XbX2h zqRC<%)#&!7CTR^*zz6RLW<48Z%$Csk1;?~y)?Iu#XBUG;{m1p&tA@v*QQaeNl?_}h zza{M(+_NLm-PAoi3IM2l*y1iGZ;kFBRe2Y2et}7&t`t2fp)>RY{xJ-s6^imO40}4I zFqmWUSY%LOw%QtdWhl?~y7xj=6O&)uzMmQ|Ie)s0&177zx@+;4m{jYH$y=k8Iz0eD zKBnH85Uce_xS;Jl#7WZPB`gL#Enf0U&aV4y-R_kogvZTGPyYAU_aF$6hy)Um;GWk3 z0LS)azfoKmAreqXr0@_yWN65UTI+6V>6Ke-wz7g;K8JlMJ3}nwJ?ifB%0V`wOo@#N zW(5HN(&8m?5n^8$`UkHJ3o_IG_|_>mneW94Mfv?ZQ_hwZ$3=+gR7zUBWKT+BV^^P# z12>p0&3%I}ly2FD`1wtVv~N%>r3Z4^OuErD^JUHb z&OwFmV}D{ab`0UuAet(A#K@jr4+@yKuRB6}n=-t*1uvxMB8UEmq5E)J* zlUMD9`v2V%67XF_4FC7&SE|Nz`i;h;fdjPZ6@@UT`tC7hu{~lqpVE_OC07*qoM6N<$f`yF3^Z)<= literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/pacman.png b/.config/i3/bar/screenshots/pacman.png new file mode 100644 index 0000000000000000000000000000000000000000..8f4169e3f1b7eeb12ff9a5947b4386ff071d929a GIT binary patch literal 860 zcmV-i1Ec(jP)q%SB`UCnqYCUSL&8;=pY7SZ=CAy(9LFNyT zLj(CG#|;eR^z4B*5{?pF4{np!v+sRBuif_E_w&8)_gIgfzW{}J|J4l6F%T$Ihzk0n zgM&jf6e{PE14)t=#|odzIp?|L^wALjR@Z7MhL!s$Czr)F_jV}qe_v237IF|aldV(w z$hqm;_~|d+l!%0n?-`owE~A0qyQyz6JW;T0d`Q0&jeVS+WvoR<)!ay}7q>s)^rFRw z0z=T#2g6&z@XJ?kw}RnkVJB3Sn`pYqiw&>r%8@ z9h5b8N5?t>npSyUjZ`e^>}WC0FEQ52qgryYUrA*LIlU-JlBMQSC6$D+H~^quqik!e zpD@kT%A|QUw|lit4|?+J#xyH`Tahc=}E?y8g literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/pasink.png b/.config/i3/bar/screenshots/pasink.png new file mode 100644 index 0000000000000000000000000000000000000000..2fd6359522b9984ea4e8df7aed4d5465f718163f GIT binary patch literal 1486 zcmV;<1u^=GP)&%fWE^XtH6W|8?-ex!UH||x4mUp^9d|mj`Qgd1L?FOZAr_1E8jLw~TDD36K~R&bDj1Fc z0MhS>H~?Cc+3E3Ss{|wf>J@SUo3)@w+D>=JtdmX`VJsSzLL!k8?f3WWY*W`$DU|<& z3TrB4VgYaQ5>~EnNa->1aKdb#Ote|KOs_wXHc%m#eeA&A$J1{2tPg^qQJcM1DOcB2 z>r9qtEcSeV3Yq-U+LiCVwjJ>Y9j=*Vij|jpcxTJr&CTT{Mb<}-Py{8US{rIx8`o7z zONOnscs#zQLUypNrB83nL;~gV(#q13MBD2RuyUEHYr*>1F=87h3{=QvcXhVsE@O~k z7y$74rlvQxzWm!IjoXWKwlqKApOc^W>6@K$iTLT#c?KAQj_hu=PfdUJ{m=HP>7yMx z5;1Y8LM~fXCjIKy^9(v|b;Sx8g0^q0`{R0FJRT>^TvjAfN+qctk$f@fbm`0%oDju) zZkbr5l1aKc+nEf;Las2}t4!q4)}v}7y5O%%$)TqIX)#|7x;p6001xI7qD54tCb$aXR_G|B@hdEYvpBWoN}?{ z^v-HcX8b6}4(#db+;wF4wnYg502@@5M>}?&yVl2KFeD=3kmaFDCQbd~#N!bZOB=IB zF4Gzo=Iz+jM2dszr^A<`p18e5JT8MuEfMl(5MN7O&3TQMPyzv~pk5(Q|Gju-2e$E8 zy8ZkQYkht}18{4|7)H^G(&Dj+$ap5B2>Ch8k81wtVL z73frI^J*2HO2rA}LHsC&%}1ljH|0|C;HY(G)+d*gjN0tdqQWV+Cvz1d7$)Lz-fG`+ z_R7u2t{EDYf?^mUmB6;f^?gG|ug{;!L}3)YanBG&Q9Km@089o$y-GzP!%GD^e&W;z zUw-%T*FRV$9g8=0ru+Ia>w6vBZyQEQ07yl`duD6qDm-y{_PnY#SjTmy`v3q=Uao5v zA*28RLM~e=lU~*iwyEonzq9|~j?FlkClCmQGxK@sZMo2YCzMX_rJ%q6007Ya)ZSK>uP{ECAfx~QZR+|hU-)c!s)#JK<}_2#R1biEuWRYK`BpbgegMYHCqy^@#-Bt5)6L0zhl^9(+m#( zD_hYHn~*?Y?`Ach&1!9^6$^Q{^dp3nOe_*|SiO2903d{-(P$L$2O!*>7cuQc`bW&k z#YLLh5de^vlS8Lc9qt*N+A$i7>Bk=U=N9~Coud5mz~F7eD1HXbHPsfoBe`~r#o`9* z_&P=Tj*WFN1pU~3an>Isqz<>WTi?!}h1p?@-(U=Sy1VJ*1XvynaU%-OFpkhKrBH*FbA}|2dmKL+< zwABKHc{#5&ZE4(63TI%~4zLga{c4TTYD-p=l|=#tBpXG2b|J0#CgQ3b2{+_zcpIzz*g;KBGfZ4~0UfzxaK{;`0Z>3Kk2!f2e6`sA>iFnzb43YnR5Q1)p|b7v{d9mm)Eg$XvoU-T7r|w1lokvBm+~MH zV6V^b_IOt=e;|UbMqGgS(N6iOYsq^X{O`3F#3i?qt=!dC(puTCvHLpm7GN)#OxapUfHNPk(ToM#v-qBcvm$B z1ImP40KkIT&SBDs1UQewu-IL@>&n`@hf@MG+U)&mO#+^eX)`_#f}rGyPQB9tXC$cP z=B<)y@k=q-@Gt?!eR6#7g^!P(d2hc|#DBJk42sF>003+&D=f@sx7{A(FzEmQqs8&} zpz70i_sttDtZb@!Mz4EfN!@1J*S#1z=P2y%CH!iykD5+=xT|2hBd7_rFRBY*A>cnqh;*D{Kd;%vWg<-l4pJ) zS^X?n9CT_{ST~sMZ|tfWpVs}OngIZ0Q^_W)b0spDL6sEbb@h)QXskN>;p@j+>v6ag z#{)8vxT9Q>5gV4EyKFAEVtgWD1p@$v|J6>-E}TBL*KBkB@Ta_cP~BWBkyRD{+}Rt) z)L00^cnSspz-CY!?#MnUHM0PKzq%tOS6-B_dh~cDvb9m#)i<8Ypoj~&XTSaf2Vu8u zPEFmkEV68~B$_W0Gl$9M=$q6e{HlaPp)0rgJMNBFiv(v*v>t7#b1!+$U+R2fam0V$ zYeXlHyiy`ylZgbWh+kh>EFYSRH1^oE!@YcDcP)!fX^~arO zfY*lvf&-U1%fn&P>#IsK@o-1a2#N;NfwzG~BEYa=(L&22W0KErB7Q!JKoGEV zqKy(E_k%-CakO#qa@X29F{`w5OI|M~rYNoUdF zz@-!Pe9L*f$QqyG7!(QwUR-&Rv0>8r7qm_VhbNI=RBZnN2G2VCFbjwe00000NkvXX Hu0mjfCfMGn literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/ping.png b/.config/i3/bar/screenshots/ping.png new file mode 100644 index 0000000000000000000000000000000000000000..1c8bf2bec8065882dc2306424b8bafe161b1f309 GIT binary patch literal 1655 zcmV--28j8IP)2#(uY1-zsGwt*t=_Gk*U($5aHrjN_ z9!DFk8Y@OoR74OrL{R|+1VmZx?E{WW>acX0nMnEOK3vYZ-+%7;|8u_o=Ug1%<%6}r zK`}&3e(e=bXveK{vYhz)T9_@ zWWXu@iWM2(oDiEF9pycf!-{yjtboI!>x|}m4WjOWG5=t>K~@ThQR>szx|HYxL}TJbd-zPx z+^RR3X;g|E8Zij@EXi%|k~>k%y+I0@RGOcD@$2e3p~Q75 z#->pe{m)}88BwmtcnmYr*V;RFQ#imtlSpQ>J33{f6Iy*Dhv^3_Hy9e%T)y>0GN>|` zEcMT&5``*1b7^}N<)n^GXzSahdXwc&ZJW)3=J66e8Vn{P3yXHMbz%9kI~*@p#+kIR z!t^v>Y1-L2QV=1T!%~b*QGmI`} z+3-YsG^0g4;NBpage=$j9VA&b_aTN&sIDPRlZ&z>=_dF4tgBym130( za^9;f>5;1}R-gZ85)r~Xy9*mSq*FR003bDv9ZDs2$(GcTo4}@rklXu4+#88z(DHck zIo#Na(!9L%g!x6AT5A9RgiuJ^w{U-}?`RUqPrf1O8=AQFsO8{xzG`|#r#G>}Lq9yW z`}=#%RW-s6>2P*xY!CrII;rzY##^8dt z#eB|@1Q|RLJqRHH0K%wb2ml-o000c8`S0#FHFnCqmGK3e z?a!{>o&0Pff_OjS#2Ch@{emk$)~Gf5tQ5}aL&avRO){w3K&;wUNo}jd*GMA-`2EUK z2!_6`uE(&Y5^Q8bbEmfLTxBT?;U;wku}n$uBG~fL$uDm_blr>P%H~d437^}mPyql? zY^At!I-Sd>AOHZla;l-d|NN0%I{jRaVyv}$aB9Zr<;z0=0JGKFDDJg8oZip5G@mEz z8B}ZZ0D$g+v8E1L@s?CSV0myB1f8uY0RUXDsYDIE9KP|;0t-Ws;4?|^z8AtaN>I~-U+P2ni#hPUc;|O>d2SFQ)CA!UmQl6#tDBDi02qdbP)Hx0*n>JT@qqI8`i>Vj@p@zZ(Iy1|9Ntw(3n6P~=YDwH zBI+OU_!@WXMdgC5b8nWA$wb54d|R*L@v|N;r-1wCCr(~`Y%r0Sz-9#wll=qBpE%c9 z*L@?(Vf9qNNB)QPMNDTj23+%h33dR1U6)qWsV%MQns!Fp8pcw` zw$w%xQ54xl1B4~)LI4SbkmY6}$;~zoEMiD*Zdc6%Qht?(oAZ6=-2XklvwY_!7!aQU z-rHs|{K29El%Y4CG;Z#`K;IP`%GPqhz8}$>YIdRtvRZ9YxqLJC0v;E}V!d;+*K_fV z({8sn_ep$6#LdbQi^b*%;|(VByCvHt7Y~cA1Az8{A-PJuSy^HL$c~F)(WveMB171} zDoWp$9!K!PZ5-L|xOkYIUBhG3a)rm%i3Bg$Vs$ti0DwOn*gGv()V7IWi)G{EOo@)* zu>#$B)8oUBl;tm7r^bbS_n%su-LVm5yW@g{vwwVY^!1F#rh^!?;|I&DY90?sr?FUU z)&2SdI}1)!mS4Dj*CkOeJU%}~xHBvDR(eq|Y$2N?C$)m zpBq}ybV>x5BjAT2$54HJ)_x7}^I0F`O(HrRj+5tZtbXdBOO|Ehu)nJ)lFAf+{>QD! zIpy)n9bU__0}`Wn=dM2VCK8il!*Dq4-oljI4I(tMk-50WIW1R+hDH$@4Q4VTc^qLx z*on$=Uov?`J+8X3-C!~u+qdiH~?{8Kcj3y^nh9JgbvD7qo z=nRXsZ6ZHkpG;v4vWrCv&>9v20BT6bqET~`1u958Ffrw^Q+TYvw3x{M%ev=7BACn; zz0tJ#u~@B3u1rA$Y_Z*I>ed-d51M=Yy~&x05dZ)dgQC?N0RSpU&!SQClA~1`-M}l) z3!z6YE^x}#n)ZPq-{eV^|yU1bRX*eZ(wZau|o*GdeDe;5+AI z4u=_by{D$eM6~xU-?w6W9>xI@R;s&m%`T|9febQ%5W;3EA#HJ1>fOe6kDbDz24u&@ zAjgDpTyLn10AD{J(p$5WOfXxlE`!tJ!rFS4d;jC&3=9S&M)CjvvkDEDNhOmAAzX%1 zquZ5}c&DM$qii%>T;S}VklO8b4+-J=wGx1q9*GHtqeFv-C#OS$m~#s%CY6GIm^10r zd9?-rkev`085;akeG?~;j_i`F)K5j?ua6v@k}H@r>Y#M`HHySdzz@S>J*4y}KPe@6 zxkxN_WC=3_Ti(J+B#;RBuaEBj?yEzmKPyX%e=~wv49dI;0sw4HjEV~7+GlKJf{IKedUQQ~(S7D(Rc&iWZc=!H5shYeZK2T&dy@%Hs|#$k&8xN7A3i^I?nX_s_}3-5!bskVY@!La@YwWyMX48W zJ(*Fc$wb1E>q&>?^!K;!w}^97qV6?zp^=S-iwm420--QjNFoprs;M9yY_V8vwzY*Z zd|v0^m{OyS2@4sVnBsGT<`z`wahfd_P9XiW@(+KiZ<c^EJ^N$Lq-?=rvx$c% zo4bZSNQ+&$ku{h}JGQ^@W?je3yqf5RhuGMkbUO2hd5axlai7I%a~IIqIaK~Z+LI2+ z{buo$T-h@+b^cmiUUIb7V4RvoS{P@Q5G}xO=~|}H94OB09i4tD83zE+1AOOI+BJuQ z0AWN(U3>4oZK>aW@zK%pe7q;w9=W*PLa2Vec?m+~JqET|ez;Y$mS`+L^)SUP*^$ZmyNcjazV!;66(*}}Zkn5ug%PR9ux7QB?u>)Jg{T^Kr zJPw;qZ5B%a07e+L*=&$bk44nHYV&HSt$)bnY0u$sTz}YJ{j579KK%5t@we!gm*-erub4^#uKA>H|siD z#3QiPMP@f4TkyhTcjYFP<|lC&0e2ciqJfDwvPY&f1mSxNQ?Xdgr8|up-Qtq#*#5!? zt-Uh}wMJ*;GHLm#vED>Nbz>Jw+3vVt4xadv$I6n4NrTBWE}Pq|FXH3*p*|#1IETHq zP#DSkeMON|)^q+^-Nuo1<6RdaW(7T5J}ZZ9~a+ z;(|y2v|v-)0tBt!K8+@L;hj?zO0BN0 zwQE;62)l~5+wH>=>Gy5j2BV1`7?8d91h0^UtM+eSghE_?4jtnS4t%_ z^WU|0J(rR4c2#-p)f+Y(-?Ie(7>)w~f*@zhiZGk4p}proHVcDJ%S}mAX>}i8X&9P# zuxc>c9S(&?ceiig%j>rT=zjc!STc#|KG2s!UdJ$l$?Uo?o6Ttno=wCstfhZ&&1h=7 zH%zDbWhEziV!2dVAY=yyeAC>9+wJ?0qd<`U;rZ(FV>xxL-A42JHh22b;_nja>n|18 zUZ~+5Vr|DkCR;4l9Uq&K7&|aF>1Z@og&P;i zY3%F^WidUm03ba+rb3kab#v?1T(i%)!(cKub^ppwihW#wN~Y2Es7w*G#-Oadu-A1^QDr>*~v-nmQ=Rz$+thqmXtOe|8r{gmp{hx`N~I1n5|6Vk@czB#$#WU&ZAkZrCXl~R_MH9R4`db>lTTX8?Ux!xZDf`bBA3`S>r zL^=ZicX|h$x2f?lk{OvNmP<|D{X_zxLVR>Dhc{q{JvqXPVX`HaWLEat^WqQ|b4s>o z-7J<-p}}ufmKx0Kmv6QxwL160RwziXED$O+x^*j-92-@Zm)+7oxU{SV0J&O|$&C+V z2PriAe0~ZqCbGW08?)hivnXVeI5Vw(WZYmf+i+aItbJ81e5h2Z-Db#B98Qsl#6m%O zFq3gKjhmJb!;OwC%gfHsNb&dc)vp-`MkjV0H7{2X351f|%u=Bsgc*3N_ul=#rZ!J> zjh2Rzh>OCwG{^dX!NUen(y%}{^0ln*W~TzA)Q1ZypWeQIz6|b zR9`$(#SUcj-XFc&{~Is*FoW*bH8k?HeX}_CwR^GK9~@i7VF#T&F1mF67PcGk{|O=9 jf2H2vKVBSO9DC5;Z`#53zx_~B00000NkvXXu0mjfg4AvN literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/sensors.png b/.config/i3/bar/screenshots/sensors.png new file mode 100644 index 0000000000000000000000000000000000000000..69d2554251d763a53b5154d8521e8a797c7952f6 GIT binary patch literal 1054 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 literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/themes/powerline-gruvbox.png b/.config/i3/bar/screenshots/themes/powerline-gruvbox.png new file mode 100644 index 0000000000000000000000000000000000000000..5b80ff331c1b61ccb6a32d4731b8e92c0d59f439 GIT binary patch literal 11594 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.Hwr|tlrd|)1 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 literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/time.png b/.config/i3/bar/screenshots/time.png new file mode 100644 index 0000000000000000000000000000000000000000..39d5210fde31641aef160d774e958f9220bd2ab3 GIT binary patch literal 2456 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 Z_&<{MbgwrU3^@P*002ovPDHLkV1hU?S~>s# literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/weather.png b/.config/i3/bar/screenshots/weather.png new file mode 100644 index 0000000000000000000000000000000000000000..1efee833af9d995b1d131bc70b8f08c4b6ccab94 GIT binary patch literal 1111 zcmV-d1gQIoP)f6q^=?> zE=pP+zH;y3?I%wux0{uPxvwY#S!B!MlRJ-}fjWYjImFpkLs9nWtG6q59r*n98(f(H z4|kxGwT6NmBO}B4n|C)KI`QS(cVcYRQIc1cktVIsy7TnemHQ8<64;y^9RL6SubMg^ zF7or&Z#di8%#eqhvt`aQM^l3^SNpjewnN0ZIM|C~L(W{iSvO-LGZRyglTB_!K+mF8 zzyJKjZ=H&?l#ZeTX{Gezm#@#3Dp}O*s3s;dVi#;dLAtwz?ATB6isii@3J$&W>O-tW$|HRf&y13vZ61r!u-CDeL4+8_kuRniYfB3j^*TJW+ z-e@by$N9Sc{Qc+3o%;}FzyJJMvTZM#Y22I~3=9lEfBz=Q4M(qBb1^aC;p8OE!yIgE z4u-mW&RqEM>lg8sZ9aT*&&l)1BHv-!Z#{g{wQv=Leew3aBj+yL8|lJ5j1@qFo1cgK z8&b1@Ao%g?*MakwT}%u(*w{$O!wd`zJe-{N2D+>)%*0#v>C4xb@7^P$PhY-bFC*T4 z5a#E@Z`$uae?NWwhFOsk1Yf>?KX~CXu96&Iau?*~vDMSXpCyQI;vxG`LPY2zj<7%m zmv7&9FwtjZWQ4Yy7#T$c`H8Xa{pT--FI~o33=!~*m>|EEjt1OHvVpUyK~A`zhJq{) z7pJkNs*Qop;qzAr>e_bn6gLM)thcieKd%4}cci;RdayT<5%B8$$75G+{Qv(SO`KWB z+#0`5K5lLfHnwMP-U{;ay#M@#JP$v3_JWm##ly? zX)4JFI#}5n>Av~+X~E{*KZ!0@K7IT4^Vcsa5#jfrzX`5A^Lc=PciIUXJ!V8nTt daik5?0s#7chG*wQ85sZo002ovPDHLkV1iDAIM4t9 literal 0 HcmV?d00001 diff --git a/.config/i3/bar/screenshots/xrandr.png b/.config/i3/bar/screenshots/xrandr.png new file mode 100644 index 0000000000000000000000000000000000000000..ec1c6de10d2188415b099a5fa5d80d0dd35e93cf GIT binary patch literal 2693 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 literal 0 HcmV?d00001 diff --git a/.config/i3/bar/testjson.sh b/.config/i3/bar/testjson.sh new file mode 100755 index 00000000..d4df9f90 --- /dev/null +++ b/.config/i3/bar/testjson.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +find themes/ -name "*.json"|grep -v invalid|xargs cat|json_verify -s diff --git a/.config/i3/bar/tests/__init__.py b/.config/i3/bar/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.config/i3/bar/tests/mocks.py b/.config/i3/bar/tests/mocks.py new file mode 100644 index 00000000..f5be753b --- /dev/null +++ b/.config/i3/bar/tests/mocks.py @@ -0,0 +1,141 @@ +# pylint: disable=C0103,C0111 + +import mock +import json +import shlex +import random +import subprocess + +from bumblebee.input import I3BarInput +from bumblebee.output import Widget +from bumblebee.config import Config + +def rand(cnt): + return "".join(random.choice("abcdefghijklmnopqrstuvwxyz0123456789") for i in range(cnt)) + +def setup_test(test, Module): + test._stdin, test._select, test.stdin, test.select = epoll_mock("bumblebee.input") + + test.popen = MockPopen() + + test.config = Config() + test.input = I3BarInput() + test.engine = mock.Mock() + test.engine.input = test.input + test.input.need_event = True + test.module = Module(engine=test.engine, config={ "config": test.config }) + for widget in test.module.widgets(): + widget.link_module(test.module) + test.anyWidget = widget + +def teardown_test(test): + test._stdin.stop() + test._select.stop() + test.popen.cleanup() + +def epoll_mock(module=""): + if len(module) > 0: module = "{}.".format(module) + + stdin = mock.patch("{}sys.stdin".format(module)) + select = mock.patch("{}select".format(module)) + epoll = mock.Mock() + + stdin_mock = stdin.start() + select_mock = select.start() + + stdin_mock.fileno.return_value = 1 + select_mock.epoll.return_value = epoll + epoll.poll.return_value = [(stdin_mock.fileno.return_value, 100)] + + return stdin, select, stdin_mock, select_mock + +def mouseEvent(stdin, button, inp, module=None, instance=None): + stdin.readline.return_value = json.dumps({ + "name": module.id if module else rand(10), + "button": button, + "instance": instance + }) + inp.start() + inp.stop() + stdin.readline.assert_any_call() + +class MockPopen(object): + def __init__(self, module=""): + if len(module) > 0: module = "{}.".format(module) + self._patch = mock.patch("{}subprocess.Popen".format(module)) + self._popen = self._patch.start() + self.mock = mock.Mock() + # for a nicer, more uniform interface + self.mock.popen = self._popen + # for easier command execution checks + self.mock.popen.assert_call = self.assert_call + self._popen.return_value = self.mock + + self.mock.communicate.return_value = [ "", None ] + self.mock.returncode = 0 + + def assert_call(self, cmd): + self.mock.popen.assert_any_call(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + + def cleanup(self): + self._patch.stop() + +class MockInput(object): + def __init__(self): + self._callbacks = {} + def start(self): + pass + + def stop(self): + pass + + def get_callback(self, uid): + return self._callbacks.get(uid, None) + + def register_callback(self, obj, button, cmd): + if not obj: + return + self._callbacks[obj.id] = { + "button": button, + "command": cmd, + } + +class MockOutput(object): + def start(self): + pass + + def stop(self): + pass + + def draw(self, widget, engine, module): + engine.stop() + + def begin(self): + pass + + def flush(self): + pass + + def end(self): + pass + +class MockEngine(object): + def __init__(self): + self.input = MockInput() + +class MockWidget(Widget): + def __init__(self, text): + super(MockWidget, self).__init__(text) + self.module = None + self.attr_state = ["state-default"] + self.id = rand(10) + + self.full_text(text) + +# def state(self): +# return self.attr_state + + def update(self, widgets): + pass + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/__init__.py b/.config/i3/bar/tests/modules/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/.config/i3/bar/tests/modules/test_battery.py b/.config/i3/bar/tests/modules/test_battery.py new file mode 100644 index 00000000..0c86cc9f --- /dev/null +++ b/.config/i3/bar/tests/modules/test_battery.py @@ -0,0 +1,108 @@ +# pylint: disable=C0103,C0111 + +import sys +import mock +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import tests.mocks as mocks + +from bumblebee.modules.battery import Module +from bumblebee.config import Config + +class TestBatteryModule(unittest.TestCase): + def setUp(self): + self._stdout = mock.patch("sys.stdout", new_callable=StringIO) + self._exists = mock.patch("bumblebee.modules.battery.os.path.exists") + self._open = mock.patch("bumblebee.modules.battery.open", create=True) + + self.stdout = self._stdout.start() + self.exists = self._exists.start() + self.open = self._open.start() + self.file = mock.Mock() + self.file.__enter__ = lambda x: self.file + self.file.__exit__ = lambda x, a, b, c: "" + self.open.return_value = self.file + + self.exists.return_value = True + self.engine = mock.Mock() + self.config = Config() + self.module = Module(engine=self.engine, config={"config":self.config}) + + self.config.set("battery.critical", "20") + self.config.set("battery.warning", "25") + self.criticalValue = "19" + self.warningValue = "21" + self.normalValue = "26" + self.chargedValue = "96" + + for widget in self.module.widgets(): + widget.link_module(self.module) + self.anyWidget = widget + + def tearDown(self): + self._stdout.stop() + self._exists.stop() + self._open.stop() + + def test_format(self): + for widget in self.module.widgets(): + self.assertEquals(len(widget.full_text()), len("100%")) + + def test_critical(self): + self.file.read.return_value = self.criticalValue + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + + def test_warning(self): + self.file.read.return_value = self.warningValue + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_normal(self): + self.file.read.return_value = self.normalValue + self.module.update_all() + self.assertTrue(not "warning" in self.module.state(self.anyWidget)) + self.assertTrue(not "critical" in self.module.state(self.anyWidget)) + + def test_overload(self): + self.file.read.return_value = "120" + self.module.update_all() + self.assertTrue(not "warning" in self.module.state(self.anyWidget)) + self.assertTrue(not "critical" in self.module.state(self.anyWidget)) + self.assertEquals(self.module.capacity(self.anyWidget), "100%") + + def test_ac(self): + self.exists.return_value = False + self.module.update_all() + self.assertEquals(self.module.capacity(self.anyWidget), "ac") + self.assertTrue("AC" in self.module.state(self.anyWidget)) + + def test_error(self): + self.file.read.side_effect = IOError("failed to read") + self.module.update_all() + self.assertEquals(self.module.capacity(self.anyWidget), "n/a") + self.assertTrue("critical" in self.module.state(self.anyWidget)) + self.assertTrue("unknown" in self.module.state(self.anyWidget)) + + def test_charging(self): + self.file.read.return_value = self.chargedValue + self.module.update_all() + self.assertTrue("charged" in self.module.state(self.anyWidget)) + self.file.read.return_value = self.normalValue + self.module.update_all() + self.assertTrue("charging" in self.module.state(self.anyWidget)) + + def test_discharging(self): + for limit in [ 10, 25, 50, 80, 100 ]: + value = limit - 1 + self.file.read.return_value = str(value) + self.module.update_all() + self.file.read.return_value = "Discharging" + self.assertTrue("discharging-{}".format(limit) in self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_brightness.py b/.config/i3/bar/tests/modules/test_brightness.py new file mode 100644 index 00000000..b636d443 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_brightness.py @@ -0,0 +1,47 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import tests.mocks as mocks + +from bumblebee.input import WHEEL_UP, WHEEL_DOWN +from bumblebee.modules.brightness import Module + +class TestBrightnessModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + + def tearDown(self): + mocks.teardown_test(self) + + def test_format(self): + for widget in self.module.widgets(): + self.assertEquals(len(widget.full_text()), len("100%")) + + def test_wheel_up(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module) + self.popen.assert_call("xbacklight +2%") + + def test_wheel_down(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module) + self.popen.assert_call("xbacklight -2%") + + def test_custom_step(self): + self.config.set("brightness.step", "10") + module = Module(engine=self.engine, config={"config": self.config}) + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=module) + self.popen.assert_call("xbacklight -10%") + + def test_update(self): + self.popen.mock.communicate.return_value = ("20.0", None) + self.module.update_all() + self.assertEquals(self.module.brightness(self.anyWidget), "020%") + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_caffeine.py b/.config/i3/bar/tests/modules/test_caffeine.py new file mode 100644 index 00000000..e95e2ac8 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_caffeine.py @@ -0,0 +1,53 @@ +# pylint: disable=C0103,C0111 + +import json +import unittest +import mock + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import tests.mocks as mocks + +from bumblebee.config import Config +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.caffeine import Module + +class TestCaffeineModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + + self.xset_active = " timeout: 0 cycle: 123" + self.xset_inactive = " timeout: 600 cycle: 123" + + def tearDown(self): + mocks.teardown_test(self) + + def test_text(self): + self.assertEquals(self.module.caffeine(self.anyWidget), "") + + def test_active(self): + self.popen.mock.communicate.return_value = (self.xset_active, None) + self.assertTrue(not "deactivated" in self.module.state(self.anyWidget)) + self.assertTrue("activated" in self.module.state(self.anyWidget)) + + def test_inactive(self): + self.popen.mock.communicate.return_value = (self.xset_inactive, None) + self.assertTrue("deactivated" in self.module.state(self.anyWidget)) + self.popen.mock.communicate.return_value = ("no text", None) + self.assertTrue("deactivated" in self.module.state(self.anyWidget)) + + def test_toggle(self): + self.popen.mock.communicate.return_value = (self.xset_active, None) + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("xset s default") + self.popen.assert_call("notify-send \"Out of coffee\"") + + self.popen.mock.communicate.return_value = (self.xset_inactive, None) + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("xset s off") + self.popen.assert_call("notify-send \"Consuming caffeine\"") + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_cmus.py b/.config/i3/bar/tests/modules/test_cmus.py new file mode 100644 index 00000000..ce1f77c9 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_cmus.py @@ -0,0 +1,114 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.cmus import Module + +class TestCmusModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + + self.songTemplate = """ +status {status} +file /path/to/file +duration {duration} +position {position} +tag title {title} +tag artist {artist} +tag album {album} +tag tracknumber 1 +tag date 1984 +tag comment comment + """ + + def tearDown(self): + mocks.teardown_test(self) + + def test_read_song(self): + self.popen.mock.communicate.return_value = ("song", None) + self.module.update_all() + self.popen.assert_call("cmus-remote -Q") + + def test_handle_runtimeerror(self): + self.popen.mock.communicate.side_effect = RuntimeError("error loading song") + self.module.update_all() + self.assertEquals(self.module.description(self.anyWidget), " - /") + + def test_format(self): + self.popen.mock.communicate.return_value = (self.songTemplate.format( + artist="an artist", title="a title", duration="100", position="20", + album="an album", status="irrelevant" + ), None) + self.module.update_all() + self.anyWidget.set("theme.width", 1000) + self.assertEquals(self.module.description(self.anyWidget), + "an artist - a title 00:20/01:40" + ) + + def test_scrollable_format(self): + self.popen.mock.communicate.return_value = (self.songTemplate.format( + artist="an artist", title="a title", duration="100", position="20", + album="an album", status="irrelevant" + ), None) + self.module.update_all() + self.anyWidget.set("theme.width", 10) + self.assertEquals(self.module.description(self.anyWidget), + "an artist - a title 00:20/01:40"[:10] + ) + + def test_repeat(self): + self.popen.mock.communicate.return_value = ("set repeat false", None) + self.module.update_all() + self.assertTrue("repeat-off" in self.module.state(self.module.widget("cmus.repeat"))) + self.popen.mock.communicate.return_value = ("set repeat true", None) + self.module.update_all() + self.assertTrue("repeat-on" in self.module.state(self.module.widget("cmus.repeat"))) + + def test_shuffle(self): + self.popen.mock.communicate.return_value = ("set shuffle false", None) + self.module.update_all() + self.assertTrue("shuffle-off" in self.module.state(self.module.widget("cmus.shuffle"))) + self.popen.mock.communicate.return_value = ("set shuffle true", None) + self.module.update_all() + self.assertTrue("shuffle-on" in self.module.state(self.module.widget("cmus.shuffle"))) + + def test_prevnext(self): + self.assertTrue("prev" in self.module.state(self.module.widget("cmus.prev"))) + self.assertTrue("next" in self.module.state(self.module.widget("cmus.next"))) + + def test_main(self): + self.popen.mock.communicate.return_value = ("status paused", None) + self.module.update_all() + self.assertTrue("paused" in self.module.state(self.module.widget("cmus.main"))) + + self.popen.mock.communicate.return_value = ("status playing", None) + self.module.update_all() + self.assertTrue("playing" in self.module.state(self.module.widget("cmus.main"))) + + self.popen.mock.communicate.return_value = ("status stopped", None) + self.module.update_all() + self.assertTrue("stopped" in self.module.state(self.module.widget("cmus.main"))) + + def test_widget(self): + self.assertEquals(len(self.module.widgets()), 5) + + for idx, val in enumerate(["prev", "main", "next", "shuffle", "repeat"]): + self.assertEquals(self.module.widgets()[idx].name, "cmus.{}".format(val)) + + def test_interaction(self): + events = [ + {"widget": "cmus.shuffle", "action": "cmus-remote -S"}, + {"widget": "cmus.repeat", "action": "cmus-remote -R"}, + {"widget": "cmus.next", "action": "cmus-remote -n"}, + {"widget": "cmus.prev", "action": "cmus-remote -r"}, + {"widget": "cmus.main", "action": "cmus-remote -u"}, + ] + for event in events: + mocks.mouseEvent(stdin=self.stdin, inp=self.input, module=self.module, instance=self.module.widget(event["widget"]).id, button=LEFT_MOUSE) + self.popen.assert_call(event["action"]) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_cpu.py b/.config/i3/bar/tests/modules/test_cpu.py new file mode 100644 index 00000000..cd5ce899 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_cpu.py @@ -0,0 +1,45 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.cpu import Module + +class TestCPUModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + self._psutil = mock.patch("bumblebee.modules.cpu.psutil") + self.psutil = self._psutil.start() + + def tearDown(self): + self._psutil.stop() + mocks.teardown_test(self) + + def test_format(self): + self.psutil.cpu_percent.return_value = 21.0 + self.module.update_all() + for widget in self.module.widgets(): + self.assertEquals(len(widget.full_text()), len("100.00%")) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("gnome-system-monitor") + + def test_warning(self): + self.config.set("cpu.critical", "20") + self.config.set("cpu.warning", "18") + self.psutil.cpu_percent.return_value = 19.0 + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("cpu.critical", "20") + self.config.set("cpu.warning", "19") + self.psutil.cpu_percent.return_value = 21.0 + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_disk.py b/.config/i3/bar/tests/modules/test_disk.py new file mode 100644 index 00000000..16f31c41 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_disk.py @@ -0,0 +1,47 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.disk import Module + +class MockVFS(object): + def __init__(self, perc): + self.f_blocks = 1024*1024 + self.f_frsize = 1 + self.f_bfree = self.f_blocks*(1.0 - perc/100.0) + +class TestDiskModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + self._os = mock.patch("bumblebee.modules.disk.os") + self.os = self._os.start() + self.config.set("disk.path", "somepath") + + def tearDown(self): + self._os.stop() + mocks.teardown_test(self) + + def test_leftclick(self): + module = Module(engine=self.engine, config={"config":self.config}) + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=module) + self.popen.assert_call("nautilus {}".format(self.module.parameter("path"))) + + def test_warning(self): + self.config.set("disk.critical", "80") + self.config.set("disk.warning", "70") + self.os.statvfs.return_value = MockVFS(75.0) + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("disk.critical", "80") + self.config.set("disk.warning", "70") + self.os.statvfs.return_value = MockVFS(85.0) + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_load.py b/.config/i3/bar/tests/modules/test_load.py new file mode 100644 index 00000000..e57bc2b0 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_load.py @@ -0,0 +1,57 @@ +# pylint: disable=C0103,C0111 + +import json +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.load import Module + +class TestLoadModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + + self._mp = mock.patch("bumblebee.modules.load.multiprocessing") + self._os = mock.patch("bumblebee.modules.load.os") + + self.mp = self._mp.start() + self.os = self._os.start() + + self.mp.cpu_count.return_value = 1 + + def tearDown(self): + self._mp.stop() + self._os.stop() + mocks.teardown_test(self) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("gnome-system-monitor") + + def test_load_format(self): + self.os.getloadavg.return_value = [ 5.9, 1.2, 0.8 ] + self.module.update_all() + self.assertEquals(self.module.load(self.anyWidget), "5.90/1.20/0.80") + + def test_warning(self): + self.config.set("load.critical", "1") + self.config.set("load.warning", "0.8") + self.os.getloadavg.return_value = [ 0.9, 0, 0 ] + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("load.critical", "1") + self.config.set("load.warning", "0.8") + self.os.getloadavg.return_value = [ 1.1, 0, 0 ] + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + + def test_assume_single_core(self): + self.mp.cpu_count.side_effect = NotImplementedError + module = Module(engine=self.engine, config={"config": mock.Mock() }) + self.assertEquals(1, module._cpus) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_memory.py b/.config/i3/bar/tests/modules/test_memory.py new file mode 100644 index 00000000..94c61f99 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_memory.py @@ -0,0 +1,52 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE +from bumblebee.modules.memory import Module + +class VirtualMemory(object): + def __init__(self, percent): + self.percent = percent + +class TestMemoryModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + self._psutil = mock.patch("bumblebee.modules.memory.psutil") + self.psutil = self._psutil.start() + + def tearDown(self): + self._psutil.stop() + mocks.teardown_test(self) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("gnome-system-monitor") + + def test_warning(self): + self.config.set("memory.critical", "80") + self.config.set("memory.warning", "70") + self.psutil.virtual_memory.return_value = VirtualMemory(75) + self.module.update_all() + self.assertTrue("warning" in self.module.state(self.anyWidget)) + + def test_critical(self): + self.config.set("memory.critical", "80") + self.config.set("memory.warning", "70") + self.psutil.virtual_memory.return_value = VirtualMemory(81) + self.module.update_all() + self.assertTrue("critical" in self.module.state(self.anyWidget)) + + def test_usage(self): + rv = VirtualMemory(50) + rv.total = 1000 + rv.available = 500 + self.psutil.virtual_memory.return_value = rv + self.module.update_all() + self.assertEquals("500.00B/1000.00B (50.00%)", self.module.memory_usage(self.anyWidget)) + self.assertEquals(None, self.module.state(self.anyWidget)) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/modules/test_pulseaudio.py b/.config/i3/bar/tests/modules/test_pulseaudio.py new file mode 100644 index 00000000..f8754c68 --- /dev/null +++ b/.config/i3/bar/tests/modules/test_pulseaudio.py @@ -0,0 +1,34 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import LEFT_MOUSE, RIGHT_MOUSE, WHEEL_UP, WHEEL_DOWN +from bumblebee.modules.pulseaudio import Module + +class TestPulseAudioModule(unittest.TestCase): + def setUp(self): + mocks.setup_test(self, Module) + + def tearDown(self): + mocks.teardown_test(self) + + def test_leftclick(self): + mocks.mouseEvent(stdin=self.stdin, button=LEFT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("pactl set-source-mute @DEFAULT_SOURCE@ toggle") + + def test_rightclick(self): + mocks.mouseEvent(stdin=self.stdin, button=RIGHT_MOUSE, inp=self.input, module=self.module) + self.popen.assert_call("pavucontrol") + + def test_wheelup(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_UP, inp=self.input, module=self.module) + self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ +2%") + + def test_wheeldown(self): + mocks.mouseEvent(stdin=self.stdin, button=WHEEL_DOWN, inp=self.input, module=self.module) + self.popen.assert_call("pactl set-source-volume @DEFAULT_SOURCE@ -2%") + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_config.py b/.config/i3/bar/tests/test_config.py new file mode 100644 index 00000000..395dcb71 --- /dev/null +++ b/.config/i3/bar/tests/test_config.py @@ -0,0 +1,77 @@ +# pylint: disable=C0103,C0111 + +import unittest +import mock +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +from bumblebee.config import Config +from bumblebee.theme import themes +from bumblebee.engine import all_modules + +class TestConfig(unittest.TestCase): + def setUp(self): + self._stdout = mock.patch("bumblebee.config.sys.stdout", new_callable=StringIO) + self._stderr = mock.patch("bumblebee.config.sys.stderr", new_callable=StringIO) + + self.stdout = self._stdout.start() + self.stderr = self._stderr.start() + + self.defaultConfig = Config() + self.someSimpleModules = ["foo", "bar", "baz"] + self.someAliasModules = ["foo:a", "bar:b", "baz:c"] + self.someTheme = "some-theme" + + def tearDown(self): + self._stdout.stop() + self._stderr.stop() + + def test_no_modules_by_default(self): + self.assertEquals(self.defaultConfig.modules(), []) + + def test_simple_modules(self): + cfg = Config(["-m"] + self.someSimpleModules) + self.assertEquals(cfg.modules(), [{ + "name": x, "module": x + } for x in self.someSimpleModules]) + + def test_alias_modules(self): + cfg = Config(["-m"] + self.someAliasModules) + self.assertEquals(cfg.modules(), [{ + "module": x.split(":")[0], + "name": x.split(":")[1], + } for x in self.someAliasModules]) + + def test_parameters(self): + cfg = Config(["-m", "module", "-p", "module.key=value"]) + self.assertEquals(cfg.get("module.key"), "value") + + def test_theme(self): + cfg = Config(["-t", self.someTheme]) + self.assertEquals(cfg.theme(), self.someTheme) + + def test_notheme(self): + self.assertEquals(self.defaultConfig.theme(), "default") + + def test_list_themes(self): + with self.assertRaises(SystemExit): + cfg = Config(["-l", "themes"]) + result = self.stdout.getvalue() + for theme in themes(): + self.assertTrue(theme in result) + + def test_list_modules(self): + with self.assertRaises(SystemExit): + cfg = Config(["-l", "modules"]) + result = self.stdout.getvalue() + for module in all_modules(): + self.assertTrue(module["name"] in result) + + def test_invalid_list(self): + with self.assertRaises(SystemExit): + cfg = Config(["-l", "invalid"]) + self.assertTrue("invalid choice" in "".join(self.stderr.getvalue())) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_engine.py b/.config/i3/bar/tests/test_engine.py new file mode 100644 index 00000000..946f6049 --- /dev/null +++ b/.config/i3/bar/tests/test_engine.py @@ -0,0 +1,90 @@ +# pylint: disable=C0103,C0111,W0703,W0212 + +import shlex +import unittest + +from bumblebee.error import ModuleLoadError +from bumblebee.engine import Engine +from bumblebee.config import Config +import bumblebee.input + +from tests.mocks import MockOutput, MockInput + +class TestEngine(unittest.TestCase): + def setUp(self): + self.engine = Engine(config=Config(), output=MockOutput(), inp=MockInput()) + self.testModule = "test" + self.testAlias = "test-alias" + self.singleWidgetModule = [{"module": self.testModule, "name": "a"}] + self.singleWidgetAlias = [{"module": self.testAlias, "name": "a" }] + self.invalidModule = "no-such-module" + self.testModuleSpec = "bumblebee.modules.{}".format(self.testModule) + self.testModules = [ + {"module": "test", "name": "a"}, + {"module": "test", "name": "b"}, + ] + + def test_stop(self): + self.assertTrue(self.engine.running()) + self.engine.stop() + self.assertFalse(self.engine.running()) + + def test_load_module(self): + module = self.engine._load_module(self.testModule) + self.assertEquals(module.__module__, self.testModuleSpec) + + def test_load_invalid_module(self): + with self.assertRaises(ModuleLoadError): + self.engine._load_module(self.invalidModule) + + def test_load_none(self): + with self.assertRaises(ModuleLoadError): + self.engine._load_module(None) + + def test_load_modules(self): + modules = self.engine.load_modules(self.testModules) + self.assertEquals(len(modules), len(self.testModules)) + self.assertEquals( + [module.__module__ for module in modules], + [self.testModuleSpec for module in modules] + ) + + def test_run(self): + self.engine.load_modules(self.singleWidgetModule) + try: + self.engine.run() + except Exception as e: + self.fail(e) + + def test_aliases(self): + modules = self.engine.load_modules(self.singleWidgetAlias) + self.assertEquals(len(modules), 1) + self.assertEquals(modules[0].__module__, self.testModuleSpec) + + def test_custom_cmd(self): + testmodules = [ + { "name": "test", "button": "test.left-click", "action": "echo" }, + { "name": "test:alias", "button": "alias.right-click", "action": "echo2" }, + ] + cmd = "-m" + for test in testmodules: + cmd += " " + test["name"] + cmd += " -p" + for test in testmodules: + cmd += " " + test["button"] + "=" + test["action"] + cfg = Config(shlex.split(cmd)) + inp = MockInput() + engine = Engine(config=cfg, output=MockOutput(), inp=inp) + + i = 0 + for module in engine.modules(): + callback = inp.get_callback(module.id) + self.assertTrue(callback is not None) + self.assertEquals(callback["command"], testmodules[i]["action"]) + if "left" in testmodules[i]["button"]: + self.assertTrue(callback["button"], bumblebee.input.LEFT_MOUSE) + if "right" in testmodules[i]["button"]: + self.assertTrue(callback["button"], bumblebee.input.RIGHT_MOUSE) + i += 1 + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_i3barinput.py b/.config/i3/bar/tests/test_i3barinput.py new file mode 100644 index 00000000..6852f97b --- /dev/null +++ b/.config/i3/bar/tests/test_i3barinput.py @@ -0,0 +1,110 @@ +# pylint: disable=C0103,C0111 + +import json +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.input import I3BarInput, LEFT_MOUSE, RIGHT_MOUSE + +class TestI3BarInput(unittest.TestCase): + def setUp(self): + self.input = I3BarInput() + self.input.need_event = True + + self._stdin = mock.patch("bumblebee.input.sys.stdin") + self.stdin = self._stdin.start() + self._select = mock.patch("bumblebee.input.select") + self.select = self._select.start() + self.popen = mocks.MockPopen() + + self.stdin.fileno.return_value = 1 + epoll = mock.Mock() + self.select.epoll.return_value = epoll + + epoll.poll.return_value = [(self.stdin.fileno.return_value, 2)] + + self.anyModule = mock.Mock() + self.anyModule.id = mocks.rand(10) + self.anotherModule = mock.Mock() + self.anotherModule.id = mocks.rand(10) + self.anyWidget = mocks.MockWidget("some-widget") + self.anotherWidget = mocks.MockWidget("another-widget") + self.anyData = self.invalidData = "any data" + self.invalidEvent = json.dumps({"name": None, "instance": None, "button": 1}) + self.incompleteEvent = json.dumps({"button": 1}) + self.anyCommand = "this is a command with arguments" + + self._called = 0 + + def tearDown(self): + self._stdin.stop() + self._select.stop() + self.popen.cleanup() + + def callback(self, event): + self._called += 1 + + def calls(self): + rv = self._called + self._called = 0 + return rv + + def test_read_event(self): + self.stdin.readline.return_value = self.anyData + self.input.start() + self.input.stop() + self.stdin.readline.assert_any_call() + + def test_ignore_invalid_input(self): + for data in [ self.invalidData, self.incompleteEvent, self.invalidEvent ]: + self.stdin.readline.return_value = data + self.input.start() + self.assertEquals(self.input.alive(), True) + self.assertEquals(self.input.stop(), True) + self.stdin.readline.assert_any_call() + + def test_global_callback(self): + self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin) + self.assertTrue(self.calls() > 0) + + def test_remove_global_callback(self): + self.test_global_callback() + self.input.deregister_callbacks(None) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin) + self.assertTrue(self.calls() == 0) + + def test_global_callback_wrong_button(self): + self.input.register_callback(None, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=RIGHT_MOUSE, inp=self.input, stdin=self.stdin) + self.assertTrue(self.calls() == 0) + + def test_module_callback(self): + self.input.register_callback(self.anyModule, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule) + self.assertTrue(self.calls() > 0) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherModule) + self.assertTrue(self.calls() == 0) + + def test_remove_module_callback(self): + self.test_module_callback() + self.input.deregister_callbacks(self.anyModule) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyModule) + self.assertTrue(self.calls() == 0) + + def test_widget_callback(self): + self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.callback) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget) + self.assertTrue(self.calls() > 0) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anotherWidget) + self.assertTrue(self.calls() == 0) + + def test_widget_cmd_callback(self): + self.input.register_callback(self.anyWidget, button=LEFT_MOUSE, cmd=self.anyCommand) + mocks.mouseEvent(button=LEFT_MOUSE, inp=self.input, stdin=self.stdin, module=self.anyWidget) + self.popen.assert_call(self.anyCommand) + + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_i3baroutput.py b/.config/i3/bar/tests/test_i3baroutput.py new file mode 100644 index 00000000..8c566efb --- /dev/null +++ b/.config/i3/bar/tests/test_i3baroutput.py @@ -0,0 +1,133 @@ +# pylint: disable=C0103,C0111 + +import json +import mock +import unittest + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +import tests.mocks as mocks + +from bumblebee.output import I3BarOutput + +class TestI3BarOutput(unittest.TestCase): + def setUp(self): + self.theme = mock.Mock() + self.theme.separator_fg.return_value = "#123456" + self.theme.separator_bg.return_value = "#000000" + self.theme.separator.return_value = "" + self.theme.prefix.return_value = "" + self.theme.suffix.return_value = "" + self.theme.separator_block_width.return_value = 1 + self.theme.fg.return_value = "#ababab" + self.theme.bg.return_value = "#ababab" + self.theme.align.return_value = None + self.theme.minwidth.return_value = "" + self.output = I3BarOutput(self.theme) + + self._stdout = mock.patch("bumblebee.output.sys.stdout", new_callable=StringIO) + self.stdout = self._stdout.start() + + self.anyWidget = mocks.MockWidget("some text") + self.anyModule = mock.Mock() + self.anyModule.id = mocks.rand(10) + self.anyModule.name = mocks.rand(10) + + self.expectedStart = json.dumps({"version": 1, "click_events": True}) + "\n[\n" + self.expectedStop = "]\n" + + self.anyColor = "#ffffff" + self.anotherColor = "#cdcdcd" + + def tearDown(self): + self._stdout.stop() + + def test_start(self): + self.output.start() + self.assertEquals(self.expectedStart, self.stdout.getvalue()) + + def test_stop(self): + self.output.stop() + self.assertEquals(self.expectedStop, self.stdout.getvalue()) + + def test_draw_single_widget(self): + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], self.anyWidget.full_text()) + + def test_draw_multiple_widgets(self): + for i in range(4): + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue()) + for res in result: + self.assertEquals(res["full_text"], self.anyWidget.full_text()) + + def test_begin(self): + self.output.begin() + self.assertEquals("", self.stdout.getvalue()) + + def test_end(self): + self.output.end() + self.assertEquals(",\n", self.stdout.getvalue()) + + def test_prefix(self): + self.theme.prefix.return_value = " - " + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], " - {}".format(self.anyWidget.full_text())) + + def test_suffix(self): + self.theme.suffix.return_value = " - " + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], "{} - ".format(self.anyWidget.full_text())) + + def test_bothfix(self): + self.theme.prefix.return_value = "*" + self.theme.suffix.return_value = " - " + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["full_text"], "*{} - ".format(self.anyWidget.full_text())) + + def test_colors(self): + self.theme.fg.return_value = self.anyColor + self.theme.bg.return_value = self.anotherColor + self.output.draw(self.anyWidget, self.anyModule) + self.output.flush() + result = json.loads(self.stdout.getvalue())[0] + self.assertEquals(result["color"], self.anyColor) + self.assertEquals(result["background"], self.anotherColor) + + def test_widget_link(self): + self.anyWidget.link_module(self.anyModule) + self.assertEquals(self.anyWidget._module, self.anyModule) + self.assertEquals(self.anyWidget.module, self.anyModule.name) + + def test_unlinked_widget_state(self): + state = self.anyWidget.state() + self.assertTrue(type(state) == list) + + def test_linked_widget_state(self): + self.anyWidget.link_module(self.anyModule) + for lst in [ "samplestate", ["a", "b", "c"], [] ]: + self.anyModule.state.return_value = lst + state = self.anyWidget.state() + self.assertEquals(type(state), list) + if type(lst) is not list: lst = [lst] + self.assertEquals(state, lst) + + def test_widget_fulltext(self): + self.anyWidget.full_text("some text") + self.assertEquals(self.anyWidget.full_text(), "some text") + self.anyWidget.full_text(lambda x: "callable fulltext") + self.assertEquals(self.anyWidget.full_text(), "callable fulltext") + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_module.py b/.config/i3/bar/tests/test_module.py new file mode 100644 index 00000000..cf663dcb --- /dev/null +++ b/.config/i3/bar/tests/test_module.py @@ -0,0 +1,89 @@ +# pylint: disable=C0103,C0111,W0703 + +import unittest + +from bumblebee.engine import Module +from bumblebee.config import Config +from tests.mocks import MockWidget + +class TestModule(unittest.TestCase): + def setUp(self): + self.widgetName = "foo" + self.widget = MockWidget(self.widgetName) + self.config = Config() + self.anyWidgetName = "random-widget-name" + self.noSuchModule = "this-module-does-not-exist" + self.moduleWithoutWidgets = Module(engine=None, widgets=None) + self.moduleWithOneWidget = Module(engine=None, widgets=self.widget, config={"config": self.config}) + self.moduleWithMultipleWidgets = Module(engine=None, + widgets=[self.widget, self.widget, self.widget] + ) + + self.anyConfigName = "cfg" + self.anotherConfigName = "cfg2" + self.anyModule = Module(engine=None, widgets=self.widget, config={ + "name": self.anyConfigName, "config": self.config + }) + self.anotherModule = Module(engine=None, widgets=self.widget, config={ + "name": self.anotherConfigName, "config": self.config + }) + self.anyKey = "some-parameter" + self.anyValue = "value" + self.anotherValue = "another-value" + self.emptyKey = "i-do-not-exist" + self.config.set("{}.{}".format(self.anyConfigName, self.anyKey), self.anyValue) + self.config.set("{}.{}".format(self.anotherConfigName, self.anyKey), self.anotherValue) + + def test_empty_widgets(self): + self.assertEquals(self.moduleWithoutWidgets.widgets(), []) + + def test_single_widget(self): + self.assertEquals(self.moduleWithOneWidget.widgets(), [self.widget]) + + def test_multiple_widgets(self): + for widget in self.moduleWithMultipleWidgets.widgets(): + self.assertEquals(widget, self.widget) + + def test_retrieve_widget_by_name(self): + widget = MockWidget(self.anyWidgetName) + widget.name = self.anyWidgetName + module = Module(engine=None, widgets=[self.widget, widget, self.widget]) + retrievedWidget = module.widget(self.anyWidgetName) + self.assertEquals(retrievedWidget, widget) + + def test_retrieve_widget_by_id(self): + widget = MockWidget(self.anyWidgetName) + widget.id = self.anyWidgetName + module = Module(engine=None, widgets=[self.widget, widget, self.widget]) + retrievedWidget = module.widget_by_id(self.anyWidgetName) + self.assertEquals(retrievedWidget, widget) + + def test_retrieve_missing_widget(self): + module = self.moduleWithMultipleWidgets + + widget = module.widget(self.noSuchModule) + self.assertEquals(widget, None) + + widget = module.widget_by_id(self.noSuchModule) + self.assertEquals(widget, None) + + def test_threshold(self): + module = self.moduleWithOneWidget + module.name = self.widgetName + + self.config.set("{}.critical".format(self.widgetName), 10.0) + self.config.set("{}.warning".format(self.widgetName), 8.0) + self.assertEquals("critical", module.threshold_state(10.1, 0, 0)) + self.assertEquals("warning", module.threshold_state(10.0, 0, 0)) + self.assertEquals(None, module.threshold_state(8.0, 0, 0)) + self.assertEquals(None, module.threshold_state(7.9, 0, 0)) + + def test_parameters(self): + self.assertEquals(self.anyModule.parameter(self.anyKey), self.anyValue) + self.assertEquals(self.anotherModule.parameter(self.anyKey), self.anotherValue) + + def test_default_parameters(self): + self.assertEquals(self.anyModule.parameter(self.emptyKey), None) + self.assertEquals(self.anyModule.parameter(self.emptyKey, self.anyValue), self.anyValue) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_modules.py b/.config/i3/bar/tests/test_modules.py new file mode 100644 index 00000000..4b280a32 --- /dev/null +++ b/.config/i3/bar/tests/test_modules.py @@ -0,0 +1,58 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest +import importlib + +import tests.mocks as mocks + +from bumblebee.engine import all_modules +from bumblebee.output import Widget +from bumblebee.config import Config + +class TestGenericModules(unittest.TestCase): + def setUp(self): + engine = mock.Mock() + engine.input = mock.Mock() + config = Config() + self.objects = {} + + self.popen = mocks.MockPopen() + self.popen.mock.communicate.return_value = (str.encode("1"), "error") + self.popen.mock.returncode = 0 + + self._platform = mock.patch("bumblebee.modules.kernel.platform") + self.platform = self._platform.start() + self.platform.release.return_value = "unknown linux v1" + + for mod in all_modules(): + name = "bumblebee.modules.{}".format(mod["name"]) + cls = importlib.import_module(name) + self.objects[mod["name"]] = getattr(cls, "Module")(engine, {"config": config}) + for widget in self.objects[mod["name"]].widgets(): + self.assertEquals(widget.get("variable", None), None) + + def tearDown(self): + self._platform.stop() + self.popen.cleanup() + + def test_widgets(self): + for mod in self.objects: + widgets = self.objects[mod].widgets() + for widget in widgets: + widget.link_module(self.objects[mod]) + self.assertEquals(widget.module, mod) + self.assertTrue(isinstance(widget, Widget)) + self.assertTrue(hasattr(widget, "full_text")) + widget.set("variable", "value") + self.assertEquals(widget.get("variable", None), "value") + self.assertTrue(isinstance(widget.full_text(), str) or isinstance(widget.full_text(), unicode)) + + def test_update(self): + for mod in self.objects: + widgets = self.objects[mod].widgets() + self.objects[mod].update(widgets) + self.test_widgets() + self.assertEquals(widgets, self.objects[mod].widgets()) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_store.py b/.config/i3/bar/tests/test_store.py new file mode 100644 index 00000000..faa1efb7 --- /dev/null +++ b/.config/i3/bar/tests/test_store.py @@ -0,0 +1,26 @@ +# pylint: disable=C0103,C0111,W0703 + +import unittest + +from bumblebee.store import Store + +class TestStore(unittest.TestCase): + def setUp(self): + self.store = Store() + self.anyKey = "some-key" + self.anyValue = "some-value" + self.unsetKey = "invalid-key" + + def test_set_value(self): + self.store.set(self.anyKey, self.anyValue) + self.assertEquals(self.store.get(self.anyKey), self.anyValue) + + def test_get_invalid_value(self): + result = self.store.get(self.unsetKey) + self.assertEquals(result, None) + + def test_get_invalid_with_default_value(self): + result = self.store.get(self.unsetKey, self.anyValue) + self.assertEquals(result, self.anyValue) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_theme.py b/.config/i3/bar/tests/test_theme.py new file mode 100644 index 00000000..747d7498 --- /dev/null +++ b/.config/i3/bar/tests/test_theme.py @@ -0,0 +1,134 @@ +# pylint: disable=C0103,C0111,W0703 + +import mock +import unittest +from bumblebee.theme import Theme +from bumblebee.error import ThemeLoadError +from tests.mocks import MockWidget + +class TestTheme(unittest.TestCase): + def setUp(self): + self.nonexistentThemeName = "no-such-theme" + self.invalidThemeName = "test_invalid" + self.validThemeName = "test" + self.validThemeSeparator = " * " + self.themedWidget = MockWidget("bla") + self.theme = Theme(self.validThemeName) + self.cycleTheme = Theme("test_cycle") + self.anyModule = mock.Mock() + self.anyWidget = MockWidget("bla") + self.anotherWidget = MockWidget("blub") + + self.anyModule.state.return_value = "state-default" + + self.anyWidget.link_module(self.anyModule) + self.themedWidget.link_module(self.anyModule) + + data = self.theme.data() + self.widgetTheme = "test-widget" + self.themedWidget.module = self.widgetTheme + self.defaultColor = data["defaults"]["fg"] + self.defaultBgColor = data["defaults"]["bg"] + self.widgetColor = data[self.widgetTheme]["fg"] + self.widgetBgColor = data[self.widgetTheme]["bg"] + self.defaultPrefix = data["defaults"]["prefix"] + self.defaultSuffix = data["defaults"]["suffix"] + self.widgetPrefix = data[self.widgetTheme]["prefix"] + self.widgetSuffix = data[self.widgetTheme]["suffix"] + + def test_load_valid_theme(self): + try: + Theme(self.validThemeName) + except Exception as e: + self.fail(e) + + def test_load_nonexistent_theme(self): + with self.assertRaises(ThemeLoadError): + Theme(self.nonexistentThemeName) + + def test_load_invalid_theme(self): + with self.assertRaises(ThemeLoadError): + Theme(self.invalidThemeName) + + def test_default_prefix(self): + self.assertEquals(self.theme.prefix(self.anyWidget), self.defaultPrefix) + + def test_default_suffix(self): + self.assertEquals(self.theme.suffix(self.anyWidget), self.defaultSuffix) + + def test_widget_prefix(self): + self.assertEquals(self.theme.prefix(self.themedWidget), self.widgetPrefix) + + def test_widget_fg(self): + self.assertEquals(self.theme.fg(self.anyWidget), self.defaultColor) + self.anyWidget.module = self.widgetTheme + self.assertEquals(self.theme.fg(self.anyWidget), self.widgetColor) + + def test_widget_bg(self): + self.assertEquals(self.theme.bg(self.anyWidget), self.defaultBgColor) + self.anyWidget.module = self.widgetTheme + self.assertEquals(self.theme.bg(self.anyWidget), self.widgetBgColor) + + def test_absent_cycle(self): + theme = self.theme + try: + theme.fg(self.anyWidget) + theme.fg(self.anotherWidget) + except Exception as e: + self.fail(e) + + def test_reset(self): + theme = self.cycleTheme + data = theme.data() + theme.reset() + self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"]) + self.assertEquals(theme.fg(self.anotherWidget), data["cycle"][1]["fg"]) + theme.reset() + self.assertEquals(theme.fg(self.anyWidget), data["cycle"][0]["fg"]) + + def test_separator_block_width(self): + theme = self.theme + data = theme.data() + + self.assertEquals(theme.separator_block_width(self.anyWidget), + data["defaults"]["separator-block-width"] + ) + + def test_separator(self): + for theme in [self.theme, self.cycleTheme]: + theme.reset() + prev_bg = theme.bg(self.anyWidget) + theme.bg(self.anotherWidget) + + self.assertEquals(theme.separator_fg(self.anotherWidget), theme.bg(self.anotherWidget)) + self.assertEquals(theme.separator_bg(self.anotherWidget), prev_bg) + + def test_state(self): + theme = self.theme + data = theme.data() + + self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["fg"]) + self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["bg"]) + + self.anyModule.state.return_value = "critical" + self.assertEquals(theme.fg(self.anyWidget), data["defaults"]["critical"]["fg"]) + self.assertEquals(theme.bg(self.anyWidget), data["defaults"]["critical"]["bg"]) + self.assertEquals(theme.fg(self.themedWidget), data[self.widgetTheme]["critical"]["fg"]) + # if elements are missing in the state theme, they are taken from the + # widget theme instead (i.e. no fallback to a more general state theme) + self.assertEquals(theme.bg(self.themedWidget), data[self.widgetTheme]["bg"]) + + def test_separator(self): + self.assertEquals(self.validThemeSeparator, self.theme.separator(self.anyWidget)) + + def test_list(self): + theme = self.theme + data = theme.data()[self.widgetTheme]["cycle-test"]["fg"] + self.anyModule.state.return_value = "cycle-test" + self.assertTrue(len(data) > 1) + + for idx in range(0, len(data)): + self.assertEquals(theme.fg(self.themedWidget), data[idx]) + self.assertEquals(theme.fg(self.themedWidget), data[0]) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/tests/test_util.py b/.config/i3/bar/tests/test_util.py new file mode 100644 index 00000000..19887568 --- /dev/null +++ b/.config/i3/bar/tests/test_util.py @@ -0,0 +1,56 @@ +# pylint: disable=C0103,C0111 + +import mock +import unittest + +import tests.mocks as mocks + +from bumblebee.util import * + +class TestUtil(unittest.TestCase): + def setUp(self): + self.popen = mocks.MockPopen("bumblebee.util") + self.some_command_with_args = "sample-command -a -b -c" + self.some_utf8 = "some string".encode("utf-8") + + def tearDown(self): + self.popen.cleanup() + + def test_bytefmt(self): + self.assertEquals(bytefmt(10), "10.00B") + self.assertEquals(bytefmt(15*1024), "15.00KiB") + self.assertEquals(bytefmt(20*1024*1024), "20.00MiB") + self.assertEquals(bytefmt(22*1024*1024*1024), "22.00GiB") + self.assertEquals(bytefmt(35*1024*1024*1024*1024), "35840.00GiB") + + def test_durationfmt(self): + self.assertEquals(durationfmt(00), "00:00") + self.assertEquals(durationfmt(25), "00:25") + self.assertEquals(durationfmt(60), "01:00") + self.assertEquals(durationfmt(119), "01:59") + self.assertEquals(durationfmt(3600), "01:00:00") + self.assertEquals(durationfmt(7265), "02:01:05") + + def test_execute(self): + execute(self.some_command_with_args) + self.assertTrue(self.popen.mock.popen.called) + self.popen.mock.popen.assert_call(self.some_command_with_args) + self.assertTrue(self.popen.mock.communicate.called) + + def test_execute_nowait(self): + execute(self.some_command_with_args, False) + self.assertTrue(self.popen.mock.popen.called) + self.popen.mock.popen.assert_call(self.some_command_with_args) + self.assertFalse(self.popen.mock.communicate.called) + + def test_execute_utf8(self): + self.popen.mock.communicate.return_value = [ self.some_utf8, None ] + self.test_execute() + + def test_execute_error(self): + self.popen.mock.returncode = 1 + + with self.assertRaises(RuntimeError): + execute(self.some_command_with_args) + +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 diff --git a/.config/i3/bar/themes/default.json b/.config/i3/bar/themes/default.json new file mode 100644 index 00000000..ddda5fe9 --- /dev/null +++ b/.config/i3/bar/themes/default.json @@ -0,0 +1,7 @@ +{ + "icons": [ "ascii" ], + "defaults": { + "urgent": true, + "fg": "#aabbcc" + } +} diff --git a/.config/i3/bar/themes/gruvbox-powerline.json b/.config/i3/bar/themes/gruvbox-powerline.json new file mode 100644 index 00000000..5cbfdc0f --- /dev/null +++ b/.config/i3/bar/themes/gruvbox-powerline.json @@ -0,0 +1,41 @@ +{ + "icons": [ "paxy97", "awesome-fonts" ], + "defaults": { + "warning": { + "fg": "#1d2021", + "bg": "#d79921" + }, + "critical": { + "fg": "#fbf1c7", + "bg": "#cc241d" + }, + "default-separators": false, + "separator-block-width": 0 + }, + "cycle": [ + { + "fg": "#ebdbb2", + "bg": "#1d2021" + }, + { + "fg": "#fbf1c7", + "bg": "#282828" + } + ], + "dnf": { + "good": { + "fg": "#002b36", + "bg": "#859900" + } + }, + "battery": { + "charged": { + "fg": "#1d2021", + "bg": "#b8bb26" + }, + "AC": { + "fg": "#1d2021", + "bg": "#b8bb26" + } + } +} diff --git a/.config/i3/bar/themes/gruvbox.json b/.config/i3/bar/themes/gruvbox.json new file mode 100644 index 00000000..eee93c1d --- /dev/null +++ b/.config/i3/bar/themes/gruvbox.json @@ -0,0 +1,41 @@ +{ + "icons": [ "paxy97", "ascii" ], + "defaults": { + "warning": { + "fg": "#1d2021", + "bg": "#d79921" + }, + "critical": { + "fg": "#fbf1c7", + "bg": "#cc241d" + }, + "default-separators": false, + "separator-block-width": 0 + }, + "cycle": [ + { + "fg": "#ebdbb2", + "bg": "#1d2021" + }, + { + "fg": "#fbf1c7", + "bg": "#282828" + } + ], + "dnf": { + "good": { + "fg": "#002b36", + "bg": "#859900" + } + }, + "battery": { + "charged": { + "fg": "#1d2021", + "bg": "#b8bb26" + }, + "AC": { + "fg": "#1d2021", + "bg": "#b8bb26" + } + } +} diff --git a/.config/i3/bar/themes/icons/ascii.json b/.config/i3/bar/themes/icons/ascii.json new file mode 100644 index 00000000..9b03a462 --- /dev/null +++ b/.config/i3/bar/themes/icons/ascii.json @@ -0,0 +1,70 @@ +{ + "defaults": { + "padding": " " + }, + "memory": { "prefix": "ram" }, + "cpu": { "prefix": "cpu" }, + "disk": { "prefix": "hdd" }, + "dnf": { "prefix": "dnf" }, + "brightness": { "prefix": "o" }, + "cmus": { + "playing": { "prefix": ">" }, + "paused": { "prefix": "||" }, + "stopped": { "prefix": "[]" }, + "prev": { "prefix": "|<" }, + "next": { "prefix": ">|" }, + "shuffle-on": { "prefix": "S" }, + "shuffle-off": { "prefix": "[s]" }, + "repeat-on": { "prefix": "R" }, + "repeat-off": { "prefix": "[r]" } + }, + "pasink": { + "muted": { "prefix": "audio(mute)" }, + "unmuted": { "prefix": "audio" } + }, + "amixer": { + "muted": { "prefix": "audio(mute)" }, + "unmuted": { "prefix": "audio" } + }, + "pasource": { + "muted": { "prefix": "mic(mute)" }, + "unmuted": { "prefix": "mic" } + }, + "nic": { + "wireless-up": { "prefix": "wifi" }, + "wireless-down": { "prefix": "wifi" }, + "wired-up": { "prefix": "lan" }, + "wired-down": { "prefix": "lan" }, + "tunnel-up": { "prefix": "tun" }, + "tunnel-down": { "prefix": "tun" } + }, + "battery": { + "charged": { "suffix": "full" }, + "charging": { "suffix": "chr" }, + "AC": { "suffix": "ac" }, + "discharging-10": { + "prefix": "!", + "suffix": "dis" + }, + "discharging-25": { "suffix": "dis" }, + "discharging-50": { "suffix": "dis" }, + "discharging-80": { "suffix": "dis" }, + "discharging-100": { "suffix": "dis" } + }, + "caffeine": { + "activated": {"prefix": "caf-on" }, "deactivated": { "prefix": "caf-off " } + }, + "xrandr": { + "on": { "prefix": " off "}, "off": { "prefix": " on "} + }, + "redshift": { + "day": { "prefix": "day" }, "night": { "prefix": "night" }, "transition": { "prefix": "trans" } + }, + "sensors": { + "prefix": "sensors" + }, + "traffic": { + "rx": { "prefix": "down"}, + "tx": { "prefix": "up"} + } +} diff --git a/.config/i3/bar/themes/icons/awesome-fonts.json b/.config/i3/bar/themes/icons/awesome-fonts.json new file mode 100644 index 00000000..4263a19f --- /dev/null +++ b/.config/i3/bar/themes/icons/awesome-fonts.json @@ -0,0 +1,86 @@ +{ + "defaults": { + "separator": "", "padding": " ", + "unknown": { "prefix": "" } + }, + "date": { "prefix": "" }, + "time": { "prefix": "" }, + "memory": { "prefix": "" }, + "cpu": { "prefix": "" }, + "disk": { "prefix": "" }, + "dnf": { "prefix": "" }, + "pacman": { "prefix": "" }, + "brightness": { "prefix": "" }, + "load": { "prefix": "" }, + "layout": { "prefix": "" }, + "cmus": { + "playing": { "prefix": "" }, + "paused": { "prefix": "" }, + "stopped": { "prefix": "" }, + "prev": { "prefix": "" }, + "next": { "prefix": "" }, + "shuffle-on": { "prefix": "" }, + "shuffle-off": { "prefix": "" }, + "repeat-on": { "prefix": "" }, + "repeat-off": { "prefix": "" } + }, + "gpmdp": { + "playing": { "prefix": "" }, + "paused": { "prefix": "" }, + "stopped": { "prefix": "" }, + "prev": { "prefix": "" }, + "next": { "prefix": "" } + }, + "pasink": { + "muted": { "prefix": "" }, + "unmuted": { "prefix": "" } + }, + "amixer": { + "muted": { "prefix": "" }, + "unmuted": { "prefix": "" } + }, + "pasource": { + "muted": { "prefix": "" }, + "unmuted": { "prefix": "" } + }, + "kernel": { + "prefix": "\uf17c" + }, + "nic": { + "wireless-up": { "prefix": "" }, + "wireless-down": { "prefix": "" }, + "wired-up": { "prefix": "" }, + "wired-down": { "prefix": "" }, + "tunnel-up": { "prefix": "" }, + "tunnel-down": { "prefix": "" } + }, + "battery": { + "charged": { "prefix": "", "suffix": "" }, + "AC": { "suffix": "" }, + "charging": { + "prefix": [ "", "", "", "", "" ], + "suffix": "" + }, + "discharging-10": { "prefix": "", "suffix": "" }, + "discharging-25": { "prefix": "", "suffix": "" }, + "discharging-50": { "prefix": "", "suffix": "" }, + "discharging-80": { "prefix": "", "suffix": "" }, + "discharging-100": { "prefix": "", "suffix": "" } + }, + "caffeine": { + "activated": {"prefix": " " }, "deactivated": { "prefix": " " } + }, + "xrandr": { + "on": { "prefix": " "}, "off": { "prefix": " " } + }, + "redshift": { + "day": { "prefix": "" }, "night": { "prefix": "" }, "transition": { "prefix": "" } + }, + "sensors": { + "prefix": "🌡" + }, + "traffic":{ + "rx": { "prefix": "" }, + "tx": { "prefix": "" } + } +} diff --git a/.config/i3/bar/themes/icons/paxy97.json b/.config/i3/bar/themes/icons/paxy97.json new file mode 100644 index 00000000..9a889ab7 --- /dev/null +++ b/.config/i3/bar/themes/icons/paxy97.json @@ -0,0 +1,5 @@ +{ + "memory": { + "prefix": "  " + } +} diff --git a/.config/i3/bar/themes/icons/test.json b/.config/i3/bar/themes/icons/test.json new file mode 100644 index 00000000..ad17178b --- /dev/null +++ b/.config/i3/bar/themes/icons/test.json @@ -0,0 +1,6 @@ +{ + "test-widget": { + "prefix": "widget-prefix", + "suffix": "widget-suffix" + } +} diff --git a/.config/i3/bar/themes/kulade.json b/.config/i3/bar/themes/kulade.json new file mode 100644 index 00000000..40f64e91 --- /dev/null +++ b/.config/i3/bar/themes/kulade.json @@ -0,0 +1,46 @@ +{ + "icons": [ "awesome-fonts" ], + "defaults": { + "separator-block-width": 0, + "warning": { + "fg": "#8C001A", + "bg": "#ffffff" + }, + "critical": { + "fg": "#ffffff", + "bg": "#8C001A" + }, + "good": { + "fg": "#000000", + "bg": "#008800" + }, + "great": { + "fg": "#000000", + "bg": "#008080" + }, + "mid": { + "fg": "#000000", + "bg": "#808000" + } + }, + "cycle": [ + { "fg": "#93a1a1", "bg": "#000000" }, + { "fg": "#eee8d5", "bg": "#333333" } + ], + "dnf": { + "good": { + "fg": "#002b36", + "bg": "#859900" + } + }, + "battery": { + "charged": { + "fg": "#000000", + "bg": "#008080" + }, + "AC": { + "fg": "#002b36", + "bg": "#859900" + } + } +} diff --git a/.config/i3/bar/themes/powerline.json b/.config/i3/bar/themes/powerline.json new file mode 100644 index 00000000..80be8cf1 --- /dev/null +++ b/.config/i3/bar/themes/powerline.json @@ -0,0 +1,41 @@ +{ + "icons": [ "awesome-fonts" ], + "defaults": { + "separator-block-width": 0, + "critical": { + "fg": "#ffffff", + "bg": "#ff0000" + }, + "warning": { + "fg": "#d75f00", + "bg": "#ffd700" + }, + "default_separators": false + }, + "cycle": [ + { + "fg": "#ffd700", + "bg": "#d75f00" + }, + { + "fg": "#ffffff", + "bg": "#0087af" + } + ], + "dnf": { + "good": { + "fg": "#494949", + "bg": "#41db00" + } + }, + "battery": { + "charged": { + "fg": "#494949", + "bg": "#41db00" + }, + "AC": { + "fg": "#494949", + "bg": "#41db00" + } + } +} diff --git a/.config/i3/bar/themes/solarized-powerline.json b/.config/i3/bar/themes/solarized-powerline.json new file mode 100644 index 00000000..e1fbe1d0 --- /dev/null +++ b/.config/i3/bar/themes/solarized-powerline.json @@ -0,0 +1,40 @@ +{ + "icons": [ "awesome-fonts" ], + "defaults": { + "separator-block-width": 0, + "warning": { + "fg": "#002b36", + "bg": "#b58900" + }, + "critical": { + "fg": "#002b36", + "bg": "#dc322f" + } + }, + "cycle": [ + { "fg": "#93a1a1", "bg": "#002b36" }, + { "fg": "#eee8d5", "bg": "#586e75" } + ], + "dnf": { + "good": { + "fg": "#002b36", + "bg": "#859900" + } + }, + "pacman": { + "good": { + "fg": "#002b36", + "bg": "#859900" + } + }, + "battery": { + "charged": { + "fg": "#002b36", + "bg": "#859900" + }, + "AC": { + "fg": "#002b36", + "bg": "#859900" + } + } +} diff --git a/.config/i3/bar/themes/solarized.json b/.config/i3/bar/themes/solarized.json new file mode 100644 index 00000000..12a71ae7 --- /dev/null +++ b/.config/i3/bar/themes/solarized.json @@ -0,0 +1,42 @@ +{ + "icons": [ "ascii" ], + "defaults": { + "separator-block-width": 0, + "critical": { + "fg": "#002b36", + "bg": "#dc322f" + }, + "warning": { + "fg": "#002b36", + "bg": "#b58900" + }, + "default_separators": false, + "separator": "" + }, + "cycle": [ + { + "fg": "#93a1a1", + "bg": "#002b36" + }, + { + "fg": "#eee8d5", + "bg": "#586e75" + } + ], + "dnf": { + "good": { + "fg": "#002b36", + "bg": "#859900" + } + }, + "battery": { + "charged": { + "fg": "#002b36", + "bg": "#859900" + }, + "AC": { + "fg": "#002b36", + "bg": "#859900" + } + } +} diff --git a/.config/i3/bar/themes/test.json b/.config/i3/bar/themes/test.json new file mode 100644 index 00000000..1d457a7e --- /dev/null +++ b/.config/i3/bar/themes/test.json @@ -0,0 +1,25 @@ +{ + "icons": [ "test" ], + "defaults": { + "prefix": "default-prefix", + "suffix": "default-suffix", + "fg": "#000000", + "bg": "#111111", + "separator": " * ", + "separator-block-width": 10, + "critical": { + "fg": "#ffffff", + "bg": "#010101" + } + }, + "test-widget": { + "fg": "#ababab", + "bg": "#222222", + "critical": { + "fg": "#bababa" + }, + "cycle-test": { + "fg": [ "#000000", "#111111" ] + } + } +} diff --git a/.config/i3/bar/themes/test_cycle.json b/.config/i3/bar/themes/test_cycle.json new file mode 100644 index 00000000..5fd7e1ac --- /dev/null +++ b/.config/i3/bar/themes/test_cycle.json @@ -0,0 +1,18 @@ +{ + "icons": [ "test" ], + "defaults": { + "prefix": "default-prefix", + "suffix": "default-suffix", + "fg": "#000000", + "bg": "#111111" + }, + "cycle": [ + { "fg": "#aa0000" }, + { "fg": "#00aa00" }, + { "fg": "#0000aa" } + ], + "test-widget": { + "fg": "#ababab", + "bg": "#222222" + } +} diff --git a/.config/i3/bar/themes/test_invalid.json b/.config/i3/bar/themes/test_invalid.json new file mode 100644 index 00000000..d510f27a --- /dev/null +++ b/.config/i3/bar/themes/test_invalid.json @@ -0,0 +1 @@ +this is really not json diff --git a/.config/i3/config b/.config/i3/config new file mode 100755 index 00000000..1ae87b17 --- /dev/null +++ b/.config/i3/config @@ -0,0 +1,393 @@ +# vim: filetype=i3 + +#exec --no-startup-id xrdb ~/.Xresources; +#Basic settings +set $mod Mod4 +floating_modifier $mod +set $term urxvt +set $screencast --no-startup-id bash ~/.config/Scripts/screencast.sh +set $screencast_sys --no-startup-id bash ~/.config/Scripts/screencast_sys.sh +set $video --no-startup-id bash ~/.config/Scripts/video.sh +set $audio --no-startup-id bash ~/.config/Scripts/audio.sh +set $stoprec --no-startup-id killall ffmpeg +font pango:hack 9 + +#The wifi manager applet: +exec --no-startup-id nm-applet +#For transparency and shadows: +#exec --no-startup-id xcompmgr -slt +exec --no-startup-id compton +#This runs the script that renews the config shortcuts: +exec_always --no-startup-id python ~/.config/Scripts/shortcuts.py + +exec --no-startup-id sudo mount -a + +#My background is always in ~/.config/wall.png. I have a shortcut in my file manager (ranger) that will automatically move a selected file here, so I can change by background on the go. +exec_always --no-startup-id feh --bg-scale ~/.config/wall.png + +#Remaps the caps lock button to escape +exec --no-startup-id setxkbmap -option caps:escape +#Remaps the menu button (usually between the right Alt and Ctrl to super +exec_always --no-startup-id xmodmap $HOME/.config/Scripts/remaps +#Makes the mouse invisible after a brief period +exec --no-startup-id unclutter +#Disables touchpad; you might like like this one! (I only use the ThinkPad trackpoint). +exec --no-startup-id exec synclient TouchpadOff=1 + +#Needed for i3-gaps +hide_edge_borders both +for_window [class="^.*"] border pixel 0 +gaps inner 20 +gaps outer 20 + + +#MUSIC: +##For moc: +set $music mocp --config ~/.config/moc/config +set $pause --no-startup-id mocp -G +set $next --no-startup-id mocp -f +set $prev --no-startup-id mocp -r +set $lilfor --no-startup-id mocp -k 10 +set $bigfor --no-startup-id mocp -k 120 +set $lilbak --no-startup-id mocp -k -10 +set $bigbak --no-startup-id mocp -k -120 +set $beg --no-startup-id mocp -j 0% + +##For cmus: +#set $music cmus +#set $pause --no-startup-id cmus-remote -u +#set $next --no-startup-id cmus-remote -n +#set $prev --no-startup-id cmus-remote -r +#set $lilfor --no-startup-id cmus-remote -k +10 +#set $bigfor --no-startup-id cmus-remote -k +120 +#set $lilbak --no-startup-id cmus-remote -k -10 +#set $bigbak --no-startup-id cmus-remote -k -120 +#set $beg --no-startup-id cmus-remote -k 00:00:00 + +#For PULSEAUDIO/PAMIXER +#set $inc --no-startup-id pamixer --allow-boost -i 5 +#set $biginc --no-startup-id pamixer --allow-boost -i 15 +#set $dec --no-startup-id pamixer --allow-boost -d 5 +#set $bigdec --no-startup-id pamixer --allow-boost -d 15 +#set $mute --no-startup-id pamixer --allow-boost -t +#set $micmute --no-startup-id pamixer --allow-boost -t +#set $truemute --no-startup-id pamixer -m + +#FOR ALSA/AMIXER +set $inc --no-startup-id amixer sset Master 2%+ +set $biginc --no-startup-id amixer sset Master 5%+ +set $dec --no-startup-id amixer sset Master 2%- +set $bigdec --no-startup-id amixer sset Master 5%- +set $mute --no-startup-id amixer sset Master toggle +set $truemute --no-startup-id amixer sset Master mute + +bar { +colors { + focused_workspace #008080 #008080 #ffffff + inactive_workspace #000000 #000000 #005050 + active_workspace #000000 #000000 #008080 + urgent_workspace #8B0000 #8B0000 #000000 + } + status_command ~/.config/i3/bar/bumblebee-status -m amixer weather battery nic disk datetime -p disk.path=/home datetime.format="%a, %b %d, %Y at %I:%M %p" -t kulade + #status_command ~/Downloads/bbs/bumblebee-status -m amixer battery nic disk datetime -p disk.path=/home datetime.format="%a, %b %d, %Y at %I:%M %p" -t kulade + position top + } + +set $flash --no-startup-id bash ~/.config/Scripts/flash_win.sh + +bindsym $mod+Return exec $term +bindsym $mod+Shift+Return exec $term -e tmux -f ~/.config/tmux.conf + +bindsym $mod+Shift+space floating toggle +bindsym $mod+space focus mode_toggle + +bindsym $mod+Escape workspace prev +bindsym $mod+Shift+Escape exec i3-msg exit + +bindsym $mod+BackSpace exec $flash +bindsym $mod+Shift+BackSpace exec sudo reboot + +bindsym $mod+Tab workspace back_and_forth + +bindsym $mod+grave exec $flash +#bindsym $mod+asciitilde + +#bindsym $mod+XF86Back +#bindsym $mod+Shift+XF86Back + +#bindsym $mod+XF86Forward +#bindsym $mod+Shift+XF86Forward +#END + +#Letter Keys + +bindsym $mod+q kill +bindsym $mod+Shift+q kill + +bindsym $mod+w exec --no-startup-id $BROWSER +bindsym $mod+Shift+w exec --no-startup-id $BROWSER + +bindsym $mod+e exec $term -e mutt -F ~/.config/mutt/muttrc +bindsym $mod+Shift+e exec $EMAIL + +bindsym $mod+r exec $term -e ranger +bindsym $mod+Shift+r restart; for_window [instance="dropdown"] move position center; for_window [instance="math"] move position center + +bindsym $mod+t split toggle +bindsym $mod+Shift+t gaps inner current set 15; gaps outer current set 15 + +bindsym $mod+y exec $term -e calcurse -D $HOME/.config/calcurse/ +bindsym $mod+Shift+y resize shrink width 10 px or 10ppt + +for_window [instance="dropdown"] floating enable +for_window [instance="dropdown"] resize set 625 400 +for_window [instance="dropdown"] move position center +for_window [instance="dropdown"] move scratchpad + + +exec --no-startup-id $term -name dropdown -e tmux -f ~/.config/Scripts/tmux.conf + +bindsym $mod+u [instance="dropdown"] scratchpad show +bindsym $mod+Shift+u resize shrink height 10 px or 10 ppt + +bindsym $mod+i exec $term -e htop +bindsym $mod+Shift+i resize grow height 10 px or 10 ppt + +bindsym $mod+o exec --no-startup-id $beg +bindsym $mod+Shift+o resize grow width 10 px or 10 ppt + +bindsym $mod+p exec $pause +bindsym $mod+Shift+p exec mypaint + +for_window [instance="math"] floating enable +for_window [instance="math"] resize set 800 300 +for_window [instance="math"] move position center +for_window [instance="math"] move scratchpad + +exec --no-startup-id $term -name math -e R -q + +bindsym $mod+a [instance="math"] scratchpad show +bindsym $mod+Shift+a exec $term -e alsamixer + +bindsym $mod+s gaps inner current plus 5 +bindsym $mod+Shift+s gaps inner current minus 5 + +bindsym $mod+d exec rofi -show run +bindsym $mod+Shift+d gaps inner current set 0; gaps outer current set 0 + +bindsym $mod+f fullscreen toggle +bindsym $mod+Shift+f exec --no-startup-id syncthing -home="$HOME/.syncthing" + +bindsym $mod+g workspace prev +bindsym $mod+Shift+g exec --no-startup-id gimp; workspace $ws5 + +bindsym $mod+h focus left +bindsym $mod+Shift+h move left 30 + +bindsym $mod+j focus down +bindsym $mod+Shift+j move down 30 + +bindsym $mod+k focus up +bindsym $mod+Shift+k move up 30 + +bindsym $mod+l focus right +bindsym $mod+Shift+l move right 30 + +bindsym $mod+z gaps outer current plus 5 +bindsym $mod+Shift+z gaps outer current minus 5 + +bindsym $mod+x exec --no-startup-id ~/.config/i3/lock.sh +bindsym $mod+Shift+x exec sudo shutdown -h now + +bindsym $mod+c exec $term -e weechat -d ~/.config/weechat +bindsym $mod+Shift+c exec --no-startup-id audacity + +bindsym $mod+v exec $term -e vis +bindsym $mod+Shift+v exec --no-startup-id projectM-pulseaudio + +bindsym $mod+b exec $prev +bindsym $mod+Shift+b exec --no-startup-id blender; workspace $ws6 + +bindsym $mod+n exec $next +bindsym $mod+Shift+n exec $term -e newsbeuter + +bindsym $mod+m exec $term -e $music +bindsym $mod+Shift+m exec $mute + +bindsym $mod+semicolon workspace next +bindsym $mod+apostrophe split horizontal ;; exec $term +bindsym $mod+slash split vertical ;; exec $term +bindsym $mod+Shift+slash kill +bindsym $mod+backslash workspace back_and_forth +#END + +#Workspaces + +set $ws1 "1: " +set $ws2 "2: " +set $ws3 "3: " +set $ws4 "4: " +set $ws5 "5: " +set $ws6 "6: " +set $ws7 "7: " +set $ws8 "8: " +set $ws9 "9: " +set $ws10 "10: " + +# switch to workspace +bindsym $mod+1 workspace $ws1 +bindsym $mod+2 workspace $ws2 +bindsym $mod+3 workspace $ws3 +bindsym $mod+4 workspace $ws4 +bindsym $mod+5 workspace $ws5 +bindsym $mod+6 workspace $ws6 +bindsym $mod+7 workspace $ws7 +bindsym $mod+8 workspace $ws8 +bindsym $mod+9 workspace $ws9 +bindsym $mod+0 workspace $ws10 + +# move focused container to workspace +bindsym $mod+Shift+1 move container to workspace $ws1 +bindsym $mod+Shift+2 move container to workspace $ws2 +bindsym $mod+Shift+3 move container to workspace $ws3 +bindsym $mod+Shift+4 move container to workspace $ws4 +bindsym $mod+Shift+5 move container to workspace $ws5 +bindsym $mod+Shift+6 move container to workspace $ws6 +bindsym $mod+Shift+7 move container to workspace $ws7 +bindsym $mod+Shift+8 move container to workspace $ws8 +bindsym $mod+Shift+9 move container to workspace $ws9 +bindsym $mod+Shift+0 move container to workspace $ws10 + +for_window [class="Blender"] move to workspace $ws6 +for_window [class="Icedove"] move to workspace $ws9 +for_window [class="Thunderbird"] move to workspace $ws9 +for_window [class="marble"] move to workspace $ws7 +#for_window [class="Iceweasel"] move to workspace $ws2 +#for_window [class="Firefox"] move to workspace $ws2 +for_window [title="GIMP Startup"] move workspace $ws5 +for_window [class="Gimp"] move workspace $ws5 +for_window [class="VirtualBox"] floating enable +for_window [window_role="gimp-dock"] floating disable; move left; resize shrink width 50 px or 50ppt +for_window [window_role="gimp-toolbox"] floating disable; move right; resize grow width 30 px or 30ppt +for_window [window_role="tr-main"] move to workspace $ws8 +#for_window [title="projectM"] exec --no-startup-id transset -a --dec .25 +for_window [window_role="GtkFileChooserDialog"] resize shrink height 10 px + +workspace $ws6 gaps inner 0 +workspace $ws6 gaps outer 0 +workspace $ws5 gaps inner 0 +workspace $ws5 gaps outer 0 +workspace $ws8 gaps inner 0 +workspace $ws8 gaps outer 0 + +#Function Buttons +#bindsym F1 nop +#bindsym F2 +#bindsym F3 +#bindsym F4 +#bindsym F5 +#bindsym F6 +#bindsym F7 +#bindsym F8 +#bindsym F9 +#bindsym F10 +#bindsym F11 fullscreen toggle +#bindsym F12 + +bindsym $mod+F1 exec --no-startup-id mupdf ~/.config/Scripts/luke_guide.pdf +bindsym $mod+F2 exec --no-startup-id python ~/.config/Scripts/shortcuts.py +bindsym $mod+F3 exec --no-startup-id arandr +bindsym $mod+F4 exec --no-startup-id sudo zzz +bindsym $mod+F5 exec --no-startup-id sudo sv restart NetworkManager +bindsym $mod+F6 exec --no-startup-id ~/.config/Scripts/transmission.sh +#bindsym $mod+F6 exec --no-startup-id $term -e transmission-remote-cli +bindsym $mod+F7 exec transset -a --dec .15 +bindsym $mod+F8 exec transset -a --inc .15 +bindsym $mod+F9 exec --no-startup-id sudo mount -a +#bindsym $mod+F9 exec --no-startup-id bash ~/.config/i3/mount.sh +bindsym $mod+F10 exec --no-startup-id bash ~/.config/i3/unmount.sh +bindsym $mod+F11 exec $video +bindsym $mod+F12 exec $screencast_sys + +#Arrow Keys +bindsym $mod+Left focus left +bindsym $mod+Down focus down +bindsym $mod+Up focus up +bindsym $mod+Right focus right +bindsym $mod+Shift+Left move left +bindsym $mod+Shift+Down move down +bindsym $mod+Shift+Up move up +bindsym $mod+Shift+Right move right +#END + +#Quick Workspace Movement +bindsym $mod+Home workspace $ws1 +bindsym $mod+Shift+Home move container to workspace $ws1 +bindsym $mod+End workspace $ws10 +bindsym $mod+Shift+End move container to workspace $ws10 +bindsym $mod+Prior workspace prev +bindsym $mod+Shift+Prior move container to workspace prev +bindsym $mod+Next workspace next +bindsym $mod+Shift+Next move container to workspace next +#END + + + +#Media +bindsym $mod+plus exec $inc +bindsym $mod+Shift+plus exec $biginc + +bindsym $mod+minus exec $dec +bindsym $mod+Shift+minus exec $bigdec + +bindsym $mod+bracketleft exec $lilbak +bindsym $mod+Shift+bracketleft exec $bigbak + +bindsym $mod+bracketright exec $lilfor +bindsym $mod+Shift+bracketright exec $bigfor + +bindsym $mod+greater exec $inc +bindsym $mod+Shift+greater exec $biginc + +bindsym $mod+less exec $dec +bindsym $mod+Shift+less exec $bigdec + +bindsym Print exec --no-startup-id scrot +bindsym Shift+Print exec --no-startup-id scrot -u + +bindsym Scroll_Lock exec $audio +bindsym Num_Lock exec $video +bindsym $mod+Scroll_Lock exec $stoprec + +bindsym Pause exec $screencast +bindsym Shift+Pause exec $screencast_sys +bindsym $mod+Pause exec $stoprec + +bindsym XF86Launch1 exec $stoprec +bindsym $mod+XF86Launch1 exec --no-startup-id xset dpms force off + +bindsym XF86AudioPlay exec $pause +bindsym XF86AudioPause exec $pause +bindsym XF86AudioNext exec $next +bindsym XF86AudioPrev exec $prev +bindsym XF86AudioStop exec $pause + +bindsym XF86AudioRaiseVolume exec $inc +bindsym XF86AudioLowerVolume exec $dec +bindsym XF86AudioMute exec $mute +bindsym XF86AudioMicMute exec $micmute +#END + +bindsym XF86Mail exec $term -e mutt -F ~/.config/mutt/muttrc +bindsym XF86MyComputer exec $term -e ranger / +#bindsym $mod+XF86MyComputer exec +bindsym XF86HomePage exec --no-startup-id $BROWSER lukesmith.xyz +bindsym $mod+XF86HomePage exec --no-startup-id $BROWSER +bindsym $mod+XF86WWW exec --no-startup-id $BROWSER + +bindsym XF86Calculator exec $term -e qalc + +bindsym XF86MonBrightnessUp exec --no-startup-id xbacklight -inc 15 +bindsym XF86MonBrightnessDown exec --no-startup-id xbacklight -dec 15 + +for_window [instance="status"] move container to workspace $ws8 diff --git a/.config/i3/lock.png b/.config/i3/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..cd8b22805b45598d20da68659fb3f4f2da545fad GIT binary patch literal 5016 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 ia@G2Sxz>prD*q3~jyNGKOX8mZ0000 +volume_down_5 = , - +volume_up_5 = . = +volume_10 = M-1 +volume_20 = M-2 +volume_30 = M-3 +volume_40 = M-4 +volume_50 = M-5 +volume_60 = M-6 +volume_70 = M-7 +volume_80 = M-8 +volume_90 = M-9 + +# Directory navigation keys: defaults are Shift-number +# (i.e., 'shift 1' -> '!' -> 'Fastdir1'). +go_to_a_directory = i +go_to_music_directory = M-m +go_to_fast_dir1 = M-a +go_to_fast_dir2 = M-l +go_to_fast_dir3 = M-p +go_to_fast_dir4 = M-d +go_to_fast_dir5 = M-c +go_to_fast_dir6 = ^ +go_to_fast_dir7 = & +go_to_fast_dir8 = * +go_to_fast_dir9 = ( +go_to_fast_dir10 = ) +#go_to_playing_file = G +go_up = h + +# Playlist specific keys: +add_file = a +add_directory = A +#plist_add_stream = u +delete_from_playlist = x +playlist_full_paths = P +plist_move_up = K +plist_move_down = J +save_playlist = V +remove_dead_entries = Y +clear_playlist = C + +# Queue manipulation keys: +enqueue_file = z +clear_queue = Z + +# User interaction control: +history_up = UP +history_down = DOWN +delete_to_start = ^u +delete_to_end = ^k +cancel = ^x ESCAPE +hide_message = M + +# Softmixer specific keys: +toggle_softmixer = w +#toggle_make_mono = J + +# Equalizer specific keys: +toggle_equalizer = E +equalizer_refresh = e +#equalizer_prev = K +#equalizer_next = k + +# External commands: +mark_start = ' +mark_end = " +exec_command1 = F1 +exec_command2 = F2 +exec_command3 = F3 +exec_command4 = F4 +exec_command5 = F5 +exec_command6 = F6 +exec_command7 = F7 +exec_command8 = F8 +exec_command9 = F9 +exec_command10 = F10 + +# The following commands are available but not assigned to any keys by +# default: +# +# toggle_percent Switch on/off play progress bar time percentage +# diff --git a/.config/moc/themes/theme b/.config/moc/themes/theme new file mode 100755 index 00000000..561ccec2 --- /dev/null +++ b/.config/moc/themes/theme @@ -0,0 +1,32 @@ +background = default default +frame = red default +window_title = white default +directory = blue default bold +selected_directory = black blue bold +playlist = white default bold +selected_playlist = black blue bold +file = yellow default +selected_file = black yellow +marked_file = green default bold +marked_selected_file = black blue bold +info = blue default bold +status = white default +title = white default bold +state = white default bold +current_time = white default bold +time_left = white default +total_time = white default bold +time_total_frames = white default +sound_parameters = white default bold +legend = white default +disabled = blue default +enabled = white default bold +empty_mixer_bar = white default +filled_mixer_bar = black cyan +empty_time_bar = white default +filled_time_bar = black cyan +entry = red default +entry_title = black cyan +error = red default bold +message = green default bold +plist_time = white default bold diff --git a/.config/mpv/input.conf b/.config/mpv/input.conf new file mode 100644 index 00000000..0a9b5fd4 --- /dev/null +++ b/.config/mpv/input.conf @@ -0,0 +1,199 @@ +# mpv keybindings +# +# Location of user-defined bindings: ~/.config/mpv/input.conf +# +# Lines starting with # are comments. Use SHARP to assign the # key. +# Copy this file and uncomment and edit the bindings you want to change. +# +# List of commands and further details: DOCS/man/input.rst +# List of special keys: --input-keylist +# Keybindings testing mode: mpv --input-test --force-window --idle +# +# Use 'ignore' to unbind a key fully (e.g. 'ctrl+a ignore'). +# +# Strings need to be quoted and escaped: +# KEY show-text "This is a single backslash: \\ and a quote: \" !" +# +# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with +# the modifiers Shift, Ctrl, Alt and Meta (may not work on the terminal). +# +# The default keybindings are hardcoded into the mpv binary. +# You can disable them completely with: --no-input-default-bindings + +# Developer note: +# On compilation, this file is baked into the mpv binary, and all lines are +# uncommented (unless '#' is followed by a space) - thus this file defines the +# default key bindings. + +# If this is enabled, treat all the following bindings as default. +#default-bindings start + +#MOUSE_BTN0 ignore # don't do anything +#MOUSE_BTN0_DBL cycle fullscreen # toggle fullscreen on/off +#MOUSE_BTN2 cycle pause # toggle pause on/off +#MOUSE_BTN3 seek 10 +#MOUSE_BTN4 seek -10 +#MOUSE_BTN5 add volume -2 +#MOUSE_BTN6 add volume 2 + +# Mouse wheels, touchpad or other input devices that have axes +# if the input devices supports precise scrolling it will also scale the +# numeric value accordingly +#AXIS_UP seek 10 +#AXIS_DOWN seek -10 +#AXIS_LEFT seek 5 +#AXIS_RIGHT seek -5 + +## Seek units are in seconds, but note that these are limited by keyframes +#RIGHT seek 5 +#LEFT seek -5 +#UP seek 60 +#DOWN seek -60 +# Do smaller, always exact (non-keyframe-limited), seeks with shift. +# Don't show them on the OSD (no-osd). +#Shift+RIGHT no-osd seek 1 exact +#Shift+LEFT no-osd seek -1 exact +#Shift+UP no-osd seek 5 exact +#Shift+DOWN no-osd seek -5 exact +# Skip to previous/next subtitle (subject to some restrictions; see manpage) +#Ctrl+LEFT no-osd sub-seek -1 +#Ctrl+RIGHT no-osd sub-seek 1 +#PGUP add chapter 1 # skip to next chapter +#PGDWN add chapter -1 # skip to previous chapter +#Shift+PGUP seek 600 +#Shift+PGDWN seek -600 +#[ multiply speed 0.9091 # scale playback speed +#] multiply speed 1.1 +#{ multiply speed 0.5 +#} multiply speed 2.0 +#BS set speed 1.0 # reset speed to normal +#q quit +#Q quit-watch-later +#q {encode} quit 4 +#ESC set fullscreen no +#ESC {encode} quit 4 +#p cycle pause # toggle pause/playback mode +#. frame-step # advance one frame and pause +#, frame-back-step # go back by one frame and pause +#SPACE cycle pause +#> playlist-next # skip to next file +#ENTER playlist-next # skip to next file +#< playlist-prev # skip to previous file +#O no-osd cycle-values osd-level 3 1 # cycle through OSD mode +#o show-progress +#P show-progress +#I show-text "${filename}" # display filename in osd +#z add sub-delay -0.1 # subtract 100 ms delay from subs +#x add sub-delay +0.1 # add +#ctrl++ add audio-delay 0.100 # this changes audio/video sync +#ctrl+- add audio-delay -0.100 +#9 add volume -2 +#/ add volume -2 +#0 add volume 2 +#* add volume 2 +#m cycle mute +#1 add contrast -1 +#2 add contrast 1 +#3 add brightness -1 +#4 add brightness 1 +#5 add gamma -1 +#6 add gamma 1 +#7 add saturation -1 +#8 add saturation 1 +#Alt+0 set window-scale 0.5 +#Alt+1 set window-scale 1.0 +#Alt+2 set window-scale 2.0 +# toggle deinterlacer (automatically inserts or removes required filter) +#d cycle deinterlace +#r add sub-pos -1 # move subtitles up +#t add sub-pos +1 # down +#v cycle sub-visibility +# stretch SSA/ASS subtitles with anamorphic videos to match historical +#V cycle sub-ass-vsfilter-aspect-compat +# switch between applying no style overrides to SSA/ASS subtitles, and +# overriding them almost completely with the normal subtitle style +#u cycle-values sub-ass-style-override "force" "no" +#j cycle sub # cycle through subtitles +#J cycle sub down # ...backwards +#SHARP cycle audio # switch audio streams +#_ cycle video +#T cycle ontop # toggle video window ontop of other windows +#f cycle fullscreen # toggle fullscreen +#s screenshot # take a screenshot +#S screenshot video # ...without subtitles +#Ctrl+s screenshot window # ...with subtitles and OSD, and scaled +#Alt+s screenshot each-frame # automatically screenshot every frame +#w add panscan -0.1 # zoom out with -panscan 0 -fs +#e add panscan +0.1 # in +# cycle video aspect ratios; "-1" is the container aspect +#A cycle-values video-aspect "16:9" "4:3" "2.35:1" "-1" +#POWER quit +#PLAY cycle pause +#PAUSE cycle pause +#PLAYPAUSE cycle pause +#STOP quit +#FORWARD seek 60 +#REWIND seek -60 +#NEXT playlist-next +#PREV playlist-prev +#VOLUME_UP add volume 2 +#VOLUME_DOWN add volume -2 +#MUTE cycle mute +#CLOSE_WIN quit +#CLOSE_WIN {encode} quit 4 +#E cycle edition # next edition +#l ab-loop # Set/clear A-B loop points +#L cycle-values loop "inf" "no" # toggle infinite looping +#ctrl+c quit 4 + +# Apple Remote section +#AR_PLAY cycle pause +#AR_PLAY_HOLD quit +#AR_CENTER cycle pause +#AR_CENTER_HOLD quit +#AR_NEXT seek 10 +#AR_NEXT_HOLD seek 120 +#AR_PREV seek -10 +#AR_PREV_HOLD seek -120 +#AR_MENU show-progress +#AR_MENU_HOLD cycle mute +#AR_VUP add volume 2 +#AR_VUP_HOLD add chapter 1 +#AR_VDOWN add volume -2 +#AR_VDOWN_HOLD add chapter -1 + +# For tv:// +#h cycle tv-channel -1 # previous channel +#k cycle tv-channel +1 # next channel + +# For dvb:// +#H cycle dvb-channel-name -1 # previous channel +#K cycle dvb-channel-name +1 # next channel + +# +# Legacy bindings (may or may not be removed in the future) +# +#! add chapter -1 # skip to previous chapter +#@ add chapter 1 # next + +# +# Not assigned by default +# (not an exhaustive list of unbound commands) +# + +# ? add sub-scale +0.1 # increase subtitle font size +# ? add sub-scale -0.1 # decrease subtitle font size +# ? sub-step -1 # immediately display next subtitle +# ? sub-step +1 # previous +# ? cycle angle # switch DVD/Bluray angle +# ? add balance -0.1 # adjust audio balance in favor of left +# ? add balance 0.1 # right +# ? cycle sub-forced-only # toggle DVD forced subs +# ? cycle program # cycle transport stream programs +# ? stop # stop playback (quit or enter idle mode) + +l seek 5 +h seek -5 +j seek -60 +k seek 60 +S cycle sub diff --git a/.config/mutt/aliases b/.config/mutt/aliases new file mode 100644 index 00000000..86a20297 --- /dev/null +++ b/.config/mutt/aliases @@ -0,0 +1,13 @@ +#Here you can put email aliases. Here's an example: + +alias luke luke@lukesmith.xyz + +#When you put "luke" as the recipient in mutt, it will automatically direct it to my email address. + +#You can also put multiple emails under one aliases: + +alias project_members billy@gmail.com, sally@gmail.com, amanda@gmail.com, chad@gmail.com + +#With the above alias, if you put "project_members" as the recipient, mutt will fill in all the emails above as the recipients. +# +#This is extremely useful, so take advantage of it! diff --git a/.config/mutt/gmailrc b/.config/mutt/gmailrc new file mode 100644 index 00000000..68b6ff9d --- /dev/null +++ b/.config/mutt/gmailrc @@ -0,0 +1,28 @@ +# vim: filetype=muttrc +set imap_user = "YOURNAME@gmail.com" +set smtp_url = "smtp://YOURNAME@smtp.gmail.com:587/" +set from = "YOURNAME@gmail.com" +set realname = "YOUR ACTUAL NAME" +set mbox_type = Maildir +set sendmail = "/usr/bin/msmtp -a gmail" +set folder = "~/.Mail/Gmail" +set spoolfile = "+INBOX" +set record = /dev/null +set mbox = "+[Gmail].All Mail" +set postponed = "+[Gmail].Drafts" +set header_cache = ~/.config/mutt/gmail/cache/headers +set message_cachedir = ~/.config/mutt/gmail/cache/bodies +set certificate_file = ~/.config/mutt/gmail/certificates +set ssl_starttls = yes +set ssl_force_tls = yes + +mailboxes +INBOX + +macro index gi "=INBOX" "Go to inbox" +macro index ga "=[Gmail].All Mail" "Go to all mail" +macro index gs "=[Gmail].Sent Mail" "Go to sent mail" +macro index gS "=[Gmail].Spam" "Go to spam" +macro index gt "=[Gmail].Trash" "Go to trash" +macro index gd "=[Gmail].Drafts" "Go to drafts" +macro index gl "=lingcircle" "Go to lingcircle" +macro index gr "=ugaroml" "Go to ugaroml" diff --git a/.config/mutt/luke_mutt_readme.md b/.config/mutt/luke_mutt_readme.md new file mode 100644 index 00000000..b805ca7b --- /dev/null +++ b/.config/mutt/luke_mutt_readme.md @@ -0,0 +1,61 @@ +# Luke's mutt readme + +mutt is a terminal email program. + +## Using my configs + +Move the muttrc file to ~/.muttrc to activate it by default. + +Make an rc file for your own email address. If you have a Gmail account, for example, modify the "gmailrc" file to your address. Make sure muttrc is sourcing the file you want. + +You should also have Neomutt installed for full compatibility. + +## Macros and shortcuts + +I've made some changes to the bindings to make mutt a little more vim-like. See them in muttrc for yourself. + +I've also employed gotbletu's shortcuts for changing accounts and moving from Inbox to Sent to Drafts etc. + +Note that the commands for movement to different boxes are in the individual email configs. This is because the folder structure of each account may differ. When mutt loads another email account, it also loades the new shortcuts. You may need to make changes for your email account. + +## Colors and themes + +My muttrc has my particular formats for email headers and date. + +You have a lot of freedom in how you can configure how mutt works. Here, I have a file "muttcol" which is originally someone else's theme (can't remember where I got it), but I've also made color changes in the muttrc. + +My config is made for Neomutt, which allows email headers to have different colors for different information. If you're not running Neomutt, you'll get errors when using my configs. To remove these errors delete the following lines from the muttrc: + +color index_author red default '.*' +color index_number blue default +color index_subject cyan default '.s' +color index_size green default + +## Aliases + +You can use aliases to avoid having to type out email addresses over and over. For example, if you have the following: + +alias luke luke@lukesmith.xyz + +whenever you type in "luke" as a recipient, it will automatically direct that you my email. You can also have aliases for groups of people, separated by a comma. + +alias best_friends billy@gmail.com, sally@gmail.com, amanda@gmail.com, chad@gmail.com + +so whenever you type in "best_friends," mutt will direct the mail to all four of these email addresses. + +## Passwords + +If you don't want mutt to ask for your password when logging on, set imap_pass to your password. + +imap_pass = muh_password + + +Same is true of smtp_pass if you want to send emails without a password. + +smtp = muh_password + +There are securer ways of storing your pass word, which you can research youself. I'm still figuring out an elegant solution that works for me. + +## To come... + +I'm still figuring out offlineIMAPs and some other little things to interface with mutt. I'll put up a new video when I'm totally successful. diff --git a/.config/mutt/mailcap b/.config/mutt/mailcap new file mode 100755 index 00000000..0e8631e7 --- /dev/null +++ b/.config/mutt/mailcap @@ -0,0 +1,11 @@ +#text/html; qutebrowser %s &; test=test -n "$DISPLAY"; needsterminal; + +text/html; w3m -I %{charset} -T text/html; copiousoutput; +#text/html; mv %s %s.html && qutebrowser %s.html > /dev/null; needsterminal; +# +application/pdf; mv %s %s.pdf && mupdf %s.pdf > /dev/null; needsterminal; + +image/*; ~/.config/mutt/muttimage.sh %s ; copiousoutput + +#application/pdf; pdftotext '%s' -; copiousoutput; description=PDF Document; +#nametemplate=%s.pdf diff --git a/.config/mutt/mutt.sh b/.config/mutt/mutt.sh new file mode 100644 index 00000000..537322ee --- /dev/null +++ b/.config/mutt/mutt.sh @@ -0,0 +1,4 @@ +#!/bin/bash +pwds='gpg --decrypt ~/.config/Scripts/DELET' +eval "$pwds" +exec mutt -F ~/.config/mutt/muttrc "$@" diff --git a/.config/mutt/muttcol b/.config/mutt/muttcol new file mode 100755 index 00000000..e9d6defb --- /dev/null +++ b/.config/mutt/muttcol @@ -0,0 +1,151 @@ +# vim: filetype=muttrc + +# +# +# make sure that you are using mutt linked against slang, not ncurses, or +# suffer the consequences of weird color issues. use "mutt -v" to check this. + +# custom body highlights ----------------------------------------------- +# highlight my name and other personally relevant strings +#color body yellow default "(ethan|schoonover)" +# custom index highlights ---------------------------------------------- +# messages which mention my name in the body +#color index yellow default "~b \"phil(_g|\!| gregory| gold)|pgregory\" !~N !~T !~F !~p !~P" +#color index J_cream brightwhite "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~N !~T !~F !~p !~P" +#color index yellow cyan "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~T !~F !~p !~P" +#color index yellow J_magent "~b \"phil(_g|\!| gregory| gold)|pgregory\" ~F !~p !~P" +## messages which are in reference to my mails +#color index J_magent default "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" !~N !~T !~F !~p !~P" +#color index J_magent brightwhite "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~N !~T !~F !~p !~P" +#color index J_magent cyan "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~T !~F !~p !~P" +#color index J_magent red "~x \"(mithrandir|aragorn)\\.aperiodic\\.net|thorin\\.hillmgt\\.com\" ~F !~p !~P" + +# for background in 16 color terminal, valid background colors include: +# base03, bg, black, any of the non brights + +# basic colors --------------------------------------------------------- +color normal brightyellow default +color error red default +color tilde black default +color message cyan default +color markers red white +color attachment white default +color search brightmagenta default +#color status J_black J_status +color status brightyellow black +color indicator brightblack yellow +color tree yellow default # arrow in threads + +# basic monocolor screen +mono bold bold +mono underline underline +mono indicator reverse +mono error bold + +# index ---------------------------------------------------------------- + +#color index red default "~D(!~p|~p)" # deleted +#color index black default ~F # flagged +#color index brightred default ~= # duplicate messages +#color index brightgreen default "~A!~N!~T!~p!~Q!~F!~D!~P" # the rest +#color index J_base default "~A~N!~T!~p!~Q!~F!~D" # the rest, new +color index red default "~A" # all messages +color index brightred default "~E" # expired messages +color index blue default "~N" # new messages +color index blue default "~O" # old messages +color index brightmagenta default "~Q" # messages that have been replied to +color index brightgreen default "~R" # read messages +color index blue default "~U" # unread messages +color index blue default "~U~$" # unread, unreferenced messages +color index brightyellow default "~v" # messages part of a collapsed thread +color index brightyellow default "~P" # messages from me +color index cyan default "~p!~F" # messages to me +color index cyan default "~N~p!~F" # new messages to me +color index cyan default "~U~p!~F" # unread messages to me +color index brightgreen default "~R~p!~F" # messages to me +color index red default "~F" # flagged messages +color index red default "~F~p" # flagged messages to me +color index red default "~N~F" # new flagged messages +color index red default "~N~F~p" # new flagged messages to me +color index red default "~U~F~p" # new flagged messages to me +color index black red "~D" # deleted messages +color index brightcyan default "~v~(!~N)" # collapsed thread with no unread +color index yellow default "~v~(~N)" # collapsed thread with some unread +color index green default "~N~v~(~N)" # collapsed thread with unread parent +# statusbg used to indicated flagged when foreground color shows other status +# for collapsed thread +color index red black "~v~(~F)!~N" # collapsed thread with flagged, no unread +color index yellow black "~v~(~F~N)" # collapsed thread with some unread & flagged +color index green black "~N~v~(~F~N)" # collapsed thread with unread parent & flagged +color index green black "~N~v~(~F)" # collapsed thread with unread parent, no unread inside, but some flagged +color index cyan black "~v~(~p)" # collapsed thread with unread parent, no unread inside, some to me directly +color index yellow red "~v~(~D)" # thread with deleted (doesn't differentiate between all or partial) +#color index yellow default "~(~N)" # messages in threads with some unread +#color index green default "~S" # superseded messages +#color index red default "~T" # tagged messages +#color index brightred red "~=" # duplicated messages + +# message headers ------------------------------------------------------ + +#color header brightgreen default "^" +color hdrdefault brightgreen default +color header brightyellow default "^(From)" +color header blue default "^(Subject)" + +# body ----------------------------------------------------------------- + +color quoted blue default +color quoted1 cyan default +color quoted2 yellow default +color quoted3 red default +color quoted4 brightred default + +color signature brightgreen default +color bold black default +color underline black default +color normal default default +# +color body brightcyan default "[;:][-o][)/(|]" # emoticons +color body brightcyan default "[;:][)(|]" # emoticons +color body brightcyan default "[*]?((N)?ACK|CU|LOL|SCNR|BRB|BTW|CWYL|\ + |FWIW|vbg|GD&R|HTH|HTHBE|IMHO|IMNSHO|\ + |IRL|RTFM|ROTFL|ROFL|YMMV)[*]?" +color body brightcyan default "[ ][*][^*]*[*][ ]?" # more emoticon? +color body brightcyan default "[ ]?[*][^*]*[*][ ]" # more emoticon? + +## pgp + +color body red default "(BAD signature)" +color body cyan default "(Good signature)" +color body brightblack default "^gpg: Good signature .*" +color body brightyellow default "^gpg: " +color body brightyellow red "^gpg: BAD signature from.*" +mono body bold "^gpg: Good signature" +mono body bold "^gpg: BAD signature from.*" + +# yes, an insance URL regex +color body red default "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]" +# and a heavy handed email regex +#color body J_magent default "((@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]),)*@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\.[0-9]?[0-9]?[0-9]\\]):)?[0-9a-z_.+%$-]+@(([0-9a-z-]+\\.)*[0-9a-z-]+\\.?|#[0-9]+|\\[[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\.[0-2]?[0-9]?[0-9]\\])" + +# Various smilies and the like +#color body brightwhite default "<[Gg]>" # +#color body brightwhite default "<[Bb][Gg]>" # +#color body yellow default " [;:]-*[})>{(<|]" # :-) etc... +# *bold* +#color body blue default "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" +#mono body bold "(^|[[:space:][:punct:]])\\*[^*]+\\*([[:space:][:punct:]]|$)" +# _underline_ +#color body blue default "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" +#mono body underline "(^|[[:space:][:punct:]])_[^_]+_([[:space:][:punct:]]|$)" +# /italic/ (Sometimes gets directory names) +#color body blue default "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" +#mono body underline "(^|[[:space:][:punct:]])/[^/]+/([[:space:][:punct:]]|$)" + +# Border lines. +#color body blue default "( *[-+=#*~_]){6,}" + +#folder-hook . "color status J_black J_status " +#folder-hook gmail/inbox "color status J_black yellow " +#folder-hook gmail/important "color status J_black yellow " + diff --git a/.config/mutt/muttimage.sh b/.config/mutt/muttimage.sh new file mode 100755 index 00000000..b67de83b --- /dev/null +++ b/.config/mutt/muttimage.sh @@ -0,0 +1,8 @@ +#! /bin/sh + +#### Determine size of Terminal +height=`stty size | awk 'BEGIN {FS = " "} {print $1;}'` +width=`stty size | awk 'BEGIN {FS = " "} {print $2;}'` + +### Display Image / offset with mutt bar +echo -e "2;3;\n0;1;210;20;$((width*7-250));$((height*14-100));0;0;0;0;$1\n4;\n3;" | /usr/lib/w3m/w3mimgdisplay & diff --git a/.config/mutt/muttrc b/.config/mutt/muttrc new file mode 100755 index 00000000..86b4a0b4 --- /dev/null +++ b/.config/mutt/muttrc @@ -0,0 +1,42 @@ +#source "gpg -dq ~/.ayylmao |" +source ~/.config/mutt/gmailrc +source ~/.config/mutt/muttcol +source ~/.config/mutt/aliases + +set sort = 'reverse-date' +set editor = vim +#set copy = no +set timeout = "5" +set mail_check = "10" +set mailcap_path = ~/.config/mutt/mailcap +set date_format="%m/%d %I:%M" +set index_format="%2C %Z %d %-15.15F %s (%-4.4c)" +#set status_format="mpv ~/.config/mutt/bump.wav" +#set wrap = 0 + +auto_view text/html +alternative_order text/plain text/enriched text/html + +mono bold bold +mono underline underline +mono indicator reverse +color index yellow default '.*' +color index_author red default '.*' +color index_number blue default +color index_subject cyan default '.s' +color index_size green default +color normal default default +color body brightred default [\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+ +color body brightblue default (https?|ftp)://[\-\.,/%~_:?&=\#a-zA-Z0-9]+ + +bind editor noop +bind index G last-entry +bind index gg first-entry +bind index d half-down +bind index u half-up +bind index D delete-message +bind index U undelete-message +bind index F search +bind index R group-reply + +auto_view application/pdf diff --git a/.config/neofetch/config b/.config/neofetch/config new file mode 100755 index 00000000..ece8c0b9 --- /dev/null +++ b/.config/neofetch/config @@ -0,0 +1,431 @@ +#!/usr/bin/env bash +# vim:fdm=marker +# +# Neofetch config file +# https://github.com/dylanaraps/neofetch + +# Speed up script by not using unicode +export LC_ALL=C +export LANG=C + +# Info Options {{{ + + +# Info +# See this wiki page for more info: +# https://github.com/dylanaraps/neofetch/wiki/Customizing-Info +printinfo () { + info title + info underline + + info "Model" model + info "OS" distro + info "Kernel" kernel + info "Uptime" uptime + info "Packages" packages + info "Shell" shell + info "Resolution" resolution + info "DE" de + info "WM" wm + info "WM Theme" wmtheme + #info "Theme" theme + #info "Icons" icons + info "Terminal" term + info "Terminal Font" termfont + info "CPU" cpu + info "GPU" gpu + info "Memory" memory + + info "CPU Usage" cpu_usage + info "Disk" disk + #info "Battery" battery + #info "Font" font + #info "Song" song + # info "Local IP" localip + # info "Public IP" publicip + #info "Users" users + #info "Birthday" birthday + + info linebreak + info cols +} + + +# Kernel + +# Show more kernel info +# --kernel_shorthand on/off +kernel_shorthand="on" + + +# Distro + +# Shorten the output of distro (tiny, on, off) +# NOTE: This is only possible on Linux +distro_shorthand="off" + +# Mac OS X hide/show build version +# --osx_buildversion on/off +osx_buildversion="on" + +# Mac OS X hide/show codename +# --osx_codename on/off +osx_codename="on" + +# Show 'x86_64' and 'x86' in 'Distro:' output. +# --os_arch on/off +os_arch="on" + + +# Uptime + +# Shorten the output of the uptime function +# --uptime_shorthand tiny, on, off +uptime_shorthand="off" + + +# Shell + +# Show the path to $SHELL +# --shell_path on/off +shell_path="on" + +# Show $SHELL's version +# --shell_version on/off +shell_version="off" + + +# CPU + +# CPU speed type +# Only works on Linux with cpufreq. +# --speed_type current, min, max, bios, +# scaling_current, scaling_min, scaling_max +speed_type="max" + +# CPU Shorthand +# Set shorthand setting +# --cpu_shorthand name, speed, tiny, on, off +cpu_shorthand="off" + +# CPU Usage display +# Set CPU usage display setting +# --cpu_display bar, infobar, barinfo, off +cpu_display="off" + +# CPU Cores +# Display CPU cores in output +# --cpu_cores on/off +cpu_cores="on" + + +# GPU + +# Shorten output of the getgpu funcion +# --gpu_shorthand on/off/tiny +gpu_shorthand="on" + +# Enable/Disable GPU Brand +# --gpu_brand on/off +gpu_brand="on" + +# Resolution + +# Display refresh rate next to each monitor +# Unsupported on Windows +# --refresh_rate on/off +refresh_rate="off" + + +# Gtk Theme / Icons + +# Shorten output (Hide [GTK2] etc) +# --gtk_shorthand on/off +gtk_shorthand="off" + + +# Enable/Disable gtk2 theme/icons output +# --gtk2 on/off +gtk2="on" + +# Enable/Disable gtk3 theme/icons output +# --gtk3 on/off +gtk3="on" + + +# Battery + +# Which battery to display. +# By default we display all batteries. +# NOTE: Only works on Linux. +# --battery_num all, 0, 1, 2, etc +battery_num="all" + +# Whether or not to print each battery on the same line. +# By default each battery gets its own line and title. +# NOTE: Only works on Linux. +# --battery_shorthand on/off +battery_shorthand="off" + + +# IP Address + +# Website to ping for the public IP +# --ip_host url +public_ip_host="http://ident.me" + + +# Song + +# Print the Artist and Title on seperate lines +# --song_shorthand on/off +song_shorthand="off" + + +# Birthday + +# Whether to show a long pretty output +# or a shortened one +# NOTE: Long pretty output doesn't work on OpenBSD or NetBSD. +# --birthday_shorthand on/off +birthday_shorthand="off" + +# Whether to show the time in the output +# --birthday_time on/off +birthday_time="on" + +# Date format to use when printing birthday +# --birthday_format "format" +birthday_format="+%a %d %b %Y %l:%M %p" + + +# }}} + +# Text Colors {{{ + + +# Text Colors +# Each number represents a different part of +# the text in this order: +# title, @, underline, subtitle, colon, info +# colors=(4 6 1 8 8 6) +# You can also specify: +# fg (foreground color) +colors=(distro) + + +# }}} + +# Text Options {{{ + + +# Toggle bold text +# --bold on/off +bold="on" + +# Enable/Disable Underline +# --underline on/off +underline_enabled="on" + +# Underline character +# --underline_char char +underline_char="-" + + +# }}} + +# Color Blocks {{{ + + +# Color block range +# --block_range start end +start=0 +end=7 + +# Toggle color blocks +# --color_blocks on/off +color_blocks="on" + +# Color block width in spaces +# --block_width num +block_width=2 + +# Color block height in lines +# --block_height num +block_height=1 + + +# }}} + +# Progress Bars {{{ + + +# Progress bar character +# --progress_char elapsed_char total_char +progress_char_elapsed="-" +progress_char_total="=" + +# Progress border +# --progress_border on/off +progress_border="on" + +# Progress bar length in spaces +# Number of chars long to make the progress bars. +# --progress_length num +progress_length="15" + +# Progress bar colors +# When set to distro, uses your distro's logo colors +# Takes: num, "distro" +# --progress_colors col col +progress_color_elapsed="distro" +progress_color_total="distro" + +# Customize how the info is displayed. +# bar: Only the progress bar is displayed. +# infobar: The bar is displayed after the info. +# barinfo: The bar is displayed before the info. +# off: Only the info is displayed. +# +# --memory_display bar/infobar/barinfo/off +# --battery_display bar/infobar/barinfo/off +# --disk_display bar/infobar/barinfo/off +memory_display="off" +battery_display="off" +disk_display="off" + + +# }}} + +# Image Options {{{ + + +# Image Source +# --image wall, ascii, /path/to/img, /path/to/dir/, off +image="wall" + +# Thumbnail directory +thumbnail_dir="$HOME/.cache/thumbnails/neofetch" + +# W3m-img path +# Some systems have this in another location +w3m_img_path="/usr/lib/w3m/w3mimgdisplay" + +# Image position +# Only works with the w3m backend +# --image_position left/right +image_position="left" + +# Crop mode +# --crop_mode normal/fit/fill +crop_mode="normal" + +# Crop offset +# Only affects normal mode. +# --crop_offset northwest/north/northeast/west/center +# east/southwest/south/southeast +crop_offset="center" + +# Image size +# The image is half the terminal width by default. +# --size auto, 00px, 00%, none +image_size="auto" + +# Right gap between image and text +# --gap num +gap=2 + +# Image offsets +# --xoffset px +# --yoffset px +yoffset=0 +xoffset=0 + + +# }}} + +# Ascii Options {{{ + + +# Default ascii image to use +# When this is set to distro it will use your +# distro's logo as the ascii. +# --ascii 'distro', path/to/ascii +ascii="distro" + +# Ascii distro +# Which distro's ascii art to display. +# --ascii_distro 'auto', 'distro_name' +ascii_distro="auto" + +# Ascii colors +# When this is set to distro it will use your +# ditro's colors to color the ascii. +# NOTE: You can also set this to a range of colors +# which will allow you to custom color distro logos +# --ascii_colors distro +# --ascii_colors 2 4 5 6 +ascii_colors=(distro) + +# Logo size +# Arch, Crux and Gentoo have a smaller logo +# variant. Changing the value below to small +# will make neofetch use the small logo. +# --ascii_logo_size small, normal +ascii_logo_size="normal" + +# Bold ascii logo +# Whether or not to bold the ascii logo. +# --ascii_bold on/off +ascii_bold="off" + + +# }}} + +# Scrot Options {{{ + + +# Whether or not to always take a screenshot +# You can manually take a screenshot with "--scrot" or "-s" +scrot="off" + +# Screenshot program to launch +# --scrot_cmd +scrot_cmd="scrot -c -d 3" + +# Scrot dir +# Where to save the screenshots +# --scrot_dir /path/to/screenshot/folder +scrot_dir="$HOME/Pictures/" + +# Scrot filename +# What to name the screenshots +# --scrot_name str +scrot_name="neofetch-$(date +%F-%I-%M-%S-${RANDOM}).png" + +# Image upload host +# Where to upload the image. +# Possible values: imgur, teknik +image_host="imgur" + +# Imgur api key +# This is an api key for neofetch, you can sign up for your own +# here: http://api.imgur.com/oauth2/addclient +imgur_client_id="0e8b44d15e9fc95" + + +# }}} + +# Config Options {{{ + + +# Enable/Disable config file +# --config off, none +config="on" + +# Path to custom config file location +# --config path/to/config +config_file="${XDG_CONFIG_HOME:-${HOME}/.config}/neofetch/config" + + +# }}} diff --git a/.config/ranger/commands.py b/.config/ranger/commands.py new file mode 100755 index 00000000..8c5e7486 --- /dev/null +++ b/.config/ranger/commands.py @@ -0,0 +1,216 @@ +# This is a sample commands.py. You can add your own commands here. +# +# Please refer to commands_full.py for all the default commands and a complete +# documentation. Do NOT add them all here, or you may end up with defunct +# commands when upgrading ranger. + +# You always need to import ranger.api.commands here to get the Command class: +from ranger.api.commands import * + +# A simple command for demonstration purposes follows. +#------------------------------------------------------------------------------ + +# You can import any python module as needed. +import os + +# Any class that is a subclass of "Command" will be integrated into ranger as a +# command. Try typing ":my_edit" in ranger! +class my_edit(Command): + # The so-called doc-string of the class will be visible in the built-in + # help that is accessible by typing "?c" inside ranger. + """:my_edit + + A sample command for demonstration purposes that opens a file in an editor. + """ + + # The execute method is called when you run this command in ranger. + def execute(self): + # self.arg(1) is the first (space-separated) argument to the function. + # This way you can write ":my_edit somefilename". + if self.arg(1): + # self.rest(1) contains self.arg(1) and everything that follows + target_filename = self.rest(1) + else: + # self.fm is a ranger.core.filemanager.FileManager object and gives + # you access to internals of ranger. + # self.fm.thisfile is a ranger.container.file.File object and is a + # reference to the currently selected file. + target_filename = self.fm.thisfile.path + + # This is a generic function to print text in ranger. + self.fm.notify("Let's edit the file " + target_filename + "!") + + # Using bad=True in fm.notify allows you to print error messages: + if not os.path.exists(target_filename): + self.fm.notify("The given file does not exist!", bad=True) + return + + # This executes a function from ranger.core.acitons, a module with a + # variety of subroutines that can help you construct commands. + # Check out the source, or run "pydoc ranger.core.actions" for a list. + self.fm.edit_file(target_filename) + + # The tab method is called when you press tab, and should return a list of + # suggestions that the user will tab through. + def tab(self): + # This is a generic tab-completion function that iterates through the + # content of the current directory. + return self._tab_directory_content() + + +# https://github.com/ranger/ranger/wiki/Integrating-File-Search-with-fzf +# Now, simply bind this function to a key, by adding this to your ~/.config/ranger/rc.conf: map fzf_select +class fzf_select(Command): + """ + :fzf_select + + Find a file using fzf. + + With a prefix argument select only directories. + + See: https://github.com/junegunn/fzf + """ + def execute(self): + import subprocess + if self.quantifier: + # match only directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + else: + # match files and directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + fzf = self.fm.execute_command(command, stdout=subprocess.PIPE) + stdout, stderr = fzf.communicate() + if fzf.returncode == 0: + fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n')) + if os.path.isdir(fzf_file): + self.fm.cd(fzf_file) + else: + self.fm.select_file(fzf_file) +# fzf_locate +class fzf_locate(Command): + """ + :fzf_locate + + Find a file using fzf. + + With a prefix argument select only directories. + + See: https://github.com/junegunn/fzf + """ + def execute(self): + import subprocess + if self.quantifier: + command="locate home media | fzf -e -i" + else: + command="locate home media | fzf -e -i" + fzf = self.fm.execute_command(command, stdout=subprocess.PIPE) + stdout, stderr = fzf.communicate() + if fzf.returncode == 0: + fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n')) + if os.path.isdir(fzf_file): + self.fm.cd(fzf_file) + else: + self.fm.select_file(fzf_file) + +class fzf_bring(Command): + """ + :fzf_bring + + Find a file using fzf and bring it to the current directory. + + See: https://github.com/junegunn/fzf + """ + def execute(self): + import subprocess + if self.quantifier: + # match only directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -type d -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + else: + # match files and directories + command="find -L . \( -path '*/\.*' -o -fstype 'dev' -o -fstype 'proc' \) -prune \ + -o -print 2> /dev/null | sed 1d | cut -b3- | fzf +m" + fzf = self.fm.execute_command(command, stdout=subprocess.PIPE) + stdout, stderr = fzf.communicate() + if fzf.returncode == 0: + fzf_file = os.path.abspath(stdout.decode('utf-8').rstrip('\n')) + if os.path.isdir(fzf_file): + self.fm.cd(fzf_file) + else: + self.fm.select_file(fzf_file) + + +import os +from ranger.core.loader import CommandLoader + +class compress(Command): + def execute(self): + """ Compress marked files to current directory """ + cwd = self.fm.thisdir + marked_files = cwd.get_selection() + + if not marked_files: + return + + def refresh(_): + cwd = self.fm.get_directory(original_path) + cwd.load_content() + + original_path = cwd.path + parts = self.line.split() + au_flags = parts[1:] + + descr = "compressing files in: " + os.path.basename(parts[1]) + obj = CommandLoader(args=['apack'] + au_flags + \ + [os.path.relpath(f.path, cwd.path) for f in marked_files], descr=descr) + + obj.signal_bind('after', refresh) + self.fm.loader.add(obj) + + def tab(self): + """ Complete with current folder name """ + + extension = ['.zip', '.tar.gz', '.rar', '.7z'] + return ['compress ' + os.path.basename(self.fm.thisdir.path) + ext for ext in extension] + + + + +import os +from ranger.core.loader import CommandLoader + +class extracthere(Command): + def execute(self): + """ Extract copied files to current directory """ + copied_files = tuple(self.fm.copy_buffer) + + if not copied_files: + return + + def refresh(_): + cwd = self.fm.get_directory(original_path) + cwd.load_content() + + one_file = copied_files[0] + cwd = self.fm.thisdir + original_path = cwd.path + au_flags = ['-X', cwd.path] + au_flags += self.line.split()[1:] + au_flags += ['-e'] + + self.fm.copy_buffer.clear() + self.fm.cut_buffer = False + if len(copied_files) == 1: + descr = "extracting: " + os.path.basename(one_file.path) + else: + descr = "extracting files from: " + os.path.basename(one_file.dirname) + obj = CommandLoader(args=['aunpack'] + au_flags \ + + [f.path for f in copied_files], descr=descr) + + obj.signal_bind('after', refresh) + self.fm.loader.add(obj) + + + diff --git a/.config/ranger/commands_full.py b/.config/ranger/commands_full.py new file mode 100755 index 00000000..9f0481ce --- /dev/null +++ b/.config/ranger/commands_full.py @@ -0,0 +1,1508 @@ +# -*- coding: utf-8 -*- +# This file is part of ranger, the console file manager. +# This configuration file is licensed under the same terms as ranger. +# =================================================================== +# +# NOTE: If you copied this file to ~/.config/ranger/commands_full.py, +# then it will NOT be loaded by ranger, and only serve as a reference. +# +# =================================================================== +# This file contains ranger's commands. +# It's all in python; lines beginning with # are comments. +# +# Note that additional commands are automatically generated from the methods +# of the class ranger.core.actions.Actions. +# +# You can customize commands in the file ~/.config/ranger/commands.py. +# It has the same syntax as this file. In fact, you can just copy this +# file there with `ranger --copy-config=commands' and make your modifications. +# But make sure you update your configs when you update ranger. +# +# =================================================================== +# Every class defined here which is a subclass of `Command' will be used as a +# command in ranger. Several methods are defined to interface with ranger: +# execute(): called when the command is executed. +# cancel(): called when closing the console. +# tab(): called when is pressed. +# quick(): called after each keypress. +# +# The return values for tab() can be either: +# None: There is no tab completion +# A string: Change the console to this string +# A list/tuple/generator: cycle through every item in it +# +# The return value for quick() can be: +# False: Nothing happens +# True: Execute the command afterwards +# +# The return value for execute() and cancel() doesn't matter. +# +# =================================================================== +# Commands have certain attributes and methods that facilitate parsing of +# the arguments: +# +# self.line: The whole line that was written in the console. +# self.args: A list of all (space-separated) arguments to the command. +# self.quantifier: If this command was mapped to the key "X" and +# the user pressed 6X, self.quantifier will be 6. +# self.arg(n): The n-th argument, or an empty string if it doesn't exist. +# self.rest(n): The n-th argument plus everything that followed. For example, +# if the command was "search foo bar a b c", rest(2) will be "bar a b c" +# self.start(n): Anything before the n-th argument. For example, if the +# command was "search foo bar a b c", start(2) will be "search foo" +# +# =================================================================== +# And this is a little reference for common ranger functions and objects: +# +# self.fm: A reference to the "fm" object which contains most information +# about ranger. +# self.fm.notify(string): Print the given string on the screen. +# self.fm.notify(string, bad=True): Print the given string in RED. +# self.fm.reload_cwd(): Reload the current working directory. +# self.fm.thisdir: The current working directory. (A File object.) +# self.fm.thisfile: The current file. (A File object too.) +# self.fm.thistab.get_selection(): A list of all selected files. +# self.fm.execute_console(string): Execute the string as a ranger command. +# self.fm.open_console(string): Open the console with the given string +# already typed in for you. +# self.fm.move(direction): Moves the cursor in the given direction, which +# can be something like down=3, up=5, right=1, left=1, to=6, ... +# +# File objects (for example self.fm.thisfile) have these useful attributes and +# methods: +# +# cf.path: The path to the file. +# cf.basename: The base name only. +# cf.load_content(): Force a loading of the directories content (which +# obviously works with directories only) +# cf.is_directory: True/False depending on whether it's a directory. +# +# For advanced commands it is unavoidable to dive a bit into the source code +# of ranger. +# =================================================================== + +from ranger.api.commands import * + +class alias(Command): + """:alias + + Copies the oldcommand as newcommand. + """ + + context = 'browser' + resolve_macros = False + + def execute(self): + if not self.arg(1) or not self.arg(2): + self.fm.notify('Syntax: alias ', bad=True) + else: + self.fm.commands.alias(self.arg(1), self.rest(2)) + +class cd(Command): + """:cd [-r] + + The cd command changes the directory. + The command 'cd -' is equivalent to typing ``. + Using the option "-r" will get you to the real path. + """ + + def execute(self): + import os.path + if self.arg(1) == '-r': + self.shift() + destination = os.path.realpath(self.rest(1)) + if os.path.isfile(destination): + self.fm.select_file(destination) + return + else: + destination = self.rest(1) + + if not destination: + destination = '~' + + if destination == '-': + self.fm.enter_bookmark('`') + else: + self.fm.cd(destination) + + def tab(self): + import os + from os.path import dirname, basename, expanduser, join + + cwd = self.fm.thisdir.path + rel_dest = self.rest(1) + + bookmarks = [v.path for v in self.fm.bookmarks.dct.values() + if rel_dest in v.path ] + + # expand the tilde into the user directory + if rel_dest.startswith('~'): + rel_dest = expanduser(rel_dest) + + # define some shortcuts + abs_dest = join(cwd, rel_dest) + abs_dirname = dirname(abs_dest) + rel_basename = basename(rel_dest) + rel_dirname = dirname(rel_dest) + + try: + # are we at the end of a directory? + if rel_dest.endswith('/') or rel_dest == '': + _, dirnames, _ = next(os.walk(abs_dest)) + + # are we in the middle of the filename? + else: + _, dirnames, _ = next(os.walk(abs_dirname)) + dirnames = [dn for dn in dirnames \ + if dn.startswith(rel_basename)] + except (OSError, StopIteration): + # os.walk found nothing + pass + else: + dirnames.sort() + if self.fm.settings.cd_bookmarks: + dirnames = bookmarks + dirnames + + # no results, return None + if len(dirnames) == 0: + return + + # one result. since it must be a directory, append a slash. + if len(dirnames) == 1: + return self.start(1) + join(rel_dirname, dirnames[0]) + '/' + + # more than one result. append no slash, so the user can + # manually type in the slash to advance into that directory + return (self.start(1) + join(rel_dirname, dirname) for dirname in dirnames) + + +class chain(Command): + """:chain ; ; ... + + Calls multiple commands at once, separated by semicolons. + """ + def execute(self): + for command in self.rest(1).split(";"): + self.fm.execute_console(command) + + +class shell(Command): + escape_macros_for_shell = True + + def execute(self): + if self.arg(1) and self.arg(1)[0] == '-': + flags = self.arg(1)[1:] + command = self.rest(2) + else: + flags = '' + command = self.rest(1) + + if not command and 'p' in flags: + command = 'cat %f' + if command: + if '%' in command: + command = self.fm.substitute_macros(command, escape=True) + self.fm.execute_command(command, flags=flags) + + def tab(self): + from ranger.ext.get_executables import get_executables + if self.arg(1) and self.arg(1)[0] == '-': + command = self.rest(2) + else: + command = self.rest(1) + start = self.line[0:len(self.line) - len(command)] + + try: + position_of_last_space = command.rindex(" ") + except ValueError: + return (start + program + ' ' for program \ + in get_executables() if program.startswith(command)) + if position_of_last_space == len(command) - 1: + selection = self.fm.thistab.get_selection() + if len(selection) == 1: + return self.line + selection[0].shell_escaped_basename + ' ' + else: + return self.line + '%s ' + else: + before_word, start_of_word = self.line.rsplit(' ', 1) + return (before_word + ' ' + file.shell_escaped_basename \ + for file in self.fm.thisdir.files \ + if file.shell_escaped_basename.startswith(start_of_word)) + +class open_with(Command): + def execute(self): + app, flags, mode = self._get_app_flags_mode(self.rest(1)) + self.fm.execute_file( + files = [f for f in self.fm.thistab.get_selection()], + app = app, + flags = flags, + mode = mode) + + def tab(self): + return self._tab_through_executables() + + def _get_app_flags_mode(self, string): + """Extracts the application, flags and mode from a string. + + examples: + "mplayer f 1" => ("mplayer", "f", 1) + "aunpack 4" => ("aunpack", "", 4) + "p" => ("", "p", 0) + "" => None + """ + + app = '' + flags = '' + mode = 0 + split = string.split() + + if len(split) == 0: + pass + + elif len(split) == 1: + part = split[0] + if self._is_app(part): + app = part + elif self._is_flags(part): + flags = part + elif self._is_mode(part): + mode = part + + elif len(split) == 2: + part0 = split[0] + part1 = split[1] + + if self._is_app(part0): + app = part0 + if self._is_flags(part1): + flags = part1 + elif self._is_mode(part1): + mode = part1 + elif self._is_flags(part0): + flags = part0 + if self._is_mode(part1): + mode = part1 + elif self._is_mode(part0): + mode = part0 + if self._is_flags(part1): + flags = part1 + + elif len(split) >= 3: + part0 = split[0] + part1 = split[1] + part2 = split[2] + + if self._is_app(part0): + app = part0 + if self._is_flags(part1): + flags = part1 + if self._is_mode(part2): + mode = part2 + elif self._is_mode(part1): + mode = part1 + if self._is_flags(part2): + flags = part2 + elif self._is_flags(part0): + flags = part0 + if self._is_mode(part1): + mode = part1 + elif self._is_mode(part0): + mode = part0 + if self._is_flags(part1): + flags = part1 + + return app, flags, int(mode) + + def _is_app(self, arg): + return not self._is_flags(arg) and not arg.isdigit() + + def _is_flags(self, arg): + from ranger.core.runner import ALLOWED_FLAGS + return all(x in ALLOWED_FLAGS for x in arg) + + def _is_mode(self, arg): + return all(x in '0123456789' for x in arg) + + +class set_(Command): + """:set