Skip to content

Instantly share code, notes, and snippets.

@wchargin
Last active July 13, 2021 00:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wchargin/f2fe17edd213d5f39180c9470bb88b08 to your computer and use it in GitHub Desktop.
Save wchargin/f2fe17edd213d5f39180c9470bb88b08 to your computer and use it in GitHub Desktop.
xmonad.hs, 2021-07-12: now with `interrogateVolume`
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