Last active
July 13, 2021 00:45
-
-
Save wchargin/f2fe17edd213d5f39180c9470bb88b08 to your computer and use it in GitHub Desktop.
xmonad.hs, 2021-07-12: now with `interrogateVolume`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import XMonad | |
import XMonad.Config.Gnome (gnomeConfig) | |
import XMonad.Hooks.ManageHelpers (doCenterFloat) | |
import XMonad.Hooks.ManageDocks (manageDocks, avoidStruts) | |
import XMonad.Layout.NoBorders (noBorders) | |
import qualified XMonad.Layout.Dwindle as Dwindle | |
import XMonad.Actions.CycleWS (nextWS, prevWS, shiftToNext, shiftToPrev) | |
import XMonad.Util.EZConfig (additionalKeys) | |
import qualified XMonad.StackSet as W | |
altMask :: KeyMask | |
altMask = mod1Mask | |
superMask :: KeyMask | |
superMask = mod4Mask | |
myModMask :: KeyMask | |
myModMask = superMask | |
main :: IO () | |
main = xmonad myConfig | |
myLayouts = Full ||| spiral | |
where | |
spiral = Dwindle.Dwindle Dwindle.R Dwindle.CW (7/6) 1.1 | |
tiled = Tall nmaster delta ratio | |
nmaster = 1 | |
ratio = 1/2 | |
delta = 3/100 | |
myConfig = gnomeConfig | |
{ borderWidth = 2 | |
, layoutHook = avoidStruts $ noBorders myLayouts | |
, manageHook = manageDocks <+> manageHook def | |
, terminal = "gnome-terminal" | |
, workspaces = [show n | n <- [1 .. 9]] | |
, modMask = myModMask | |
} `additionalKeys` | |
([ -- | |
-- Session management | |
((myModMask .|. shiftMask, xK_q), promptForLogOut) | |
-- | |
-- Launchers | |
, ((myModMask, xK_grave), launchKalk) | |
, ((myModMask, xK_w), launchWeb) | |
, ((myModMask .|. shiftMask, xK_w), launchWebIncognito) | |
, ((myModMask .|. shiftMask .|. controlMask, xK_w), launchWebSandboxed) | |
, ((altMask .|. controlMask, xK_l), lockScreen) | |
, ((myModMask, xK_s), suspend) | |
, ((0, xK_Print), takeScreenshot) | |
, ((shiftMask, xK_Print), takeQuickScreenshot) | |
-- | |
-- Workspace manipulation | |
, ((myModMask, xK_Left), prevWS) | |
, ((myModMask, xK_Right), nextWS) | |
, ((myModMask .|. shiftMask, xK_Left), shiftToPrev >> prevWS) | |
, ((myModMask .|. shiftMask, xK_Right), shiftToNext >> nextWS) | |
-- | |
-- Audio manipulation | |
, ((myModMask, xK_Up), volumeDelta 2) | |
, ((myModMask, xK_Down), volumeDelta (-2)) | |
, ((myModMask .|. shiftMask, xK_Up), volumeDelta 10) | |
, ((myModMask .|. shiftMask, xK_Down), volumeDelta (-10)) | |
, ((myModMask, xK_0), volumeMute "toggle") | |
, ((myModMask .|. shiftMask, xK_0), volumeMute "1") -- hard mute | |
, ((myModMask, xK_slash), interrogateVolume) | |
, ((myModMask, xK_x), micMute "toggle") | |
, ((myModMask, xK_z), micMute "1") -- hard mic mute | |
, ((myModMask, xK_semicolon), playerctl "play-pause") | |
, ((myModMask .|. shiftMask, xK_semicolon), playerctl "position 1-") | |
] ++ [ | |
((m .|. myModMask, key), screenWorkspace sc >>= flip whenJust (windows . f)) | |
| (key, sc) <- zip [xK_e, xK_r] [0..] | |
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)] | |
]) | |
promptForLogOut = spawn "gnome-session-quit" | |
launchKalk = spawn "gnome-terminal -- kalk" | |
launchWeb = spawn "firefox" | |
launchWebIncognito = spawn "firefox -private-window" | |
launchWebSandboxed = spawn $ "sh -c '" ++ command ++ "'" | |
where | |
command = concat | |
[ "set -eu; " | |
, "datadir=\"$(mktemp -d --suffix=_chrome)\"; " | |
, "chromium --user-data-dir=\"${datadir}\" about:blank; " | |
, "rm -r \"${datadir}\"" | |
] | |
lockScreen = spawn "slock" | |
suspend = spawn $ unwords args | |
where | |
args = | |
[ "dbus-send" | |
, "--system" | |
, "--print-reply" | |
, "--dest=org.freedesktop.login1" | |
, "/org/freedesktop/login1" | |
, "org.freedesktop.login1.Manager.Suspend" | |
, "boolean:true" | |
] | |
takeScreenshot = spawn "gnome-screenshot --interactive" | |
takeQuickScreenshot = spawn "gnome-screenshot" | |
-- Undocumented alias for default sink; see: | |
-- https://github.com/pulseaudio/pulseaudio/blob/4e3a080d7699732be9c522be9a96d851f97fbf11/src/pulsecore/namereg.c#L193 | |
pactlSink = "@DEFAULT_SINK@" | |
pactlSource = "@DEFAULT_SOURCE@" | |
volumeDelta p = spawn $ concat ["pactl set-sink-volume ", pactlSink, " ", deltaText, "%"] | |
where | |
deltaText = if p < 0 then show p else "+" ++ (show p) | |
volumeMute how = spawn $ concat ["pactl set-sink-mute ", pactlSink, " ", how] | |
micMute how = spawn $ concat ["pactl set-source-mute ", pactlSource, " ", how] | |
-- Play a short sine-wave tone to gauge the current output device and volume. | |
interrogateVolume = spawn $ concat $ | |
[ "python3 -c '" | |
, unlines | |
[ "import math, re, struct, subprocess, sys" | |
, "" | |
, "def get_volume():" | |
, " pactl = subprocess.check_output([\"pactl\", \"list\", \"sinks\"])" | |
, " blobs = pactl.split(b\"\\n\\n\")" | |
, " running = [b for b in blobs if b\"State: RUNNING\" in b]" | |
, " if len(running) != 1: return 0.5" | |
, " lines = running[0].split(b\"\\n\")" | |
, " volumes = [line for line in lines if b\"Volume:\" in line and b\"Base Volume:\" not in line]" | |
, " if len(volumes) != 1: return 0.5" | |
, " matches = [float(m) / 100 for m in re.findall(br\"([0-9]+)%\", volumes[0])]" | |
, " if not matches: return 0.9" | |
, " return sum(matches) / len(matches)" | |
, "" | |
, "DURATION_SECS = 1 / 16" | |
, "AMPLITUDE = 0.8" | |
, "FREQUENCY_HZ = 523 * 2 ** (get_volume())" | |
, "" | |
, "SAMPLE_RATE_HZ = 8000" | |
, "NUM_SAMPLES = int(SAMPLE_RATE_HZ * DURATION_SECS)" | |
, "samples = [int(0x7fff * AMPLITUDE * (math.sin(i / SAMPLE_RATE_HZ * math.tau * FREQUENCY_HZ))) for i in range(NUM_SAMPLES)]" | |
, "data = b\"\".join(struct.pack(\"<h\", x) for x in samples)" | |
, "sys.stdout.buffer.write(data)" | |
] | |
, "' | aplay -f S16_LE" | |
] | |
-- slightly hacky; installed playerctl from source into "$HOME", | |
-- and the LD_LIBRARY_PATH export in my bashrc isn't in the xmonad env | |
--playerctl what = spawn $ "env LD_LIBRARY_PATH=/home/wchargin/lib playerctl " ++ what | |
playerctl what = spawn $ "playerctl " ++ what |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment