commit 8c6254bb7e619b936656e01be3f77270805d96e0 Author: Lars Vierbergen Date: Tue Sep 20 08:29:44 2016 +0200 Initial commit diff --git a/.xmobarrc b/.xmobarrc new file mode 100644 index 0000000..4d30f7e --- /dev/null +++ b/.xmobarrc @@ -0,0 +1,17 @@ +Config { font = "-*-Fixed-Bold-R-Normal-*-13-*-*-*-*-*-*-*" + , bgColor = "black" + , fgColor = "grey" + , position = BottomW L 90 + , commands = [Run Cpu ["-L","3","-H","50","--normal","green","--high","red", "-p", "3"] 10 + , Run Memory ["-t","Mem: %"] 10 + , Run Swap [] 10 + , Run Date "%a %b %_d %H:%M" "date" 10 + , Run Battery ["-t", "", "-L", "10", "-H", "80", "-l", "red", "-h", "green", "--", "-O", "%", "-i", "%", "-o", "Bat: % / "] 10 + , Run Locks + , Run Com "python" ["/home/lars/.xmonad/xmonad-pulsevolume/show-volume.py"] "vol" 1 + , Run StdinReader + ] + , sepChar = "%" + , alignSep = "}{" + , template = "%StdinReader% }{ %vol% | %locks% | %battery% | %cpu% | %memory% * %swap% | %date%" + } diff --git a/battery-monitor.sh b/battery-monitor.sh new file mode 100644 index 0000000..068ee81 --- /dev/null +++ b/battery-monitor.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e +trap 'xmessage -nearmouse "WARNING: battery monitor died!"' EXIT +was_notified=0 +while true; do + acpi_data=$(acpi -b) + status=$(echo -n "$acpi_data" | awk -F'[,:%]' '{print $2}') + capacity=$(echo -n "$acpi_data" | awk -F'[,:%]' '{print $3}') + echo "Status: $status; Capacity: $capacity; was_notified: $was_notified" + if [[ "$status" =~ "Charging" ]]; then + was_notified=0 + fi + if [[ "$status" =~ "Discharging" && "$capacity" -lt 25 && "$was_notified" = 0 ]]; then + xmessage -nearmouse "$acpi_data"& + was_notified=1 + fi + if [[ "$status" =~ "Discharging" && "$capacity" -lt 15 ]]; then + bash -c 'echo "Battery critical, suspending"; acpi -b' | xmessage -nearmouse -file -& + sleep 5 + systemctl hibernate + fi + sleep 10 +done + diff --git a/xmonad-pulsevolume/LICENSE b/xmonad-pulsevolume/LICENSE new file mode 100644 index 0000000..e06d208 --- /dev/null +++ b/xmonad-pulsevolume/LICENSE @@ -0,0 +1,202 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/xmonad-pulsevolume/README.md b/xmonad-pulsevolume/README.md new file mode 100644 index 0000000..f951009 --- /dev/null +++ b/xmonad-pulsevolume/README.md @@ -0,0 +1,72 @@ +# xmonad-pulsevolume +Scripts for setting and visualizing pulseaudio volume with XMonad+xmobar (and maybe others). + +## Purpose + + * Easily change pulseaudio volume from xmonad + * Visualize volume in xmobar. Here are some snapshots of the representation in increasing volume: + +``` + [ (mute) ] + [ ] + [. ] + [- ] + [/ ] + [/. ] + [/- ] + [// ] + [//. ] + [///- ] + [////. ] + [//////////] +``` + +## Install & Use + +0. Dependencies: working xmonad, pulseaudio. For visualization: python, xmobar. +1. Add pulse-volume.sh and show-volume.py somewhere on your $PATH. +2. Update xmonad.hs for media buttons. I add the following keybindings: + ``` + -- mute button + , ((0 , 0x1008FF12), spawn "pulse-volume.sh toggle") + + -- volumeup button + , ((0 , 0x1008FF13), spawn "pulse-volume.sh increase") + + -- volumedown button + , ((0 , 0x1008FF11), spawn "pulse-volume.sh decrease") + ``` +3. Update xmobar configuation to run python script. The important thing is to add to the 'commands' array, and to the template. Here's a full example: + ``` + Config { font = "xft:Droid Sans Mono:size=9:antialias=true" + , bgColor = "#181818" + , fgColor = "green" + , position = TopW L 95 + , commands = [ Run Cpu ["-L","3","-H","50","--normal","green","--high","red"] 5 + , Run Memory ["-t","Mem: %"] 5 + , Run Date "%a %b %d %Y %H:%M:%S " "date" 1 + , Run Com "python" ["/path/to/show-volume.py"] "vol" 1 + , Run StdinReader + ] + , sepChar = "%" + , alignSep = "}{" + , template = "%StdinReader% }{ %cpu% | %memory% %vol% %date%" + } + ``` + +4. (Optional) Upon boot, I like to reset the volume to what I consider a reasonable volume and unmute. In my .xsession file I run `pulse-volume.sh reset`. This also ensures that ~/.mute and ~/.volume are correct (see below). +5. Restart xmonad. + +## Under the hood and future work + +The pulse-volume.sh script maintains two files in your home directory: ~/.volume and ~/.mute. Whenever you run this script it reads the current volume from the file and updates it appropriately (it takes a parameter "increase", "decrease", "mute", "unmute", "toggle"). It also updates pulse-volume. If this file is changed between uses of the script, the volume can jump dramatically; hence the "reset" feature to go back to a fixed volume. Obviously, it would be better to get the current volume from pulse audio itself instead of replicating state... contributions welcome! + +./show-volume.py generates the graphical representation from these files. It takes no parameters -- run it from the command line to see what it does. It's very easy to hack on this to make a new visualization... let me know if you do something cool! + +I expect you can use these scripts with other window managers or status bars, like dzen2. I somehow doubt there's much need -- but if you find it useful, feel free to contribute additions. + +## Bugs + +I wrote this years ago and never really intended to release it. Yet, it's stood the test of time for me, and I automatically deploy it on several linux systems without issues. However, given that nobody **else** has ever tested it, I imagine it has a lingering issue or two -- even if only in the setup instructions that I wrote. + +Use the issue tracker. For security bugs, email berkeley@berkeleychurchill.com. You can get a gpg key off my webpage at https://www.berkeleychurchill.com if you like. diff --git a/xmonad-pulsevolume/pulse-volume.sh b/xmonad-pulsevolume/pulse-volume.sh new file mode 100755 index 0000000..33e1598 --- /dev/null +++ b/xmonad-pulsevolume/pulse-volume.sh @@ -0,0 +1,59 @@ +#!/bin/bash +ACTION=$1 +declare -i CURVOL=`cat ~/.volume` #Reads in the current volume + +function mute() { + for i in 0 1 2 3 4 5 6; do + pactl set-sink-mute $i $1 + done + echo $1 > ~/.mute +} + +function setvolume() { + for i in 0 1 2 3 4 5 6; do + pactl set-sink-volume $i $1 + done + echo $1 > ~/.volume # Write the new volume to disk to be read the next time the script is run. +} + +if [[ $ACTION == "reset" ]]; then + CURVOL=30000 + echo $CURVOL > ~/.volume + echo 0 > ~/.mute + for i in 0 1 2 3 4 5 6; do + pactl set-sink-mute $i 0 + pactl set-sink-volume $i $CURVOL + done +fi + +if [[ $ACTION == "increase" ]]; then + mute 0 + CURVOL=$(($CURVOL + 3000)) +fi +if [[ $ACTION == "decrease" ]]; then + mute 0 + CURVOL=$(($CURVOL - 3000)) +fi + +if [[ $CURVOL -le 90000 && $CURVOL -ge 0 ]]; then + for i in 0 1 2 3 4 5 6; do + pactl set-sink-volume $i $CURVOL + done + echo $CURVOL > ~/.volume # Write the new volume to disk to be read the next time the script is run. +fi + +if [[ $ACTION == "toggle" ]]; then + if [[ `cat ~/.mute` -eq 1 ]]; then + ACTION=unmute + else + ACTION=mute + fi +fi + +if [[ $ACTION == "mute" ]]; then + mute 1 +fi + +if [[ $ACTION == "unmute" ]]; then + mute 0 +fi diff --git a/xmonad-pulsevolume/show-volume.py b/xmonad-pulsevolume/show-volume.py new file mode 100755 index 0000000..bb63bda --- /dev/null +++ b/xmonad-pulsevolume/show-volume.py @@ -0,0 +1,36 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- + +import sys +import time +import os + +allbars = "[//////////]" +emptybars = "[ ]" + +home = os.getenv("HOME") + +with open(home + "/.volume") as f: + content = f.readlines() + +with open(home + "/.mute") as f: + isunmuted = (int(f.readlines()[0]) == 0) + +if isunmuted: + volume = int(content[0]) + bars = int(volume / 9000) + + if (volume % 9000 == 0): + output = allbars[0:bars+1] + emptybars[bars+1:] + if (volume % 9000 == 3000): + output = allbars[0:bars+1] + "." + emptybars[bars+2:] + if (volume % 9000 == 6000): + output = allbars[0:bars+1] + "-" + emptybars[bars+2:] +else: + output = "[ (mute) ]" + + +print(output) +sys.stdout.flush + + diff --git a/xmonad-session-rc b/xmonad-session-rc new file mode 100644 index 0000000..01b0b1f --- /dev/null +++ b/xmonad-session-rc @@ -0,0 +1,24 @@ +#!/bin/bash +xrdb -merge .Xresources + +# Touchpad +$(syndaemon -d -K -i 0.5s && synclient HorizTwoFingerScroll=1 && synclient TapButton1=1 && synclient TapButton2=3)& + +# Tray applets +trayer --edge bottom --align right --SetDockType true --SetPartialStrut true --expand true --width 10 --transparent true --tint 0x000000 --alpha 0 --height 17 & +xscreensaver -no-splash& +nm-applet --sm-disable& +gnome-power-manager& + +# Fix for java applications +export _JAVA_AWT_WM_NONREPARENTING=1 + +# Auto-start applications +eval $(ssh-agent) +gajim& +dropbox start& +keepass2 ~/Documents/passwords.kdbx& +redshift -l 51:5 -t 6500:3000 & +icedove& +mate-terminal -e ssh-add ~/.ssh/id_rsa& +bash ~/.xmonad/battery-monitor.sh& diff --git a/xmonad.hs b/xmonad.hs new file mode 100644 index 0000000..7e9f8df --- /dev/null +++ b/xmonad.hs @@ -0,0 +1,114 @@ +import XMonad +import XMonad.Actions.CycleWS +import XMonad.Hooks.DynamicLog +import XMonad.Hooks.ManageDocks +import XMonad.Hooks.ManageHelpers +import XMonad.Util.Run(spawnPipe) +import XMonad.Util.EZConfig +import XMonad.Util.Scratchpad +import XMonad.Config.Desktop +import XMonad.Config.Azerty +import XMonad.Layout.Fullscreen +import XMonad.Layout.NoBorders +import XMonad.Layout.Maximize +import XMonad.Layout.Tabbed +import XMonad.Layout.Minimize +import XMonad.Layout.BoringWindows +import XMonad.Layout.PerWorkspace +import XMonad.Prompt +import XMonad.Prompt.Ssh +import System.IO +import qualified Data.Map as M +import qualified XMonad.StackSet as W +import qualified Data.List as L + +baseConfig = desktopConfig + { modMask = mod5Mask -- AltGr + , terminal = "mate-terminal.wrapper" + , workspaces = ["1:web", "2:com", "3:dev"] ++ map show [4..8] ++ ["9:div"] + , layoutHook = smartBorders $ fullscreenFull $ avoidStruts $ boringWindows $ minimize $ maximize $ onWorkspaces ["2:com", "9:div"] simpleTabbed $ (layoutHook desktopConfig ||| simpleTabbed ) + , handleEventHook = handleEventHook desktopConfig <+> fullscreenEventHook + } + `removeKeys` + [(m .|. modMask baseConfig , k) + | (k) <- [xK_1 .. xK_9] + , (m) <- [(0), (shiftMask)]] +-- `removeKeys` +-- [(modMask baseConfig .|. shiftMask, xK_q)] + + +baseXPConfig = defaultXPConfig { + position = Top +} + +workspaceManageHook :: ManageHook +workspaceManageHook = composeAll [ + className =? "Firefox" --> doShift "1:web" + ,className =? "Icedove" --> doShift "2:com" + ,className =? "Gajim" --> doShift "2:com" + ,className =? "jetbrains-phpstorm" --> doShift "3:dev" + ,className =? "jetbrains-pycharm" --> doShift "3:dev" + ,className =? "KeePass2" --> doShift "9:div" + ,className =? "Xmessage" --> doFloat + ] + + +main = do + xmobar_proc <- spawnPipe "xmobar" + xmonad $ baseConfig + { manageHook = (scratchpadManageHook $ W.RationalRect 0.0 0.0 1.0 0.5) <+> workspaceManageHook <+> fullscreenManageHook <+> manageDocks <+> manageHook baseConfig + , logHook = dynamicLogWithPP xmobarPP + { ppOutput = hPutStrLn xmobar_proc + , ppTitle = xmobarColor "green" "" . shorten 50 + , ppSort = fmap(.scratchpadFilterOutWorkspace) (ppSort xmobarPP) + , ppOrder = \(ws:_:t:_) -> [ws, t] + } + } + `additionalKeys` + [ ((modMask baseConfig .|. controlMask, xK_l), spawn "xscreensaver-command -lock") + , ((modMask baseConfig .|. controlMask, xK_s), spawn "systemctl hibernate") + , ((modMask baseConfig, xK_e), spawn "caja") + , ((0, xK_Print), spawn "scrot") + -- Normal Alt-Tab behavior + , ((mod1Mask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window + , ((mod1Mask .|. shiftMask, xK_Tab ), windows W.focusUp ) -- %! Move focus to the previous window + -- XMonad.Actions.CycleWS + , ((modMask baseConfig, xK_Right), nextWS) + , ((modMask baseConfig, xK_Left), prevWS) + , ((modMask baseConfig .|. shiftMask, xK_Right), shiftToNext >> nextWS) + , ((modMask baseConfig .|. shiftMask, xK_Left), shiftToPrev >> prevWS) + -- XMonad.Layout.Maximize + , ((modMask baseConfig, xK_F11), withFocused (sendMessage . maximizeRestore)) + -- XMonad.Prompt.Ssh + , ((modMask baseConfig, xK_s), sshPrompt baseXPConfig) + -- XMonad.Layout.Minimize + , ((modMask baseConfig, xK_c), withFocused minimizeWindow) + , ((modMask baseConfig, xK_g), sendMessage RestoreNextMinimizedWin) + -- XMonad.Layout.BoringWindows + , ((modMask baseConfig, xK_j), focusUp) + , ((modMask baseConfig, xK_k), focusDown) + , ((modMask baseConfig, xK_m), focusMaster) + -- XMonad.Util.Scratchpad + , ((modMask baseConfig, xK_F12), scratchpadSpawnActionCustom "mate-terminal --disable-factory --name scratchpad") + -- Azerty support + , ((modMask baseConfig, xK_semicolon), sendMessage (IncMasterN (-1))) + -- mute button + , ((0, 0x1008FF12), spawn "~/.xmonad/xmonad-pulsevolume/pulse-volume.sh toggle") + -- volumeup button + , ((0, 0x1008FF13), spawn "~/.xmonad/xmonad-pulsevolume/pulse-volume.sh increase") + -- volumedown button + , ((0, 0x1008FF11), spawn "~/.xmonad/xmonad-pulsevolume/pulse-volume.sh decrease") + ] + `additionalKeys` + -- + -- mod-ctrl-[1..9], Switch to workspace N + -- + -- mod-ctrl-[1..9], Switch to workspace N + -- mod-ctrl-shift-[1..9], Move client to workspace N + -- + [((m .|. modMask baseConfig , k), windows $ f i) + | (i, k) <- zip (workspaces baseConfig) [xK_F1 .. xK_F9] + , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]] + + +