summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif')
-rw-r--r--accounts/gkleen@sif/default.nix15
-rw-r--r--accounts/gkleen@sif/dunst-settings.nix45
-rw-r--r--accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf4
-rw-r--r--accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf4
-rw-r--r--accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf4
-rw-r--r--accounts/gkleen@sif/dunstrc.d/10-brightness.conf5
-rw-r--r--accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf5
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-element.conf3
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-kitty.conf3
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-mail.conf3
-rw-r--r--accounts/gkleen@sif/dunstrc.d/20-zulip.conf3
-rw-r--r--accounts/gkleen@sif/niri/default.nix29
-rw-r--r--accounts/gkleen@sif/niri/mako.nix28
-rw-r--r--accounts/gkleen@sif/niri/waybar.nix58
-rw-r--r--accounts/gkleen@sif/systemd.nix9
-rw-r--r--accounts/gkleen@sif/taffybar/default.nix2
-rw-r--r--accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal32
-rw-r--r--accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs111
-rw-r--r--accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs101
-rw-r--r--accounts/gkleen@sif/taffybar/src/taffybar.hs89
-rw-r--r--accounts/gkleen@sif/taffybar/taffybar.css146
-rw-r--r--accounts/gkleen@sif/xmonad/.gitignore4
-rw-r--r--accounts/gkleen@sif/xmonad/default.nix7
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs127
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs94
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs105
-rw-r--r--accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs246
-rw-r--r--accounts/gkleen@sif/xmonad/package.yaml31
-rw-r--r--accounts/gkleen@sif/xmonad/stack.nix17
-rw-r--r--accounts/gkleen@sif/xmonad/stack.yaml10
-rw-r--r--accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix21
-rw-r--r--accounts/gkleen@sif/xmonad/xmonad.hs939
32 files changed, 99 insertions, 2201 deletions
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
index bcfd1224..7d5a9c25 100644
--- a/accounts/gkleen@sif/default.nix
+++ b/accounts/gkleen@sif/default.nix
@@ -285,14 +285,6 @@ in {
285 }; 285 };
286 286
287 services = { 287 services = {
288 dunst = {
289 settings = import ./dunst-settings.nix inputs;
290 iconTheme = {
291 package = pkgs.paper-icon-theme;
292 name = "Paper";
293 };
294 enable = true;
295 };
296 emacs = { 288 emacs = {
297 enable = true; 289 enable = true;
298 socketActivation.enable = true; 290 socketActivation.enable = true;
@@ -468,13 +460,6 @@ in {
468 }; 460 };
469 461
470 xdg.configFile = { 462 xdg.configFile = {
471 "dunst/dunstrc.d" = {
472 source = ./dunstrc.d;
473 recursive = true;
474 onChange = ''
475 ${pkgs.systemd}/bin/systemctl --user try-restart dunst
476 '';
477 };
478 "wireplumber" = { 463 "wireplumber" = {
479 source = ./wireplumber; 464 source = ./wireplumber;
480 recursive = true; 465 recursive = true;
diff --git a/accounts/gkleen@sif/dunst-settings.nix b/accounts/gkleen@sif/dunst-settings.nix
deleted file mode 100644
index 72687aea..00000000
--- a/accounts/gkleen@sif/dunst-settings.nix
+++ /dev/null
@@ -1,45 +0,0 @@
1{ pkgs, ... }:
2{
3 global = {
4 font = "Fira Sans 12";
5 markup = "full";
6 format = "<i>%s</i> %p\\n%b";
7 alignment = "left";
8 # geometry = "1216x10-32+64";
9 width = 500;
10 height = 100;
11 offset = "4x4";
12 origin = "top-right";
13 shrink = true;
14 monitor = 0;
15 follow = "none";
16 padding = 6;
17 horizontal_padding = 6;
18 separator_height = 1;
19 separator_color = "frame";
20 idle_threshold = 0;
21
22 transparency = 10;
23
24 frame_width = 1;
25 frame_color = "#999999";
26
27 word_wrap = true;
28 show_age_threshold = 15;
29 show_indicators = false;
30 icon_position = "right";
31 min_icon_size = 25;
32 max_icon_size = 25;
33 sort = false;
34 sticky_history = false;
35
36 dmenu = "fuzzel --dmenu";
37 browser = "${pkgs.xdg-utils}/bin/xdg-open";
38 };
39 # shortcuts = {
40 # close = "ctrl+space";
41 # close_all = "ctrl+shift+space";
42 # history = "ctrl+comma";
43 # context = "ctrl+period";
44 # };
45}
diff --git a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf b/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf
deleted file mode 100644
index 98c94b64..00000000
--- a/accounts/gkleen@sif/dunstrc.d/00-urgency_low.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1[urgency_low]
2background="#000000aa"
3foreground="#999999"
4timeout=5 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf b/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf
deleted file mode 100644
index f8fa8e2d..00000000
--- a/accounts/gkleen@sif/dunstrc.d/01-urgency_normal.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1[urgency_normal]
2background="#000000aa"
3foreground="#ffffff"
4timeout=15 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf b/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf
deleted file mode 100644
index a08bf4b1..00000000
--- a/accounts/gkleen@sif/dunstrc.d/02-urgency_critical.conf
+++ /dev/null
@@ -1,4 +0,0 @@
1[urgency_critical]
2background="#900000aa"
3foreground="#ffffff"
4timeout=0
diff --git a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf b/accounts/gkleen@sif/dunstrc.d/10-brightness.conf
deleted file mode 100644
index c54595ab..00000000
--- a/accounts/gkleen@sif/dunstrc.d/10-brightness.conf
+++ /dev/null
@@ -1,5 +0,0 @@
1[brightness]
2appname="brightness"
3set_stack_tag="brightness"
4set_transient=yes
5history_ignore=yes
diff --git a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf b/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf
deleted file mode 100644
index 074f4535..00000000
--- a/accounts/gkleen@sif/dunstrc.d/10-pulseaudio-ctl.conf
+++ /dev/null
@@ -1,5 +0,0 @@
1[pulseaudio-ctl]
2body="Current is *"
3history_ignore=yes
4set_stack_tag="volume"
5summary="Volume *"
diff --git a/accounts/gkleen@sif/dunstrc.d/20-element.conf b/accounts/gkleen@sif/dunstrc.d/20-element.conf
deleted file mode 100644
index 5ff6031e..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-element.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[element-im]
2appname=Element
3timeout=0 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf b/accounts/gkleen@sif/dunstrc.d/20-kitty.conf
deleted file mode 100644
index b27ee27e..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-kitty.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[kitty]
2appname=kitty
3urgency=low
diff --git a/accounts/gkleen@sif/dunstrc.d/20-mail.conf b/accounts/gkleen@sif/dunstrc.d/20-mail.conf
deleted file mode 100644
index cb568e01..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-mail.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[element]
2appname="notmuch"
3timeout=0 \ No newline at end of file
diff --git a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf b/accounts/gkleen@sif/dunstrc.d/20-zulip.conf
deleted file mode 100644
index d7fbd32c..00000000
--- a/accounts/gkleen@sif/dunstrc.d/20-zulip.conf
+++ /dev/null
@@ -1,3 +0,0 @@
1[zulip]
2appname="Zulip"
3timeout=0 \ No newline at end of file
diff --git a/accounts/gkleen@sif/niri/default.nix b/accounts/gkleen@sif/niri/default.nix
index 841c972a..cc6c85c3 100644
--- a/accounts/gkleen@sif/niri/default.nix
+++ b/accounts/gkleen@sif/niri/default.nix
@@ -4,7 +4,7 @@ let
4 terminal = lib.getExe config.programs.kitty.package; 4 terminal = lib.getExe config.programs.kitty.package;
5 lightctl = lib.getExe' config.services.avizo.package "lightctl"; 5 lightctl = lib.getExe' config.services.avizo.package "lightctl";
6 volumectl = lib.getExe' config.services.avizo.package "volumectl"; 6 volumectl = lib.getExe' config.services.avizo.package "volumectl";
7 dunstctl = lib.getExe' config.services.dunst.package "dunstctl"; 7 makoctl = lib.getExe' config.services.mako.package "makoctl";
8 loginctl = lib.getExe' hostConfig.systemd.package "loginctl"; 8 loginctl = lib.getExe' hostConfig.systemd.package "loginctl";
9 systemctl = lib.getExe' hostConfig.systemd.package "systemctl"; 9 systemctl = lib.getExe' hostConfig.systemd.package "systemctl";
10 10
@@ -121,6 +121,7 @@ let
121in { 121in {
122 imports = [ 122 imports = [
123 ./waybar.nix 123 ./waybar.nix
124 ./mako.nix
124 ]; 125 ];
125 126
126 config = { 127 config = {
@@ -244,13 +245,13 @@ in {
244 }; 245 };
245 246
246 window-rules = [ 247 window-rules = [
247 { 248 # {
248 geometry-corner-radius = 249 # geometry-corner-radius =
249 let 250 # let
250 allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; }; 251 # allCorners = r: { bottom-left = r; bottom-right = r; top-left = r; top-right = r; };
251 in allCorners 4.; 252 # in allCorners 4.;
252 clip-to-geometry = true; 253 # clip-to-geometry = true;
253 } 254 # }
254 { 255 {
255 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ]; 256 matches = [ { app-id = "^com\.saivert\.pwvucontrol$"; } ];
256 open-on-workspace = "pwctl"; 257 open-on-workspace = "pwctl";
@@ -264,7 +265,7 @@ in {
264 excludes = [ 265 excludes = [
265 { title = "^Unlock Database.*"; } 266 { title = "^Unlock Database.*"; }
266 { title = "^Access Request.*"; } 267 { title = "^Access Request.*"; }
267 { title = "^Passkey credentials.*"; } 268 { title = ".*Passkey credentials$"; }
268 ]; 269 ];
269 open-on-workspace = "kpxc"; 270 open-on-workspace = "kpxc";
270 open-focused = false; 271 open-focused = false;
@@ -273,7 +274,7 @@ in {
273 matches = [ 274 matches = [
274 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Unlock Database.*"; } 275 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Unlock Database.*"; }
275 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Access Request.*"; } 276 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Access Request.*"; }
276 { app-id = "^org\.keepassxc\.KeePassXC$"; title = "^Passkey credentials.*"; } 277 { app-id = "^org\.keepassxc\.KeePassXC$"; title = ".*Passkey credentials$"; }
277 ]; 278 ];
278 open-focused = true; 279 open-focused = true;
279 } 280 }
@@ -519,10 +520,10 @@ in {
519 allow-when-locked = true; 520 allow-when-locked = true;
520 }; 521 };
521 522
522 "Mod+Semicolon".action = spawn dunstctl "close"; 523 "Mod+Semicolon".action = spawn makoctl "dismiss";
523 "Mod+Shift+Semicolon".action = spawn dunstctl "close-all"; 524 "Mod+Shift+Semicolon".action = spawn makoctl "dismiss" "--all";
524 "Mod+Period".action = spawn dunstctl "context"; 525 "Mod+Period".action = spawn makoctl (lib.getExe config.programs.fuzzel.package) "--dmenu";
525 "Mod+Comma".action = spawn dunstctl "history-pop"; 526 "Mod+Comma".action = spawn makoctl "restore";
526 527
527 "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol"; 528 "Mod+Control+A".action = focus-or-spawn-action-app_id "com.saivert.pwvucontrol" "pwctl" "pwvucontrol";
528 "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc"; 529 "Mod+Control+P".action = focus-or-spawn-action-app_id "org.keepassxc.KeePassXC" "kpxc" "keepassxc";
diff --git a/accounts/gkleen@sif/niri/mako.nix b/accounts/gkleen@sif/niri/mako.nix
new file mode 100644
index 00000000..7e31f7e1
--- /dev/null
+++ b/accounts/gkleen@sif/niri/mako.nix
@@ -0,0 +1,28 @@
1{ config, lib, ... }:
2{
3 config = {
4 services.mako = {
5 enable = true;
6 font = "Fira Sans 10";
7 format = "<i>%s</i>\\n%b";
8 margin = "2";
9 maxVisible = -1;
10 backgroundColor = "#000000dd";
11 progressColor = "source #223544ff";
12 width = 384;
13 extraConfig = ''
14 outer-margin=1
15 max-history=100
16
17 [urgency=low]
18 text-color=#999999ff
19
20 [urgency=critical]
21 background-color=#900000dd
22
23 [mode=silent]
24 invisible=1
25 '';
26 };
27 };
28}
diff --git a/accounts/gkleen@sif/niri/waybar.nix b/accounts/gkleen@sif/niri/waybar.nix
index ff48ba83..26e76a1d 100644
--- a/accounts/gkleen@sif/niri/waybar.nix
+++ b/accounts/gkleen@sif/niri/waybar.nix
@@ -25,8 +25,58 @@
25 modules-right = [ # "custom/worktime" "custom/worktime-today" 25 modules-right = [ # "custom/worktime" "custom/worktime-today"
26 "custom/weather" 26 "custom/weather"
27 "custom/keymap" 27 "custom/keymap"
28 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "clock" ]; 28 "privacy" "tray" "wireplumber" "backlight" "battery" "idle_inhibitor" "custom/mako" "clock" ];
29 29
30 "custom/mako" = {
31 format = "{}";
32 return-type = "json";
33 exec = pkgs.writers.writePython3 "mako-silent" { libraries = [ pkgs.python3Packages.dbus-next ]; } ''
34 from dbus_next.aio import MessageBus
35
36 import asyncio
37
38 import json
39
40
41 loop = asyncio.new_event_loop()
42 asyncio.set_event_loop(loop)
43
44
45 async def main():
46 bus = await MessageBus().connect()
47 # the introspection xml would normally be included in your project, but
48 # this is convenient for development
49 introspection = await bus.introspect('org.freedesktop.Notifications', '/fr/emersion/Mako') # noqa: E501
50
51 obj = bus.get_proxy_object('org.freedesktop.Notifications', '/fr/emersion/Mako', introspection) # noqa: E501
52 mako = obj.get_interface('fr.emersion.Mako')
53 properties = obj.get_interface('org.freedesktop.DBus.Properties')
54
55 async def print_mode():
56 modes = await mako.get_modes()
57 is_silent = "silent" in modes
58 icon = "&#xf009b;" if is_silent else "&#xf009a;"
59 text = f"<span font=\"Symbols Nerd Font Mono\" size=\"90%\">{icon}</span>" # noqa: E501
60 if is_silent:
61 text = f"<span color=\"#ffffff\">{text}</span>"
62 print(json.dumps({'text': text}, separators=(',', ':')), flush=True) # noqa: E501
63
64 async def on_properties_changed(interface_name, changed_properties, invalidated_properties): # noqa: E501
65 if "Modes" not in invalidated_properties:
66 return
67
68 await print_mode()
69
70 properties.on_properties_changed(on_properties_changed)
71 await print_mode()
72
73 await loop.create_future()
74
75
76 loop.run_until_complete(main())
77 '';
78 on-click = "makoctl mode -t silent";
79 };
30 "custom/weather" = { 80 "custom/weather" = {
31 format = "{}"; 81 format = "{}";
32 tooltip = true; 82 tooltip = true;
@@ -240,11 +290,15 @@
240 #tray { 290 #tray {
241 margin: 0; 291 margin: 0;
242 } 292 }
243 #battery, #idle_inhibitor, #backlight, #wireplumber { 293 #battery, #idle_inhibitor, #backlight, #wireplumber, #custom-mako {
244 color: @grey; 294 color: @grey;
245 margin: 0 5px 0 2px; 295 margin: 0 5px 0 2px;
246 } 296 }
247 #idle_inhibitor { 297 #idle_inhibitor {
298 margin-right: 4px;
299 margin-left: 6px;
300 }
301 #custom-mako {
248 margin-right: 2px; 302 margin-right: 2px;
249 margin-left: 3px; 303 margin-left: 3px;
250 } 304 }
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
index 119d8cc3..cefcf4ea 100644
--- a/accounts/gkleen@sif/systemd.nix
+++ b/accounts/gkleen@sif/systemd.nix
@@ -126,15 +126,6 @@ in {
126 After = ["graphical-session-pre.target"]; 126 After = ["graphical-session-pre.target"];
127 }; 127 };
128 }; 128 };
129 dunst = {
130 Service = {
131 ExecStart = lib.mkForce "${cfg.services.dunst.package}/bin/dunst";
132 Restart = "always";
133 };
134 Install = {
135 WantedBy = ["graphical-session.target"];
136 };
137 };
138 keepassxc = { 129 keepassxc = {
139 Service = { 130 Service = {
140 Type = "dbus"; 131 Type = "dbus";
diff --git a/accounts/gkleen@sif/taffybar/default.nix b/accounts/gkleen@sif/taffybar/default.nix
deleted file mode 100644
index 98366d8f..00000000
--- a/accounts/gkleen@sif/taffybar/default.nix
+++ /dev/null
@@ -1,2 +0,0 @@
1{ haskellPackages ? (import <nixpkgs> {}).haskellPackages }:
2haskellPackages.callCabal2nix "gkleen-sif-taffybar" ./. {}
diff --git a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal b/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal
deleted file mode 100644
index e32cb473..00000000
--- a/accounts/gkleen@sif/taffybar/gkleen-sif-taffybar.cabal
+++ /dev/null
@@ -1,32 +0,0 @@
1name: gkleen-sif-taffybar
2version: 0.0.0
3build-type: Simple
4cabal-version: >=1.10
5
6data-files: taffybar.css
7
8executable taffybar
9 hs-source-dirs: src
10 main-is: taffybar.hs
11 ghc-options: -threaded -rtsopts -with-rtsopts=-N -O2 -Wall
12 build-depends: base
13 , containers
14 , directory
15 , filepath
16 , gtk3
17 , taffybar
18 , X11>=1.8
19 , transformers
20 , gi-gtk
21 , time, time-locale-compat
22 , text
23 , HStringTemplate
24 , gtk-sni-tray
25 , hslogger
26 other-modules: Paths_gkleen_sif_taffybar
27 , System.Taffybar.Widget.Clock
28 , System.Taffybar.Widget.TooltipBattery
29 default-language: Haskell2010
30 default-extensions: ScopedTypeVariables
31 , LambdaCase
32 , NamedFieldPuns \ No newline at end of file
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs
deleted file mode 100644
index e8dc480f..00000000
--- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/Clock.hs
+++ /dev/null
@@ -1,111 +0,0 @@
1{-# LANGUAGE OverloadedStrings #-}
2module System.Taffybar.Widget.Clock
3 ( textClockNew
4 , textClockNewWith
5 , defaultClockConfig
6 , ClockConfig(..)
7 , ClockUpdateStrategy(..)
8 ) where
9
10import Control.Monad.IO.Class
11import Data.Maybe
12import qualified Data.Text as T
13import qualified Data.Time.Clock as Clock
14import Data.Time.Format
15import Data.Time.LocalTime
16import qualified Data.Time.Locale.Compat as L
17import GI.Gtk
18import System.Taffybar.Widget.Generic.PollingLabel
19
20type ClockFormat = L.TimeLocale -> ZonedTime -> T.Text
21
22-- | Create the widget. I recommend passing @Nothing@ for the TimeLocale
23-- parameter. The format string can include Pango markup
24-- (<http://developer.gnome.org/pango/stable/PangoMarkupFormat.html>).
25textClockNew ::
26 MonadIO m => Maybe L.TimeLocale -> ClockFormat -> Double -> m GI.Gtk.Widget
27textClockNew userLocale format interval =
28 textClockNewWith cfg
29 where
30 cfg = defaultClockConfig { clockTimeLocale = userLocale
31 , clockFormat = format
32 , clockUpdateStrategy = ConstantInterval interval
33 }
34
35data ClockUpdateStrategy
36 = ConstantInterval Double
37 | RoundedTargetInterval Int Double
38 deriving (Eq, Ord, Show)
39
40data ClockConfig = ClockConfig
41 { clockTimeZone :: Maybe TimeZone
42 , clockTimeLocale :: Maybe L.TimeLocale
43 , clockFormat :: ClockFormat
44 , clockUpdateStrategy :: ClockUpdateStrategy
45 }
46
47-- | A clock configuration that defaults to the current locale
48defaultClockConfig :: ClockConfig
49defaultClockConfig = ClockConfig
50 { clockTimeZone = Nothing
51 , clockTimeLocale = Nothing
52 , clockFormat = \locale zonedTime -> T.pack $ formatTime locale "%a %b %_d %r" zonedTime
53 , clockUpdateStrategy = RoundedTargetInterval 5 0.0
54 }
55
56systemGetTZ :: IO TimeZone
57systemGetTZ = getCurrentTimeZone
58
59-- | A configurable text-based clock widget. It currently allows for
60-- a configurable time zone through the 'ClockConfig'.
61--
62-- See also 'textClockNew'.
63textClockNewWith :: MonadIO m => ClockConfig -> m Widget
64textClockNewWith ClockConfig
65 { clockTimeZone = userZone
66 , clockTimeLocale = userLocale
67 , clockFormat = format
68 , clockUpdateStrategy = updateStrategy
69 } = liftIO $ do
70 let getTZ = maybe systemGetTZ return userZone
71 locale = fromMaybe L.defaultTimeLocale userLocale
72
73 let getUserZonedTime =
74 utcToZonedTime <$> getTZ <*> Clock.getCurrentTime
75
76 doTimeFormat = format locale
77
78 getRoundedTimeAndNextTarget = do
79 zonedTime <- getUserZonedTime
80 return $ case updateStrategy of
81 ConstantInterval interval ->
82 (doTimeFormat zonedTime, Nothing, interval)
83 RoundedTargetInterval roundSeconds offset ->
84 let roundSecondsDiffTime = fromIntegral roundSeconds
85 addTheRound = addLocalTime roundSecondsDiffTime
86 localTime = zonedTimeToLocalTime zonedTime
87 ourLocalTimeOfDay = localTimeOfDay localTime
88 seconds = round $ todSec ourLocalTimeOfDay
89 secondsFactor = seconds `div` roundSeconds
90 displaySeconds = secondsFactor * roundSeconds
91 baseLocalTimeOfDay =
92 ourLocalTimeOfDay { todSec = fromIntegral displaySeconds }
93 ourLocalTime =
94 localTime { localTimeOfDay = baseLocalTimeOfDay }
95 roundedLocalTime =
96 if seconds `mod` roundSeconds > roundSeconds `div` 2
97 then addTheRound ourLocalTime
98 else ourLocalTime
99 roundedZonedTime =
100 zonedTime { zonedTimeToLocalTime = roundedLocalTime }
101 nextTarget = addTheRound ourLocalTime
102 amountToWait = realToFrac $ diffLocalTime nextTarget localTime
103 in (doTimeFormat roundedZonedTime, Nothing, amountToWait - offset)
104
105 label <- pollingLabelWithVariableDelay getRoundedTimeAndNextTarget
106 ebox <- eventBoxNew
107 containerAdd ebox label
108 eventBoxSetVisibleWindow ebox False
109 widgetShowAll ebox
110 toWidget ebox
111
diff --git a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs b/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs
deleted file mode 100644
index 9dc52774..00000000
--- a/accounts/gkleen@sif/taffybar/src/System/Taffybar/Widget/TooltipBattery.hs
+++ /dev/null
@@ -1,101 +0,0 @@
1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE ScopedTypeVariables #-}
3module System.Taffybar.Widget.TooltipBattery ( batteryIconTooltipNew ) where
4
5import Control.Applicative
6import Control.Monad
7import Control.Monad.IO.Class
8import Control.Monad.Trans.Reader
9import Data.Int (Int64)
10import qualified Data.Text as T
11import GI.Gtk
12import Prelude
13import StatusNotifier.Tray (scalePixbufToSize)
14import System.Taffybar.Context
15import System.Taffybar.Information.Battery
16import System.Taffybar.Util
17import System.Taffybar.Widget.Generic.AutoSizeImage
18import System.Taffybar.Widget.Generic.ChannelWidget
19import Text.Printf
20import Text.StringTemplate
21import Data.Function ((&))
22
23-- | Just the battery info that will be used for display (this makes combining
24-- several easier).
25data BatteryWidgetInfo = BWI
26 { seconds :: Maybe Int64
27 , percent :: Double
28 , status :: String
29 , rate :: Maybe Double
30 } deriving (Eq, Show)
31
32-- | Format a duration expressed as seconds to hours and minutes
33formatDuration :: Int64 -> String
34formatDuration secs = let minutes, hours, minutes' :: Int64
35 minutes = secs `div` 60
36 (hours, minutes') = minutes `divMod` 60
37 in printf "%02d:%02d" hours minutes'
38
39getBatteryWidgetInfo :: BatteryInfo -> BatteryWidgetInfo
40getBatteryWidgetInfo info =
41 let battPctNum :: Double
42 battPctNum = batteryPercentage info
43 battTime :: Maybe Int64
44 battTime =
45 case batteryState info of
46 BatteryStateCharging -> Just $ batteryTimeToFull info
47 BatteryStateDischarging -> Just $ batteryTimeToEmpty info
48 _ -> Nothing
49 battStatus :: String
50 battStatus =
51 case batteryState info of
52 BatteryStateCharging -> "↑"
53 BatteryStateDischarging -> "↓"
54 BatteryStateEmpty -> "⤓"
55 BatteryStateFullyCharged -> "⤒"
56 _ -> "?"
57 battRate :: Maybe Double
58 battRate | rawRate < 0.1 = Nothing
59 | otherwise = Just rawRate
60 where rawRate = batteryEnergyRate info
61 in BWI{ seconds = battTime, percent = battPctNum, status = battStatus, rate = battRate }
62
63-- | Given (maybe summarized) battery info and format: provides the string to display
64formatBattInfo :: BatteryWidgetInfo -> String -> T.Text
65formatBattInfo info fmt =
66 let tpl = newSTMP fmt
67 tpl' = tpl
68 & setManyAttrib [ ("percentage", printf "%.0f" $ percent info)
69 , ("status", status info)
70 ]
71 & setManyAttrib [ ("time", formatDuration <$> seconds info)
72 , ("rate", printf "%.0f" <$> rate info)
73 ]
74 in render tpl'
75
76themeLoadFlags :: [IconLookupFlags]
77themeLoadFlags = [IconLookupFlagsGenericFallback, IconLookupFlagsUseBuiltin]
78
79batteryIconTooltipNew :: String -> TaffyIO Widget
80batteryIconTooltipNew format = do
81 DisplayBatteryChanVar (chan, _) <- setupDisplayBatteryChanVar ["IconName", "State", "Percentage", "TimeToFull", "TimeToEmpty", "EnergyRate"]
82 ctx <- ask
83 liftIO $ do
84 image <- imageNew
85 styleCtx <- widgetGetStyleContext =<< toWidget image
86 defaultTheme <- iconThemeGetDefault
87 let getCurrentBatteryIconNameStringTooltip = do
88 info <- runReaderT getDisplayBatteryInfo ctx
89 let iconNameString = T.pack $ batteryIconName info
90 tooltip = formatBattInfo (getBatteryWidgetInfo info) format
91 return (iconNameString, tooltip)
92 extractPixbuf info =
93 fst <$> iconInfoLoadSymbolicForContext info styleCtx
94 setIconForSize size = do
95 (name, tooltip) <- getCurrentBatteryIconNameStringTooltip
96 widgetSetTooltipMarkup image $ Just tooltip
97 iconThemeLookupIcon defaultTheme name size themeLoadFlags >>=
98 traverse extractPixbuf >>=
99 traverse (scalePixbufToSize size OrientationHorizontal)
100 updateImage <- autoSizeImage image setIconForSize OrientationHorizontal
101 toWidget =<< channelWidgetNew image chan (const $ postGUIASync updateImage)
diff --git a/accounts/gkleen@sif/taffybar/src/taffybar.hs b/accounts/gkleen@sif/taffybar/src/taffybar.hs
deleted file mode 100644
index 67ee942d..00000000
--- a/accounts/gkleen@sif/taffybar/src/taffybar.hs
+++ /dev/null
@@ -1,89 +0,0 @@
1{-# LANGUAGE OverloadedStrings #-}
2
3module Main where
4
5import System.Taffybar (startTaffybar)
6import System.Taffybar.Context (TaffybarConfig(..))
7import System.Taffybar.Hooks
8import System.Taffybar.SimpleConfig hiding (SimpleTaffyConfig(cssPaths))
9import System.Taffybar.Widget
10import qualified System.Taffybar.Widget.Clock as MyClock
11import System.Taffybar.Widget.TooltipBattery
12
13import Data.Time.Format
14import Data.Time.LocalTime
15import Data.Time.Calendar.WeekDate
16
17import qualified Data.Text as T
18
19import Control.Exception (SomeException, try)
20import Control.Monad.Trans.Reader (mapReaderT)
21
22import Paths_gkleen_sif_taffybar
23
24import System.Log.Logger
25
26
27main :: IO ()
28main = do
29 logger <- getLogger "System.Taffybar"
30 saveGlobalLogger $ setLevel INFO logger
31
32 myCssPath <- getDataFileName "taffybar.css"
33 startTaffybar taffybarConfig{ cssPaths = pure myCssPath }
34
35
36taffybarConfig :: TaffybarConfig
37taffybarConfig =
38 let myWorkspacesConfig =
39 defaultWorkspacesConfig
40 { maxIcons = Just 0
41 , widgetGap = 7
42 , showWorkspaceFn = \case
43 -- Workspace{ workspaceState = Empty } -> False
44 Workspace{ workspaceName } | workspaceName == "NSP" -> False
45 _other -> True
46 , getWindowIconPixbuf = \i d -> either (\(_ :: SomeException) -> Nothing) id <$> mapReaderT try (defaultGetWindowIconPixbuf i d)
47 , urgentWorkspaceState = True
48 }
49 workspaces = workspacesNew myWorkspacesConfig
50 clock = MyClock.textClockNewWith MyClock.defaultClockConfig
51 { MyClock.clockUpdateStrategy = MyClock.RoundedTargetInterval 1 0.0
52 , MyClock.clockFormat = \tl zt@ZonedTime{ zonedTimeToLocalTime = LocalTime{ localDay } }
53 -> let date = formatTime tl "%Y-%m-%d" localDay
54 weekdate = "W" <> show2 woy <> "-" <> show dow
55 where (_, woy, dow) = toWeekDate localDay
56 show2 :: Int -> String
57 show2 x = replicate (2 - length s) '0' ++ s
58 where s = show x
59 time = formatTime tl "%H:%M:%S%Ez" zt
60 in T.intercalate " " $ map T.pack [weekdate, date, time]
61 }
62 layout = layoutNew defaultLayoutConfig
63 windowsW = windowsNew defaultWindowsConfig
64 { getMenuLabel = truncatedGetMenuLabel 80
65 , getActiveLabel = truncatedGetActiveLabel 80
66 }
67 worktime = commandRunnerNew 60 "worktime" [] "worktime"
68 worktimeToday = commandRunnerNew 60 "worktime" ["today"] "worktime today"
69 -- See https://github.com/taffybar/gtk-sni-tray#statusnotifierwatcher
70 -- for a better way to set up the sni tray
71 -- tray = sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt
72 tray = sniTrayNew
73 myConfig = defaultSimpleTaffyConfig
74 { startWidgets =
75 workspaces : map (>>= buildContentsBox) [ layout, windowsW ]
76 , endWidgets = map (>>= buildContentsBox) $ reverse
77 -- , mpris2New
78 [ worktime, worktimeToday
79 , clock
80 , tray
81 , batteryIconTooltipNew "$status$ $percentage$%$if(time)$$if(rate)$ ($rate$W $time$)$else$ ($time$)$endif$$elseif(rate)$ ($rate$W)$endif$"
82 ]
83 , barPosition = Top
84 , barPadding = 2
85 , barHeight = ExactSize 28
86 , widgetSpacing = 10
87 }
88 in withBatteryRefresh $ withLogServer $
89 withToggleServer $ toTaffyConfig myConfig
diff --git a/accounts/gkleen@sif/taffybar/taffybar.css b/accounts/gkleen@sif/taffybar/taffybar.css
deleted file mode 100644
index 7a297465..00000000
--- a/accounts/gkleen@sif/taffybar/taffybar.css
+++ /dev/null
@@ -1,146 +0,0 @@
1@define-color transparent rgba(0.0, 0.0, 0.0, 0.0);
2@define-color white #808080;
3@define-color gray #202020;
4@define-color green #008000;
5@define-color yellow #808000;
6@define-color blue #000080;
7@define-color red #800000;
8@define-color black #000000;
9/* @define-color taffy-blue #0c7cd5; */
10@define-color taffy-blue @blue;
11
12@define-color active-window-color @white;
13@define-color urgent-window-color @taffy-blue;
14@define-color font-color @white;
15@define-color menu-background-color @black;
16@define-color menu-font-color @white;
17
18/* Top level styling */
19
20.taffy-window * {
21 /*
22 This removes any existing styling from UI elements. Taffybar will not
23 cohere with your gtk theme.
24 */
25 all: unset;
26
27 font-family: "Fira Sans", sans-serif;
28 font-size: 21px;
29 color: @font-color;
30}
31
32.taffy-box {
33 /* border-radius: 10px; */
34 background-color: @black;
35}
36
37.inner-pad {
38 /* padding-bottom: 5px; */
39 /* padding-top: 5px; */
40 padding-left: 2px;
41 padding-right: 2px;
42}
43
44.contents {
45 /* padding-bottom: 4px; */
46 /* padding-top: 4px; */
47 padding-right: 2px;
48 padding-left: 2px;
49 transition: background-color .5s;
50 border-radius: 5px;
51}
52
53/* Workspaces styling */
54
55.workspace-label {
56 padding-right: 3px;
57 padding-left: 2px;
58 font-size: 21px;
59}
60
61.workspace-label.active {
62 color: @green;
63}
64.workspace-label.visible {
65 color: @yellow;
66}
67.workspace-label.empty {
68 color: @gray;
69}
70.workspace-label.urgent {
71 color: @red;
72}
73
74.active .contents {
75 background-color: rgba(0.0, 0.0, 0.0, 0.5);
76}
77
78.visible .contents {
79 background-color: rgba(0.0, 0.0, 0.0, 0.2);
80}
81
82.window-icon-container {
83 transition: opacity .5s, box-shadow .5s;
84 opacity: 1;
85}
86
87/* This gives space for the box-shadow (they look like underlines) that follow.
88 This will actually affect all widgets, (not just the workspace icons), but
89 that is what we want since we want the icons to look the same. */
90.auto-size-image, .sni-tray {
91 padding-top: 3px;
92 padding-bottom: 3px;
93}
94
95.window-icon-container.active {
96 box-shadow: inset 0 -3px @white;
97}
98
99.window-icon-container.urgent {
100 box-shadow: inset 0 -3px @urgent-window-color;
101}
102
103.window-icon-container.inactive .window-icon {
104 padding: 0px;
105}
106
107.window-icon-container.minimized .window-icon {
108 opacity: .3;
109}
110
111.window-icon {
112 opacity: 1;
113 transition: opacity .5s;
114}
115
116/* Button styling */
117
118button {
119 background-color: @transparent;
120 border-width: 0px;
121 border-radius: 0px;
122}
123
124button:checked, button:hover .Contents:hover {
125 box-shadow: inset 0 -3px @taffy-blue;
126}
127
128/* Menu styling */
129
130/* The ".taffy-window" prefixed selectors are needed because if they aren't present,
131 the top level .Taffybar selector takes precedence */
132.taffy-window menuitem *, menuitem * {
133 color: @menu-font-color;
134}
135
136.taffy-window menuitem, menuitem {
137 background-color: @menu-background-color;
138}
139
140.taffy-window menuitem:hover, menuitem:hover {
141 background-color: @taffy-blue;
142}
143
144.taffy-window menuitem:hover > label, menuitem:hover > label {
145 color: @white;
146}
diff --git a/accounts/gkleen@sif/xmonad/.gitignore b/accounts/gkleen@sif/xmonad/.gitignore
deleted file mode 100644
index c11891cd..00000000
--- a/accounts/gkleen@sif/xmonad/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
1**/#*#
2**/.stack-work/
3/stack.yaml.lock
4/*.cabal
diff --git a/accounts/gkleen@sif/xmonad/default.nix b/accounts/gkleen@sif/xmonad/default.nix
deleted file mode 100644
index 8790c12f..00000000
--- a/accounts/gkleen@sif/xmonad/default.nix
+++ /dev/null
@@ -1,7 +0,0 @@
1argumentPackages@{ ... }:
2
3let
4 # defaultPackages = (import ./stackage.nix {});
5 # haskellPackages = defaultPackages // argumentPackages;
6 haskellPackages = argumentPackages;
7in haskellPackages.callPackage ./xmonad-yggdrasil.nix {}
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs
deleted file mode 100644
index e6accdcc..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs
+++ /dev/null
@@ -1,127 +0,0 @@
1{-# LANGUAGE DeriveGeneric, OverloadedLists, OverloadedStrings, ViewPatterns, ExistentialQuantification, MultiWayIf #-}
2
3module XMonad.Mpv
4 ( MpvCommand(..), MpvResponse(..), MpvException(..)
5 , mpv
6 , mpvDir
7 , mpvAll, mpvOne
8 , mpvResponse
9 ) where
10
11import Data.Aeson
12
13import Data.Monoid
14
15import Network.Socket hiding (recv)
16import Network.Socket.ByteString
17
18import qualified Data.ByteString as BS
19import qualified Data.ByteString.Char8 as CBS
20import qualified Data.ByteString.Lazy as LBS
21
22import GHC.Generics (Generic)
23import Data.Typeable (Typeable)
24import Data.String (IsString(..))
25
26import Control.Exception
27
28import System.IO.Temp (getCanonicalTemporaryDirectory)
29
30import Control.Monad
31import Control.Exception (bracket)
32import Control.Monad.IO.Class (MonadIO(..))
33
34import System.FilePath
35import System.Directory (getDirectoryContents)
36
37import Data.List
38import Data.Either
39import Data.Maybe
40
41import Debug.Trace
42
43
44data MpvCommand
45 = forall a. ToJSON a => MpvSetProperty String a
46 | MpvGetProperty String
47data MpvResponse
48 = MpvError String
49 | MpvSuccess (Maybe Value)
50 deriving (Read, Show, Generic, Eq)
51data MpvException = MpvException String
52 | MpvNoValue
53 | MpvNoParse String
54 deriving (Generic, Typeable, Read, Show)
55instance Exception MpvException
56
57
58instance ToJSON MpvCommand where
59 toJSON (MpvSetProperty name val) = Array ["set_property", fromString name, toJSON val]
60 toJSON (MpvGetProperty name) = Array ["get_property", fromString name]
61
62instance FromJSON MpvResponse where
63 parseJSON = withObject "response object" $ \obj -> do
64 mval <- obj .:? "data"
65 err <- obj .: "error"
66
67 let ret
68 | err == "success" = MpvSuccess mval
69 | otherwise = MpvError err
70
71 return ret
72
73mpvSocket :: FilePath -> (Socket -> IO a) -> IO a
74mpvSocket sockPath = withSocketsDo . bracket mkSock close
75 where
76 mkSock = do
77 sock <- socket AF_UNIX Stream defaultProtocol
78 connect sock $ SockAddrUnix (traceId sockPath)
79 return sock
80
81mpvResponse :: FromJSON v => MpvResponse -> IO v
82mpvResponse (MpvError str) = throwIO $ MpvException str
83mpvResponse (MpvSuccess Nothing) = throwIO MpvNoValue
84mpvResponse (MpvSuccess (Just v)) = case fromJSON v of
85 Success v' -> return v'
86 Error str -> throwIO $ MpvNoParse str
87
88mpv :: FilePath -> MpvCommand -> IO MpvResponse
89mpv sockPath cmd = mpvSocket sockPath $ \sock -> do
90 let message = (`BS.append` "\n") . LBS.toStrict . encode $ Object [("command", toJSON cmd)]
91 traceIO $ show message
92 sendAll sock message
93 let recvAll = do
94 prefix <- recv sock 4096
95 if
96 | (prefix', rest) <- CBS.break (== '\n') prefix
97 , not (BS.null rest) -> return prefix'
98 | BS.null prefix -> return prefix
99 | otherwise -> BS.append prefix <$> recvAll
100 response <- recvAll
101 traceIO $ show response
102 either (ioError . userError) return . traceShowId $ eitherDecodeStrict' response
103
104mpvDir :: Exception e => FilePath -> (FilePath -> [(FilePath, Either e MpvResponse)] -> Maybe MpvCommand) -> IO [(FilePath, Either e MpvResponse)]
105mpvDir dir step = do
106 socks <- filter (".sock" `isSuffixOf`) <$> getDirectoryContents dir
107 go [] socks
108 where
109 go acc [] = return acc
110 go acc (sock:socks)
111 | Just cmd <- step sock acc = do
112 res <- try $ mpv (dir </> sock) cmd
113 go ((sock, res) : acc) socks
114 | otherwise =
115 go acc socks
116
117mpvAll :: FilePath -> MpvCommand -> IO [MpvResponse]
118mpvAll dir cmd = do
119 results <- map snd <$> (mpvDir dir (\_ _ -> Just cmd) :: IO [(FilePath, Either SomeException MpvResponse)])
120 mapM (either throwIO return) results
121
122mpvOne :: FilePath -> MpvCommand -> IO (Maybe MpvResponse)
123mpvOne dir cmd = listToMaybe . snd . partitionEithers . map snd <$> (mpvDir dir step :: IO [(FilePath, Either SomeException MpvResponse)])
124 where
125 step _ results
126 | any (isRight . snd) results = Nothing
127 | otherwise = Just cmd
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs
deleted file mode 100644
index 1caefae5..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs
+++ /dev/null
@@ -1,94 +0,0 @@
1module XMonad.Prompt.MyPass
2 (
3 -- * Usages
4 -- $usages
5 mkPassPrompt
6 ) where
7
8import Control.Monad (liftM)
9import XMonad.Core
10import XMonad.Prompt ( XPrompt
11 , showXPrompt
12 , commandToComplete
13 , nextCompletion
14 , getNextCompletion
15 , XPConfig
16 , mkXPrompt
17 , searchPredicate)
18import System.Directory (getHomeDirectory)
19import System.FilePath (takeExtension, dropExtension, combine)
20import System.Posix.Env (getEnv)
21import XMonad.Util.Run (runProcessWithInput)
22
23-- $usages
24-- You can use this module with the following in your @~\/.xmonad\/xmonad.hs@:
25--
26-- > import XMonad.Prompt.Pass
27--
28-- Then add a keybinding for 'passPrompt', 'passGeneratePrompt' or 'passRemovePrompt':
29--
30-- > , ((modMask x , xK_p) , passPrompt xpconfig)
31-- > , ((modMask x .|. controlMask, xK_p) , passGeneratePrompt xpconfig)
32-- > , ((modMask x .|. controlMask .|. shiftMask, xK_p), passRemovePrompt xpconfig)
33--
34-- For detailed instructions on:
35--
36-- - editing your key bindings, see "XMonad.Doc.Extending#Editing_key_bindings".
37--
38-- - how to setup the password storage, see <http://git.zx2c4.com/password-store/about/>
39--
40
41type Predicate = String -> String -> Bool
42
43getPassCompl :: [String] -> Predicate -> String -> IO [String]
44getPassCompl compls p s
45 | length s <= minL
46 , all ((> minL) . length) compls = return []
47 | otherwise = do return $ filter (p s) compls
48 where
49 minL = 3
50
51type PromptLabel = String
52
53data Pass = Pass PromptLabel
54
55instance XPrompt Pass where
56 showXPrompt (Pass prompt) = prompt ++ ": "
57 commandToComplete _ c = c
58 nextCompletion _ = getNextCompletion
59
60-- | Default password store folder in $HOME/.password-store
61--
62passwordStoreFolderDefault :: String -> String
63passwordStoreFolderDefault home = combine home ".password-store"
64
65-- | Compute the password store's location.
66-- Use the PASSWORD_STORE_DIR environment variable to set the password store.
67-- If empty, return the password store located in user's home.
68--
69passwordStoreFolder :: IO String
70passwordStoreFolder =
71 getEnv "PASSWORD_STORE_DIR" >>= computePasswordStoreDir
72 where computePasswordStoreDir Nothing = liftM passwordStoreFolderDefault getHomeDirectory
73 computePasswordStoreDir (Just storeDir) = return storeDir
74
75-- | A pass prompt factory
76--
77mkPassPrompt :: PromptLabel -> (String -> X ()) -> XPConfig -> X ()
78mkPassPrompt promptLabel passwordFunction xpconfig = do
79 passwords <- io (passwordStoreFolder >>= getPasswords)
80 mkXPrompt (Pass promptLabel) xpconfig (getPassCompl passwords $ searchPredicate xpconfig) passwordFunction
81
82-- | Retrieve the list of passwords from the password storage 'passwordStoreDir
83getPasswords :: FilePath -> IO [String]
84getPasswords passwordStoreDir = do
85 files <- runProcessWithInput "find" [
86 passwordStoreDir,
87 "-type", "f",
88 "-name", "*.gpg",
89 "-printf", "%P\n"] []
90 return $ map removeGpgExtension $ lines files
91
92removeGpgExtension :: String -> String
93removeGpgExtension file | takeExtension file == ".gpg" = dropExtension file
94 | otherwise = file
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs
deleted file mode 100644
index c268f87d..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs
+++ /dev/null
@@ -1,105 +0,0 @@
1module XMonad.Prompt.MyShell
2 ( Shell (..)
3 , shellPrompt
4 , prompt
5 , safePrompt
6 , unsafePrompt
7 , getCommands
8 , getShellCompl
9 , split
10 ) where
11
12import Codec.Binary.UTF8.String (encodeString)
13import Control.Exception as E
14import Control.Monad (forM)
15import Data.List (isPrefixOf)
16import System.Directory (doesDirectoryExist, getDirectoryContents)
17import System.Environment (getEnv)
18import System.Posix.Files (getFileStatus, isDirectory)
19
20import XMonad hiding (config)
21import XMonad.Prompt
22import XMonad.Util.Run
23
24econst :: Monad m => a -> IOException -> m a
25econst = const . return
26
27data Shell = Shell String
28
29instance XPrompt Shell where
30 showXPrompt (Shell q) = q
31 completionToCommand _ = escape
32
33shellPrompt :: String -> XPConfig -> X ()
34shellPrompt q c = do
35 cmds <- io getCommands
36 mkXPrompt (Shell q) c (getShellCompl cmds) spawn
37
38{- $spawns
39 See safe and unsafeSpawn in "XMonad.Util.Run".
40 prompt is an alias for safePrompt;
41 safePrompt and unsafePrompt work on the same principles, but will use
42 XPrompt to interactively query the user for input; the appearance is
43 set by passing an XPConfig as the second argument. The first argument
44 is the program to be run with the interactive input.
45 You would use these like this:
46
47 > , ((modm, xK_b), safePrompt "firefox" greenXPConfig)
48 > , ((modm .|. shiftMask, xK_c), prompt ("xterm" ++ " -e") greenXPConfig)
49
50 Note that you want to use safePrompt for Firefox input, as Firefox
51 wants URLs, and unsafePrompt for the XTerm example because this allows
52 you to easily start a terminal executing an arbitrary command, like
53 'top'. -}
54
55prompt, unsafePrompt, safePrompt :: String -> FilePath -> XPConfig -> X ()
56prompt = unsafePrompt
57safePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run
58 where run = safeSpawn c . return
59unsafePrompt q c config = mkXPrompt (Shell q) config (getShellCompl [c]) run
60 where run a = unsafeSpawn $ c ++ " " ++ a
61
62getShellCompl :: [String] -> String -> IO [String]
63getShellCompl cmds s | s == "" || last s == ' ' = return []
64 | otherwise = do
65 f <- fmap lines $ runProcessWithInput "bash" [] ("compgen -A file -- "
66 ++ s ++ "\n")
67 files <- case f of
68 [x] -> do fs <- getFileStatus (encodeString x)
69 if isDirectory fs then return [x ++ "/"]
70 else return [x]
71 _ -> return f
72 return . uniqSort $ files ++ commandCompletionFunction cmds s
73
74commandCompletionFunction :: [String] -> String -> [String]
75commandCompletionFunction cmds str | '/' `elem` str = []
76 | otherwise = filter (isPrefixOf str) cmds
77
78getCommands :: IO [String]
79getCommands = do
80 p <- getEnv "PATH" `E.catch` econst []
81 let ds = filter (/= "") $ split ':' p
82 es <- forM ds $ \d -> do
83 exists <- doesDirectoryExist d
84 if exists
85 then getDirectoryContents d
86 else return []
87 return . uniqSort . filter ((/= '.') . head) . concat $ es
88
89split :: Eq a => a -> [a] -> [[a]]
90split _ [] = []
91split e l =
92 f : split e (rest ls)
93 where
94 (f,ls) = span (/=e) l
95 rest s | s == [] = []
96 | otherwise = tail s
97
98escape :: String -> String
99escape [] = ""
100escape (x:xs)
101 | isSpecialChar x = '\\' : x : escape xs
102 | otherwise = x : escape xs
103
104isSpecialChar :: Char -> Bool
105isSpecialChar = flip elem " &\\@\"'#?$*()[]{};"
diff --git a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs
deleted file mode 100644
index 998c533e..00000000
--- a/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs
+++ /dev/null
@@ -1,246 +0,0 @@
1module XMonad.Prompt.MySsh
2 ( -- * Usage
3 -- $usage
4 sshPrompt,
5 Ssh,
6 Override (..),
7 mkOverride,
8 Conn (..),
9 moshCmd,
10 moshCmd',
11 sshCmd,
12 inTmux,
13 withEnv
14 ) where
15
16import XMonad
17import XMonad.Util.Run
18import XMonad.Prompt
19
20import System.Directory
21import System.Environment
22import qualified Control.Exception as E
23
24import Control.Monad
25import Data.Maybe
26
27import Text.Parsec.String
28import Text.Parsec
29import Data.Char (isSpace)
30
31econst :: Monad m => a -> E.IOException -> m a
32econst = const . return
33
34-- $usage
35-- 1. In your @~\/.xmonad\/xmonad.hs@:
36--
37-- > import XMonad.Prompt
38-- > import XMonad.Prompt.Ssh
39--
40-- 2. In your keybindings add something like:
41--
42-- > , ((modm .|. controlMask, xK_s), sshPrompt defaultXPConfig)
43--
44-- Keep in mind, that if you want to use the completion you have to
45-- disable the "HashKnownHosts" option in your ssh_config
46--
47-- For detailed instruction on editing the key binding see
48-- "XMonad.Doc.Extending#Editing_key_bindings".
49
50data Override = Override
51 { oUser :: Maybe String
52 , oHost :: String
53 , oPort :: Maybe Int
54 , oCommand :: Conn -> String
55 }
56
57mkOverride = Override { oUser = Nothing, oHost = "", oPort = Nothing, oCommand = sshCmd }
58sshCmd c = concat
59 [ "ssh -t "
60 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
61 , cHost c
62 , if isJust $ cPort c then " -p " ++ (show $ fromJust $ cPort c) else ""
63 , " -- "
64 , cCommand c
65 ]
66moshCmd c = concat
67 [ "mosh "
68 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
69 , cHost c
70 , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else ""
71 , " -- "
72 , cCommand c
73 ]
74moshCmd' p c = concat
75 [ "mosh "
76 , "--server=" ++ p ++ " "
77 , if isJust $ cUser c then (fromJust $ cUser c) ++ "@" else ""
78 , cHost c
79 , if isJust $ cPort c then " --ssh=\"ssh -p " ++ (show $ fromJust $ cPort c) ++ "\"" else ""
80 , " -- "
81 , cCommand c
82 ]
83inTmux Nothing c
84 | null $ cCommand c = c { cCommand = "tmux new-session" }
85 | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" }
86inTmux (Just h) c
87 | null $ cCommand c = c { cCommand = "tmux new-session -As " <> h }
88 | otherwise = c { cCommand = "tmux new-session \"" ++ (cCommand c) ++ "\"" }
89withEnv :: [(String, String)] -> Conn -> Conn
90withEnv envs c = c { cCommand = "env" ++ (concat $ map (\(n, v) -> ' ' : (n ++ "=" ++ v)) envs) ++ " " ++ (cCommand c) }
91
92data Conn = Conn
93 { cUser :: Maybe String
94 , cHost :: String
95 , cPort :: Maybe Int
96 , cCommand :: String
97 } deriving (Eq, Show, Read)
98
99data Ssh = Ssh
100
101instance XPrompt Ssh where
102 showXPrompt Ssh = "SSH to: "
103 commandToComplete _ c = c
104 nextCompletion _ = getNextCompletion
105
106toConn :: String -> Maybe Conn
107toConn = toConn' . parse connParser "(unknown)"
108toConn' :: Either ParseError Conn -> Maybe Conn
109toConn' (Left _) = Nothing
110toConn' (Right a) = Just a
111
112connParser :: Parser Conn
113connParser = do
114 spaces
115 user' <- optionMaybe $ try $ do
116 str <- many1 $ satisfy (\c -> (not $ isSpace c) && (c /= '@'))
117 char '@'
118 return str
119 host' <- many1 $ satisfy (not . isSpace)
120 port' <- optionMaybe $ try $ do
121 space
122 string "-p"
123 spaces
124 int <- many1 digit
125 (space >> return ()) <|> eof
126 return $ (read int :: Int)
127 spaces
128 command' <- many anyChar
129 eof
130 return $ Conn
131 { cHost = host'
132 , cUser = user'
133 , cPort = port'
134 , cCommand = command'
135 }
136
137sshPrompt :: [Override] -> XPConfig -> X ()
138sshPrompt o c = do
139 sc <- io sshComplList
140 mkXPrompt Ssh c (mkComplFunFromList c sc) $ ssh o
141
142ssh :: [Override] -> String -> X ()
143ssh overrides str = do
144 let cmd = applyOverrides overrides str
145 liftIO $ putStr "SSH Command: "
146 liftIO $ putStrLn cmd
147 runInTerm "" cmd
148
149applyOverrides :: [Override] -> String -> String
150applyOverrides [] str = "ssh " ++ str
151applyOverrides (o:os) str = case (applyOverride o str) of
152 Just str -> str
153 Nothing -> applyOverrides os str
154
155applyOverride :: Override -> String -> Maybe String
156applyOverride o str = let
157 conn = toConn str
158 in
159 if isNothing conn then Nothing else
160 case (fromJust conn) `matches` o of
161 True -> Just $ (oCommand o) (fromJust conn)
162 False -> Nothing
163
164matches :: Conn -> Override -> Bool
165a `matches` b = and
166 [ justBool (cUser a) (oUser b) (==)
167 , (cHost a) == (oHost b)
168 , justBool (cPort a) (oPort b) (==)
169 ]
170
171justBool :: Eq a => Maybe a -> Maybe a -> (a -> a -> Bool) -> Bool
172justBool Nothing _ _ = True
173justBool _ Nothing _ = True
174justBool (Just a) (Just b) match = a `match` b
175
176sshComplList :: IO [String]
177sshComplList = uniqSort `fmap` liftM2 (++) sshComplListLocal sshComplListGlobal
178
179sshComplListLocal :: IO [String]
180sshComplListLocal = do
181 h <- getEnv "HOME"
182 s1 <- sshComplListFile $ h ++ "/.ssh/known_hosts"
183 s2 <- sshComplListConf $ h ++ "/.ssh/config"
184 return $ s1 ++ s2
185
186sshComplListGlobal :: IO [String]
187sshComplListGlobal = do
188 env <- getEnv "SSH_KNOWN_HOSTS" `E.catch` econst "/nonexistent"
189 fs <- mapM fileExists [ env
190 , "/usr/local/etc/ssh/ssh_known_hosts"
191 , "/usr/local/etc/ssh_known_hosts"
192 , "/etc/ssh/ssh_known_hosts"
193 , "/etc/ssh_known_hosts"
194 ]
195 case catMaybes fs of
196 [] -> return []
197 (f:_) -> sshComplListFile' f
198
199sshComplListFile :: String -> IO [String]
200sshComplListFile kh = do
201 f <- doesFileExist kh
202 if f then sshComplListFile' kh
203 else return []
204
205sshComplListFile' :: String -> IO [String]
206sshComplListFile' kh = do
207 l <- readFile kh
208 return $ map (getWithPort . takeWhile (/= ',') . concat . take 1 . words)
209 $ filter nonComment
210 $ lines l
211
212sshComplListConf :: String -> IO [String]
213sshComplListConf kh = do
214 f <- doesFileExist kh
215 if f then sshComplListConf' kh
216 else return []
217
218sshComplListConf' :: String -> IO [String]
219sshComplListConf' kh = do
220 l <- readFile kh
221 return $ map (!!1)
222 $ filter isHost
223 $ map words
224 $ lines l
225 where
226 isHost ws = take 1 ws == ["Host"] && length ws > 1
227
228fileExists :: String -> IO (Maybe String)
229fileExists kh = do
230 f <- doesFileExist kh
231 if f then return $ Just kh
232 else return Nothing
233
234nonComment :: String -> Bool
235nonComment [] = False
236nonComment ('#':_) = False
237nonComment ('|':_) = False -- hashed, undecodeable
238nonComment _ = True
239
240getWithPort :: String -> String
241getWithPort ('[':str) = host ++ " -p " ++ port
242 where (host,p) = break (==']') str
243 port = case p of
244 ']':':':x -> x
245 _ -> "22"
246getWithPort str = str
diff --git a/accounts/gkleen@sif/xmonad/package.yaml b/accounts/gkleen@sif/xmonad/package.yaml
deleted file mode 100644
index f65137af..00000000
--- a/accounts/gkleen@sif/xmonad/package.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
1name: xmonad-yggdrasil
2
3executables:
4 xmonad:
5 dependencies:
6 - base
7 - xmonad
8 - xmonad-contrib
9 - aeson
10 - bytestring
11 - text
12 - temporary
13 - filepath
14 - directory
15 - network
16 - unix
17 - utf8-string
18 - parsec
19 - process
20 - mtl
21 - X11
22 - transformers
23 - containers
24 - hostname
25 - libnotify
26 - taffybar
27
28 main: xmonad.hs
29 source-dirs:
30 - .
31 - lib
diff --git a/accounts/gkleen@sif/xmonad/stack.nix b/accounts/gkleen@sif/xmonad/stack.nix
deleted file mode 100644
index 17a49e04..00000000
--- a/accounts/gkleen@sif/xmonad/stack.nix
+++ /dev/null
@@ -1,17 +0,0 @@
1{ ghc, nixpkgs ? import ./nixpkgs.nix {} }:
2
3let
4 haskellPackages = import ./stackage.nix { inherit nixpkgs; };
5 inherit (nixpkgs {}) pkgs;
6in pkgs.haskell.lib.buildStackProject {
7 inherit ghc;
8 inherit (haskellPackages) stack;
9 name = "stackenv";
10 buildInputs = (with pkgs;
11 [ xorg.libX11 xorg.libXrandr xorg.libXinerama xorg.libXScrnSaver xorg.libXext xorg.libXft
12 cairo
13 glib
14 ]) ++ (with haskellPackages;
15 [
16 ]);
17}
diff --git a/accounts/gkleen@sif/xmonad/stack.yaml b/accounts/gkleen@sif/xmonad/stack.yaml
deleted file mode 100644
index b8ed1147..00000000
--- a/accounts/gkleen@sif/xmonad/stack.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
1nix:
2 enable: true
3 shell-file: stack.nix
4
5resolver: lts-13.21
6
7packages:
8 - .
9
10extra-deps: []
diff --git a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix
deleted file mode 100644
index 7c853619..00000000
--- a/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix
+++ /dev/null
@@ -1,21 +0,0 @@
1{ mkDerivation, aeson, base, bytestring, containers, directory
2, filepath, hostname, hpack, mtl, network, parsec, process, lib
3, temporary, transformers, unix, utf8-string, X11, xmonad
4, xmonad-contrib, libnotify, taffybar
5}:
6mkDerivation {
7 pname = "xmonad-yggdrasil";
8 version = "0.0.0";
9 src = ./.;
10 isLibrary = false;
11 isExecutable = true;
12 libraryToolDepends = [ hpack ];
13 executableHaskellDepends = [
14 aeson base bytestring containers directory filepath hostname mtl
15 network parsec process temporary transformers unix utf8-string X11
16 xmonad xmonad-contrib libnotify taffybar
17 ];
18 preConfigure = "hpack";
19 license = "unknown";
20 hydraPlatforms = lib.platforms.none;
21}
diff --git a/accounts/gkleen@sif/xmonad/xmonad.hs b/accounts/gkleen@sif/xmonad/xmonad.hs
deleted file mode 100644
index a44d3bb7..00000000
--- a/accounts/gkleen@sif/xmonad/xmonad.hs
+++ /dev/null
@@ -1,939 +0,0 @@
1{-# LANGUAGE TupleSections, ViewPatterns, OverloadedStrings, FlexibleInstances, UndecidableInstances, MultiWayIf, NumDecimals #-}
2
3import XMonad
4import XMonad.Hooks.DynamicLog
5import XMonad.Hooks.ManageDocks
6import XMonad.Util.Run hiding (proc)
7import XMonad.Util.Loggers
8import XMonad.Util.EZConfig(additionalKeys)
9import System.IO
10import System.IO.Error
11import System.Environment
12import Data.Map (Map)
13import qualified Data.Map as Map
14import qualified XMonad.StackSet as W
15import System.Exit
16import Control.Monad.State (get)
17-- import XMonad.Layout.Spiral
18import Data.Ratio
19import Data.List
20import Data.Char
21import Data.Maybe (fromMaybe, listToMaybe, maybeToList, catMaybes, isJust)
22import XMonad.Layout.Tabbed
23import XMonad.Prompt
24import XMonad.Prompt.Input
25import XMonad.Util.Scratchpad
26import XMonad.Util.NamedScratchpad
27import XMonad.Util.Ungrab
28import Control.Monad (sequence, liftM, liftM2, join, void)
29import XMonad.Util.WorkspaceCompare
30import XMonad.Layout.NoBorders
31import XMonad.Layout.PerWorkspace
32import XMonad.Layout.SimplestFloat
33import XMonad.Layout.Renamed
34import XMonad.Layout.Reflect
35import XMonad.Layout.OnHost
36import XMonad.Layout.Combo
37import XMonad.Layout.ComboP
38import XMonad.Layout.Column
39import XMonad.Layout.TwoPane
40import XMonad.Layout.IfMax
41import XMonad.Layout.LayoutBuilder
42import XMonad.Layout.WindowNavigation
43import XMonad.Layout.Dwindle
44import XMonad.Layout.TrackFloating
45import System.Process
46import System.Directory (removeFile)
47import System.Posix.Files
48import System.FilePath ((</>))
49import Control.Concurrent
50import System.Posix.Process (getProcessID)
51import System.IO.Error
52import System.IO
53import XMonad.Hooks.ManageHelpers hiding (CW)
54import XMonad.Hooks.UrgencyHook as U
55import XMonad.Hooks.EwmhDesktops
56import XMonad.StackSet (RationalRect (..))
57import Control.Monad (when, filterM, (<=<))
58import Graphics.X11.ExtraTypes.XF86
59import XMonad.Util.Cursor
60import XMonad.Actions.Warp
61import XMonad.Actions.FloatKeys
62import XMonad.Util.SpawnOnce
63import System.Directory
64import System.FilePath
65import XMonad.Actions.CopyWindow
66import XMonad.Hooks.ServerMode
67import XMonad.Actions.Commands
68import XMonad.Actions.CycleWS
69import XMonad.Actions.RotSlaves
70import XMonad.Actions.UpdatePointer
71import XMonad.Prompt.Window
72import Data.IORef
73import Data.Monoid
74import Data.String
75import qualified XMonad.Actions.PhysicalScreens as P
76
77import XMonad.Layout.IM
78
79import System.Taffybar.Support.PagerHints (pagerHints)
80
81import XMonad.Prompt.MyShell
82import XMonad.Prompt.MyPass
83import XMonad.Prompt.MySsh
84
85import XMonad.Mpv
86
87import Network.HostName
88
89import Control.Applicative ((<$>))
90
91import Libnotify as Notify hiding (appName)
92import qualified Libnotify as Notify (appName)
93import Libnotify (Notification)
94-- import System.Information.Battery
95
96import Data.Int (Int32)
97
98import System.Posix.Process
99import System.Posix.Signals
100import System.Posix.IO as Posix
101import Control.Exception
102
103import System.IO.Unsafe
104
105import Control.Monad.Trans.Class
106import Control.Monad.Trans.Maybe
107
108import Data.Fixed (Micro)
109
110import qualified Data.Text as Text
111import Data.Ord (comparing)
112import Debug.Trace
113
114instance MonadIO m => IsString (m ()) where
115 fromString = spawn
116
117type KeyMap = Map (ButtonMask, KeySym) (X ())
118
119data Host = Host
120 { hName :: HostName
121 , hManageHook :: ManageHook
122 , hWsp :: Integer -> WorkspaceId
123 , hCoWsp :: String -> Maybe WorkspaceId
124 , hKeysMod :: XConfig Layout -> (KeyMap -> KeyMap)
125 , hScreens :: [P.PhysicalScreen]
126 , hKbLayouts :: [(String, Maybe String)]
127 , hCmds :: X [(String, X ())]
128 , hKeyUpKeys :: XConfig Layout -> KeyMap
129 }
130
131defaultHost = Host { hName = "unkown"
132 , hManageHook = composeOne [manageScratchTerm]
133 , hWsp = show
134 , hCoWsp = const Nothing
135 , hKeysMod = const id
136 , hScreens = [0,1..]
137 , hKbLayouts = [ ("us", Just "dvp")
138 , ("us", Nothing)
139 , ("de", Nothing)
140 ]
141 , hCmds = return []
142 , hKeyUpKeys = const Map.empty
143 }
144
145browser :: String
146browser = "env MOZ_USE_XINPUT2=1 firefox"
147
148gray, darkGray, red, green :: String
149gray = "#808080"
150darkGray = "#202020"
151red = "#800000"
152green = "#008000"
153
154hostFromName :: HostName -> Host
155hostFromName h@("vali") = defaultHost { hName = h
156 , hManageHook = composeOne $ catMaybes [ Just manageScratchTerm
157 , assign "web" $ className =? ".dwb-wrapped"
158 , assign "web" $ className =? "Chromium"
159 , assign "work" $ className =? "Emacs"
160 , assign "media" $ className =? "mpv"
161 ]
162 , hWsp = hWsp
163 , hCoWsp = hCoWsp
164 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_d, ["chromium", "chromium $(xclip -o)"])
165 , (xK_e, ["emacsclient -c"])
166 ])
167 `Map.union`
168 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), scratchpadSpawnActionCustom $ (XMonad.terminal conf) ++ " -name scratchpad -title scratchpad -e tmux new-session -D -s scratch")
169 ] )
170 , hScreens = hScreens defaultHost
171 }
172 where
173 workspaceNames = Map.fromList [ (2, "web")
174 , (3, "work")
175 , (10, "media")
176 ]
177 hWsp = wspFromMap workspaceNames
178 hCoWsp = coWspFromMap workspaceNames
179 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
180hostFromName h
181 | h `elem` ["hel", "sif"] = defaultHost { hName = h
182 , hManageHook = namedScratchpadManageHook scratchpads <+> composeOne (catMaybes
183 [ assign "mpv" $ className =? "mpv"
184 , assign "mpv" $ stringProperty "WM_WINDOW_ROLE" =? "presentation"
185 , assign "read" $ stringProperty "WM_WINDOW_ROLE" =? "presenter"
186 , assign "mpv" $ className =? "factorio"
187 , assign "mpv" $ resource =? "twitch"
188 , assign "web" $ className =? "chromium-browser"
189 , assign "web" $ className =? "Google-chrome"
190 , assign "work" $ (appName =? "Devtools" <&&> className =? "firefox")
191 , assign "work" $ className =? "Postman"
192 , assign "web" $ (appName =? "Navigator" <&&> className =? "firefox")
193 , assign "comm" $ (className =? "Emacs" <&&> title =? "Mail")
194 , assign "comm" $ className =? "Zulip"
195 , assign "comm" $ className =? "Element"
196 , assign "comm" $ className =? "Rocket.Chat"
197 , assign "comm" $ className =? "Discord"
198 , assign "comm" $ className =? "Rainbow"
199 , assign "media" $ resource =? "media"
200 , assign "monitor" $ className =? "Grafana"
201 , assign "monitor" $ className =? "Virt-viewer"
202 , assign "monitor" $ resource =? "htop"
203 , assign "monitor" $ resource =? "monitor"
204 , assign "monitor" $ className =? "xfreerdp"
205 , assign "monitor" $ className =? "org.remmina.Remmina"
206 , Just $ resource =? "htop" -?> centerFloat
207 , Just $ (className =? "Scp-dbus-service.py") -?> centerFloat
208 , Just $ resource =? "log" -?> centerFloat
209 , assign "work" $ className =? "Alacritty"
210 , Just $ (appName =? "Edit with Emacs FRAME") -?> centerFloat
211 , assign' ["work", "uni"] $ (className =? "Emacs" <&&> appName /=? "Edit with Emacs FRAME")
212 , assign' ["work", "uni"] $ className =? "jetbrains-idea-ce"
213 , assign "read" $ className =? "llpp"
214 , assign "read" $ className =? "Evince"
215 , assign "read" $ className =? "Zathura"
216 , assign "read" $ className =? "MuPDF"
217 , assign "read" $ className =? "Xournal"
218 , assign "read" $ appName =? "libreoffice"
219 , assign "read" $ appName =? "com-trollworks-gcs-app-GCS"
220 , assign "read" $ appName =? "Tux.py"
221 , assign "read" $ className =? "Gnucash"
222 , assign "comm" $ className =? "Skype"
223 , assign "comm" $ className =? "Daily"
224 , assign "comm" $ className =? "Pidgin"
225 , assign "comm" $ className =? "Thunderbird"
226 , assign "comm" $ className =? "Slack"
227 , Just $ (resource =? "xvkbd") -?> doRectFloat $ RationalRect (1 % 8) (3 % 8) (6 % 8) (4 % 8)
228 , Just $ (stringProperty "_NET_WM_WINDOW_TYPE" =? "_NET_WM_WINDOW_TYPE_DIALOG") -?> doFloat
229 , Just $ (className =? "Dunst") -?> doFloat
230 , Just $ (className =? "Xmessage") -?> doCenterFloat
231 , Just $ (className =? "Nm-openconnect-auth-dialog") -?> centerFloat
232 , Just $ (className =? "Pinentry") -?> doCenterFloat
233 , Just $ (className =? "pinentry") -?> doCenterFloat
234 , Just $ (stringProperty "WM_WINDOW_ROLE" =? "GtkFileChooseDialog") -?> centerFloatSmall
235 , Just $ (className =? "Nvidia-settings") -?> doCenterFloat
236 , Just $ fmap ("Minetest" `isInfixOf`) title -?> doIgnore
237 , Just $ fmap ("Automachef" `isInfixOf`) title -?> doIgnore
238 , assign "call" $ className =? "zoom"
239 ])
240 , hWsp = hWsp
241 , hCoWsp = hCoWsp
242 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_e, ["emacsclient -c"])
243 , (xK_d, [fromString browser, "google-chrome" {- , "notmuch-links" -}])
244 , (xK_c, [ inputPrompt xPConfigMonospace "dc" ?+ dc ])
245 , (xK_g, ["pidgin"])
246 , (xK_s, ["skype"])
247 -- , (xK_p, [mkPassPrompt "Type password" pwType xPConfig, mkPassPrompt "Show password" pwShow xPConfig, mkPassPrompt "Copy password" pwClip xPConfig])
248 , (xK_w, ["sudo rewacom"])
249 , (xK_y, [ "tmux new-window -dt media /var/media/link.hs $(xclip -o)"
250 , "tmux new-window -dt media /var/media/download.hs $(xclip -o)"
251 , "tmux new-window -dt media /var/media/download.hs $(xclip -o -selection clipboard)"
252 ])
253 , (xK_l, [ "tmux new-window -dt media mpv $(xclip -o)"
254 , "tmux new-window -dt media mpv $(xclip -o -selection clipboard)"
255 , "alacritty --class media -e tmuxp load /var/media"
256 ])
257 {- , (xK_m, [ "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch)'"
258 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch-mua-new-mail)'"
259 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e \"(browse-url-mail \"$(xclip -o)\")\""
260 ]) -}
261 , (xK_Return, ["keynav start,windowzoom", "keynav start"])
262 , (xK_t, [inputPrompt xPConfigMonospace "fuzzytime timer" ?+ fuzzytime, fuzzytime "unset", work_fuzzytime])
263 , (xK_a, [inputPrompt xPConfigMonospace "adjmix" ?+ adjmix])
264 , (xK_s, [ inputPromptWithCompl xPConfigMonospace "start synergy" synergyCompl ?+ synergyStart
265 , inputPromptWithCompl xPConfigMonospace "stop synergy" synergyCompl ?+ synergyStop
266 ])
267 , (xK_h, [ "alacritty --class htop -e htop"
268 , "alacritty --class log -e journalctl -xef"
269 ])
270 , (xK_x, [ "autorandr -c"
271 , "autorandr -fl def"
272 ])
273 , (xK_z, [ "zulip -- --force-device-scale-factor=2"
274 ])
275 ])
276 `Map.union`
277 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), namedScratchpadAction scratchpads "term")
278 , ((XMonad.modMask conf .|. controlMask, xK_a), namedScratchpadAction scratchpads "pavucontrol")
279 , ((XMonad.modMask conf .|. controlMask, xK_o), namedScratchpadAction scratchpads "easyeffects")
280 , ((XMonad.modMask conf .|. controlMask .|. shiftMask, xK_o), namedScratchpadAction scratchpads "helvum")
281 , ((XMonad.modMask conf .|. controlMask, xK_w), namedScratchpadAction scratchpads "alarms")
282 , ((XMonad.modMask conf .|. controlMask, xK_b), namedScratchpadAction scratchpads "blueman")
283 , ((XMonad.modMask conf .|. controlMask, xK_p), namedScratchpadAction scratchpads "keepassxc")
284 , ((XMonad.modMask conf .|. controlMask, xK_t), namedScratchpadAction scratchpads "toggl")
285 , ((XMonad.modMask conf .|. controlMask, xK_e), namedScratchpadAction scratchpads "emacs")
286 , ((XMonad.modMask conf .|. controlMask, xK_m), namedScratchpadAction scratchpads "calendar")
287 , ((XMonad.modMask conf .|. controlMask, xK_f), namedScratchpadAction scratchpads "music")
288 , ((XMonad.modMask conf .|. mod1Mask, xK_Up), rotate U)
289 , ((XMonad.modMask conf .|. mod1Mask, xK_Down), rotate D)
290 , ((XMonad.modMask conf .|. mod1Mask, xK_Left), rotate L)
291 , ((XMonad.modMask conf .|. mod1Mask, xK_Right), rotate R)
292 , ((controlMask, xK_space ), "dunstctl close" )
293 , ((controlMask .|. shiftMask, xK_space ), "dunstctl close-all" )
294 , ((controlMask, xK_period), "dunstctl context" )
295 , ((controlMask, xK_comma ), "dunstctl history-pop")
296 -- , ((XMonad.modMask conf .|. shiftMask, xK_a), startMute "hel")
297 ] )
298 , hKeyUpKeys = \conf -> Map.fromList [ -- ((XMonad.modMask conf .|. shiftMask, xK_a), stopMute "hel")
299 ]
300 , hScreens = hScreens defaultHost
301 , hCmds = return [ ("prev-workspace", prevWS)
302 , ("next-workspace", nextWS)
303 , ("prev-window", rotAllDown)
304 , ("next-window", rotAllUp)
305 , ("banish", banishScreen LowerRight)
306 , ("update-gpg-tty", safeSpawn "gpg-connect-agent" ["UPDATESTARTUPTTY", "/bye"])
307 , ("rescreen", rescreen)
308 , ("repanel", do
309 spawn "nm-applet"
310 spawn "blueman-applet"
311 spawn "pasystray"
312 spawn "kdeconnect-indicator"
313 spawn "dunst -print"
314 spawn "udiskie"
315 spawn "autocutsel -s PRIMARY"
316 spawn "autocutsel -s CLIPBOARD"
317 )
318 , ("pause", mediaMpv $ MpvSetProperty "pause" True)
319 , ("unpause", mediaMpv $ MpvSetProperty "pause" False)
320 , ("exit", io $ exitWith ExitSuccess)
321 ]
322 }
323 where
324 withGdkScale act = void . xfork $ setEnv "GDK_SCALE" "2" >> act
325 workspaceNames = Map.fromList [ (1, "comm")
326 , (2, "web")
327 , (3, "work")
328 , (4, "read")
329 , (5, "monitor")
330 , (6, "uni")
331 , (8, "call")
332 , (9, "media")
333 , (10, "mpv")
334 ]
335 scratchpads = [ NS "term" "alacritty --class scratchpad --title scratchpad -e tmux new-session -AD -s scratch" (resource =? "scratchpad") centerFloat
336 , NS "pavucontrol" "pavucontrol" (resource =? "pavucontrol") centerFloat
337 , NS "helvum" "helvum" (resource =? "helvum") centerFloat
338 , NS "easyeffects" "easyeffects" (resource =? "easyeffects") centerFloat
339 , NS "alarms" "alarm-clock-applet" (className =? "Alarm-clock-applet" <&&> title =? "Alarms") centerFloat
340 , NS "blueman" "blueman-manager" (className =? ".blueman-manager-wrapped") centerFloat
341 , NS "keepassxc" "keepassxc" (className =? "KeePassXC") centerFloat
342 , NS "toggl" "toggldesktop" (className =? "Toggl Desktop") centerFloat
343 , NS "calendar" "minetime -- --force-device-scale-factor=1.6" (className =? "MineTime") centerFloat
344 , NS "emacs" "emacsclient -c -F \"'(title . \\\"Scratchpad\\\")\"" (className =? "Emacs" <&&> title =? "Scratchpad") centerFloat
345 , NS "music" "ytmdesktop" (className =? "youtube-music-desktop-app") centerFloat
346 ]
347 centerFloat = customFloating $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
348 centerFloatSmall = customFloating $ RationalRect (1 % 4) (1 % 4) (1 % 2) (1 % 2)
349 hWsp = wspFromMap workspaceNames
350 hCoWsp = coWspFromMap workspaceNames
351 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
352 assign' :: [String] -> Query Bool -> Maybe MaybeManageHook
353 assign' wsps test = do
354 wsIds <- mapM hCoWsp wsps
355 return $ test -?> go wsIds
356 where
357 go :: [WorkspaceId] -> ManageHook
358 go wsps = do
359 visWsps <- liftX $ (\wset -> W.tag . W.workspace <$> W.current wset : W.visible wset) <$> gets windowset
360 case (filter (`elem` visWsps) wsps, wsps) of
361 (wsp : _, _) -> doShift wsp
362 (_, wsp : _) -> doShift wsp
363 ([], []) -> return mempty
364 rotate rot = do
365 safeSpawn "xrandr" ["--output", "eDP-1", "--rotate", xrandrDir]
366 mapM_ rotTouch touchscreens
367 where
368 xrandrDir = case rot of
369 U -> "normal"
370 L -> "left"
371 R -> "right"
372 D -> "inverted"
373 matrix = case rot of
374 U -> [ [ 1, 0, 0]
375 , [ 0, 1, 0]
376 , [ 0, 0, 1]
377 ]
378 L -> [ [ 0, -1, 1]
379 , [ 1, 0, 0]
380 , [ 0, 0, 1]
381 ]
382 R -> [ [ 0, 1, 0]
383 , [-1, 0, 1]
384 , [ 0, 0, 1]
385 ]
386 D -> [ [-1, 0, 1]
387 , [ 0, -1, 1]
388 , [ 0, 0, 1]
389 ]
390 touchscreens = [ "Wacom Co.,Ltd. Pen and multitouch sensor Finger touch"
391 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen stylus"
392 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen eraser"
393 ]
394 rotTouch screen = do
395 safeSpawn "xinput" $ ["set-prop", screen, "Coordinate Transformation Matrix"] ++ map (\n -> show n ++ ",") (concat matrix)
396 safeSpawn "xinput" ["map-to-output", screen, "eDP-1"]
397 withPw f label = io . void . forkProcess $ do
398 uninstallSignalHandlers
399 void $ createSession
400 (dropWhileEnd isSpace -> pw) <- readCreateProcess (proc "pass" ["show", label]) ""
401 void $ f pw
402 pwType :: String -> X ()
403 pwType = withPw $ readCreateProcess (proc "xdotool" ["type", "--clearmodifiers", "--file", "-"])
404 pwClip label = safeSpawn "pass" ["show", "--clip", label]
405 pwShow :: String -> X ()
406 pwShow = withPw $ \pw -> do
407 xmessage <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE")
408 readCreateProcess (proc xmessage ["-file", "-"]) pw
409 fuzzytime str = safeSpawn "fuzzytime" $ "timer" : words str
410 work_fuzzytime = io . void . forkProcess $ do
411 readCreateProcess (proc "worktime" []) "" >>= safeSpawn "fuzzytime" . ("timer" : ) . pure
412 adjmix str = safeSpawn "adjmix" $ words str
413 dc expr = void . xfork $ do
414 result <- readProcess "dc" [] $ expr ++ "f"
415 let
416 (first : rest) = filter (not . null) $ lines result
417 notification = Notify.summary first <> Notify.body (unlines rest) <> Notify.timeout Infinite <> Notify.urgency Normal <> Notify.appName "dc"
418 void $ Notify.display notification
419 synergyCompl = mkComplFunFromList' xPConfigMonospace ["mathw86"]
420 synergyStart host = safeSpawn "systemctl" ["--user", "start", "synergy-rtunnel@" ++ host ++ ".service"]
421 synergyStop host = safeSpawn "systemctl" ["--user", "stop", "synergy-rtunnel@" ++ host ++ ".service"]
422
423hostFromName _ = defaultHost
424
425-- muteRef :: IORef (Maybe (String, Notification))
426-- {-# NOINLINE muteRef #-}
427-- muteRef = unsafePerformIO $ newIORef Nothing
428
429-- startMute, stopMute :: String -> X ()
430-- startMute sink = liftIO $ do
431-- muted <- isJust <$> readIORef muteRef
432-- when (not muted) $ do
433-- let
434-- notification = Notify.summary "Muted" <> Notify.timeout Infinite <> Notify.urgency Normal
435-- level = "0.0dB"
436-- -- level <- runProcessWithInput "ssh" ["bragi", "cat", "/dev/shm/mix/" ++ sink ++ "/level"] ""
437-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", "0"]
438-- hPutStrLn stderr "Mute"
439-- writeIORef muteRef . Just . (level, ) =<< Notify.display notification
440-- stopMute sink = liftIO $ do
441-- let
442-- unmute (Just (level, notification)) = do
443-- hPutStrLn stderr "Unmute"
444-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", level]
445-- Notify.close notification
446-- unmute Nothing = return ()
447-- muted <- isJust <$> readIORef muteRef
448-- when muted . join . atomicModifyIORef muteRef $ (Nothing, ) . unmute
449
450wspFromMap workspaceNames = \i -> case Map.lookup i workspaceNames of
451 Just str -> show i ++ " " ++ str
452 Nothing -> show i
453
454coWspFromMap workspaceNames = \str -> case filter ((== str) . snd) $ Map.toList workspaceNames of
455 [] -> Nothing
456 [(i, _)] -> Just $ wspFromMap workspaceNames i
457 _ -> Nothing
458
459spawnModifiers = [0, controlMask, shiftMask .|. controlMask]
460spawnBindings :: XConfig layout -> (KeySym, [X ()]) -> [((KeyMask, KeySym), X ())]
461spawnBindings conf (k, cmds) = zipWith (\m cmd -> ((modm .|. mod1Mask .|. m, k), cmd)) spawnModifiers cmds
462 where
463 modm = XMonad.modMask conf
464
465manageScratchTerm = (resource =? "scratchpad" <||> resource =? "keysetup") -?> doRectFloat $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
466
467tabbedLayout t = renamed [Replace "Tabbed"] $ reflectHoriz $ t CustomShrink $ tabbedTheme
468tabbedLayoutHoriz t = renamed [Replace "Tabbed Horiz"] $ reflectVert $ t CustomShrink $ tabbedTheme
469tabbedTheme = def
470 { activeColor = "black"
471 , inactiveColor = "black"
472 , urgentColor = "black"
473 , activeBorderColor = gray
474 , inactiveBorderColor = darkGray
475 , urgentBorderColor = red
476 , activeTextColor = gray
477 , inactiveTextColor = gray
478 , urgentTextColor = gray
479 , decoHeight = 32
480 , fontName = "xft:Fira Sans:pixelsize=21"
481 }
482
483main :: IO ()
484main = do
485 arguments <- either (const []) id <$> tryIOError getArgs
486 case arguments of
487 ["--command", s] -> do
488 d <- openDisplay ""
489 rw <- rootWindow d $ defaultScreen d
490 a <- internAtom d "XMONAD_COMMAND" False
491 m <- internAtom d s False
492 allocaXEvent $ \e -> do
493 setEventType e clientMessage
494 setClientMessageEvent e rw a 32 m currentTime
495 sendEvent d rw False structureNotifyMask e
496 sync d False
497 _ -> do
498 -- batteryMon <- xfork $ monitorBattery Nothing Nothing
499 hostname <- getHostName
500 let
501 host = hostFromName hostname
502 setEnv "HOST" hostname
503 let myConfig = withHostUrgency . ewmhFullscreen . ewmh . pagerHints $ docks def
504 { manageHook = hManageHook host
505 , terminal = "alacritty"
506 , layoutHook = smartBorders . avoidStruts $ windowNavigation layout'
507 , logHook = do
508 dynamicLogString xmobarPP' >>= writeProps
509 updatePointer (99 % 100, 98 % 100) (0, 0)
510 , modMask = mod4Mask
511 , keys = \conf -> hKeysMod host conf $ myKeys' conf host
512 , workspaces = take (length numKeys) $ map wsp [1..]
513 , startupHook = setDefaultCursor xC_left_ptr
514 , normalBorderColor = darkGray
515 , focusedBorderColor = gray
516 , handleEventHook = serverModeEventHookCmd' (hCmds host) <+> keyUpEventHook
517 }
518 writeProps str = do
519 let encodeCChar = map $ fromIntegral . fromEnum
520 atoms = [ "_XMONAD_WORKSPACES"
521 , "_XMONAD_LAYOUT"
522 , "_XMONAD_TITLE"
523 ]
524 (flip mapM_) (zip atoms (lines str)) $ \(atom', content) -> do
525 ustring <- getAtom "UTF8_STRING"
526 atom <- getAtom atom'
527 withDisplay $ \dpy -> io $ do
528 root <- rootWindow dpy $ defaultScreen dpy
529 changeProperty8 dpy root atom ustring propModeReplace $ encodeCChar content
530 sync dpy True
531 wsp = hWsp host
532 -- We can´t define per-host layout modifiers because we lack dependent types
533 layout' = onHost "skadhi" ( onWorkspace (wsp 1) (Full ||| withIM (1%5) (Title "Buddy List") tabbedLayout') $
534 onWorkspace (wsp 10) Full $
535 onWorkspace (wsp 2) (Full ||| tabbedLayout') $
536 onWorkspace (wsp 5) tabbedLayout' $
537 onWorkspace (wsp 8) (withIM (1%5) (Title "Friends") tabbedLayout') $
538 defaultLayouts
539 ) $
540 onHost "vali" ( onWorkspace (wsp 2) (Full ||| tabbedLayout' ||| combineTwo (TwoPane 0.01 0.57) Full tabbedLayout') $
541 onWorkspace (wsp 3) workLayouts $
542 defaultLayouts
543 ) $
544 onHost "hel" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
545 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
546 onWorkspace (wsp 3) workLayouts $
547 onWorkspace (wsp 6) workLayouts $
548 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
549 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
550 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
551 defaultLayouts
552 ) $
553 onHost "sif" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
554 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
555 onWorkspace (wsp 3) workLayouts $
556 onWorkspace (wsp 6) workLayouts $
557 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
558 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
559 onWorkspace (wsp 8) tabbedLayout''' $
560 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
561 defaultLayouts
562 ) $
563 defaultLayouts
564 -- tabbedLayout''' = renamed [Replace "Tabbed'"] $ IfMax 1 (noBorders Full) (tabbedLayout tabbedBottomAlways)
565 tabbedLayout''' = tabbedLayout tabbedBottom
566 tabbedLayout' = tabbedLayout tabbedBottomAlways
567 tabbedLayoutHoriz' = tabbedLayoutHoriz tabbedLeftAlways
568 defaultLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW 1 (5 % 100) ||| tabbedLayout' ||| Full
569 -- workLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW (2 % 1) (5 % 100) ||| tabbedLayout' ||| Full
570 workLayouts = tabbedLayout' ||| (renamed [Replace "Combined"] $ combineTwoP (TwoPane (1 % 100) (1891 % 2560)) tabbedLayout''' (Column 1.6) (ClassName "Postman" `Or` ClassName "Emacs" `Or` ClassName "jetbrains-idea-ce" `Or` (Resource "Devtools" `And` ClassName "Firefox"))) ||| Full ||| Dwindle R CW 1 (5 % 100)
571 sqrtTwo = approxRational (sqrt 2) (1 / 2560)
572 xmobarPP' = xmobarPP { ppTitle = shorten 80
573 , ppSort = (liftM2 (.)) getSortByIndex $ return scratchpadFilterOutWorkspace
574 , ppUrgent = wrap "(" ")" . xmobarColor "#800000" ""
575 , ppHiddenNoWindows = xmobarColor "#202020" "" . wrap "(" ")"
576 , ppVisible = wrap "(" ")" . xmobarColor "#808000" ""
577 , ppCurrent = wrap "(" ")" . xmobarColor "#008000" ""
578 , ppHidden = wrap "(" ")"
579 , ppWsSep = " "
580 , ppSep = "\n"
581 }
582 withHostUrgency = case hostname of
583 "sif" -> withUrgencyHookC urgencyHook' $ def { suppressWhen = U.Never, remindWhen = Every 2 }
584 _ -> id
585 urgencyHook' window = do
586 let blinkLight = (lightHigh >> threadDelay 0.5e6) `finally` lightLow
587 where
588 lightHigh =
589 writeFile "/sys/class/leds/input0::capslock/brightness" =<< readFile "/sys/class/leds/input0::capslock/max_brightness"
590 lightLow = writeFile "/sys/class/leds/input0::capslock/brightness" "0"
591 runQuery ((resource =? "comm" <||> resource =? "Pidgin" <||> className =? "Gajim" <||> className =? "Skype" <||> className =? "Thunderbird") --> void (xfork blinkLight)) window
592 urgencyHook (BorderUrgencyHook { urgencyBorderColor = red }) window
593 shutdown :: SomeException -> IO a
594 shutdown e = do
595 let pids = [ -- batteryMon
596 ]
597 mapM_ (signalProcess sigTERM) pids
598 mapM_ (getProcessStatus False False) pids
599 throw e
600 keyUpEventHook :: Event -> X All
601 keyUpEventHook event = handle event >> return (All True)
602 where
603 handle (KeyEvent { ev_event_type = t, ev_state = m, ev_keycode = code })
604 | t == keyRelease = withDisplay $ \dpy -> do
605 s <- io $ keycodeToKeysym dpy code 0
606 mClean <- cleanMask m
607 ks <- asks $ hKeyUpKeys host . config
608 userCodeDef () $ whenJust (Map.lookup (mClean, s) ks) id
609 | otherwise = return ()
610 handle _ = return ()
611 handle shutdown $ launch myConfig =<< getDirectories
612
613secs :: Int -> Int
614secs = (* 1000000)
615
616-- monitorBattery :: Maybe BatteryContext -> Maybe Notification -> IO ()
617-- monitorBattery Nothing n = do
618-- ctx <- batteryContextNew
619-- case ctx of
620-- Nothing -> threadDelay (secs 10) >> monitorBattery Nothing n
621-- Just _ -> monitorBattery ctx n
622-- monitorBattery ctx@(Just ctx') n = do
623-- batInfo <- getBatteryInfo ctx'
624-- case batInfo of
625-- Nothing -> threadDelay (secs 1) >> monitorBattery ctx n
626-- Just batInfo -> do
627-- let n'
628-- | batteryState batInfo == BatteryStateDischarging
629-- , timeLeft <= 1200
630-- , timeLeft > 0 = Just $ summary "Discharging" <> hint "value" percentage <> urgency u <> body (duz timeLeft ++ "left")
631-- | otherwise = Nothing
632-- u
633-- | timeLeft <= 600 = Critical
634-- | timeLeft <= 1800 = Normal
635-- | otherwise = Low
636-- timeLeft = batteryTimeToEmpty batInfo
637-- percentage :: Int32
638-- percentage = round $ batteryPercentage batInfo
639-- ts = [("s", 60), ("m", 60), ("h", 24), ("d", 365), ("y", 1)]
640-- duz ms = ss
641-- where (ss, _) = foldl (\(ss, x) (s, y) -> ((if rem x y > 0 then show (rem x y) ++ s ++ " " else "") ++ ss , quot x y)) ("", ms) ts
642-- case n' of
643-- Just n' -> Notify.display (maybe mempty reuse n <> Notify.appName "monitorBattery" <> n') >>= (\n -> threadDelay (secs 2) >> monitorBattery ctx (Just n))
644-- Nothing -> threadDelay (secs 30) >> monitorBattery ctx n
645
646disableTouchpad, disableTrackpoint, enableTrackpoint, enableTouchpad :: X ()
647enableTouchpad = safeSpawn "xinput" ["enable", "SynPS/2 Synaptics TouchPad"]
648disableTouchpad = safeSpawn "xinput" ["disable", "SynPS/2 Synaptics TouchPad"]
649enableTrackpoint = safeSpawn "xinput" ["enable", "TPPS/2 IBM TrackPoint"]
650disableTrackpoint = safeSpawn "xinput" ["disable", "TPPS/2 IBM TrackPoint"]
651
652isDisabled :: String -> X Bool
653isDisabled str = do
654 out <- runProcessWithInput "xinput" ["list", str] ""
655 return $ "disabled" `isInfixOf` out
656
657
658spawnKeychain :: X ()
659spawnKeychain = do
660 home <- liftIO getHomeDirectory
661 let keys = (map ((home </>) . (".ssh/" ++)) ["id", "id-rsa"]) ++ ["6B13AA67"]
662 liftIO (maybe (return ()) (setEnv "SSH_ASKPASS") =<< findAskpass)
663 safeSpawn "keychain" . (["--agents", "gpg,ssh"] ++)=<< liftIO (filterM doesFileExist keys)
664 where
665 findAskpass = filter `liftM` readFile "/etc/zshrc"
666 filter = listToMaybe . catMaybes . map (stripPrefix "export SSH_ASKPASS=") . lines
667
668assimilateKeychain :: X ()
669assimilateKeychain = liftIO $ assimilateKeychain' >> return ()
670assimilateKeychain' = tryIOError $ do
671 -- pid <- getProcessID
672 -- tmpDir <- lookupEnv "TMPDIR"
673 -- let tmpDir' = fromMaybe "/tmp" tmpDir
674 -- tmpFile = tmpDir' </> "xmonad-keychain" ++ (show pid) ++ ".env"
675 env <- runProcessWithInput "sh" ["-c", "eval $(keychain --eval --noask --agents gpg,ssh); env"] "" -- > " ++ tmpFile] ""
676 -- env <- readFile tmpFile
677 let envVars = Map.fromList $ map (\(k, v) -> (k, tail' v)) $ map (span (/= '=')) $ envLines
678 envVars' = Map.filterWithKey (\k _ -> k `elem` transfer) envVars
679 transfer = ["SSH_AUTH_SOCK", "SSH_AGENT_PID", "GPG_AGENT_INFO"]
680 envLines = filter (elem '=') $ lines env :: [String]
681 sequence $ map (\(k, c) -> setEnv k c) $ Map.toList envVars'
682 -- removeFile tmpFile
683 where
684 tail' [] = []
685 tail' (x:xs) = xs
686
687
688numKeys = [xK_parenleft, xK_parenright, xK_braceright, xK_plus, xK_braceleft, xK_bracketright, xK_bracketleft, xK_exclam, xK_equal, xK_asterisk]
689
690instance Shrinker CustomShrink where
691 shrinkIt _ "" = [""]
692 shrinkIt s cs
693 | length cs >= 4 = cs : shrinkIt s ((reverse . drop 4 . reverse $ cs) ++ "...")
694 | otherwise = cs : shrinkIt s (init cs)
695
696xPConfig, xPConfigMonospace :: XPConfig
697xPConfig = def
698 { font = "xft:Fira Sans:pixelsize=21"
699 , height = 32
700 , bgColor = "black"
701 , fgColor = gray
702 , fgHLight = green
703 , bgHLight = "black"
704 , borderColor = gray
705 , searchPredicate = (\needle haystack -> all (`isInfixOf` map toLower haystack) . map (map toLower) $ words needle)
706 , position = Top
707 }
708xPConfigMonospace = xPConfig { font = "xft:Fira Code:pixelsize=21" }
709
710sshOverrides host = map (\h -> mkOverride { oHost = h, oCommand = moshCmd . inTmux host} )
711 [ "odin"
712 , "ymir"
713 , "surtr"
714 , "vidhar"
715 , "srv02.uniworx.de"
716 ]
717 ++
718 map (\h -> mkOverride { oHost = h, oCommand = moshCmd' "/run/current-system/sw/bin/mosh-server" . withEnv [("TERM", "xterm")] . inTmux host} )
719 [ "bragi", "bragi.asgard.yggdrasil"
720 ]
721 ++
722 map (\h -> mkOverride { oHost = h, oCommand = sshCmd . inTmux host } )
723 [ "uni2work-dev1", "srv01.uniworx.de"
724 ]
725 ++
726 map (\h -> mkOverride { oHost = h, oCommand = sshCmd . withEnv [("TERM", "xterm")] . inTmux host } )
727 [ "remote.cip.ifi.lmu.de"
728 , "uniworx3", "uniworx4", "uniworx5", "uniworxdb2"
729 , "testworx"
730 ]
731
732backlight :: (Rational -> Rational) -> X ()
733backlight f = void . xfork . liftIO $ do
734 [ _device
735 , _class
736 , read . Text.unpack -> currentBright
737 , _currentPercentage
738 , read . Text.unpack -> maximumBright
739 ] <- Text.splitOn "," . Text.pack <$> readProcess "brightnessctl" ["-m"] ""
740 let current = currentBright % maximumBright
741 new' = f current * fromIntegral maximumBright
742 new :: Integer
743 new | floor new' < 0 = 0
744 | ceiling new' > maximumBright = maximumBright
745 | new' >= maximumBright % 2 = ceiling new'
746 | otherwise = floor new'
747 callProcess "brightnessctl" ["-m", "s", show new]
748
749cycleThrough :: [Rational] -> (Rational -> Rational)
750cycleThrough opts current = fromMaybe currentOpt $ listToMaybe next'
751 where currentOpt = minimumBy (comparing $ abs . subtract current) opts
752 (_, _ : next') = break (== currentOpt) opts
753
754cycleKbLayout :: [(String, Maybe String)] -> X ()
755cycleKbLayout [] = return ()
756cycleKbLayout layouts = liftIO $ do
757 next <- (getNext . extract) `liftM` runProcessWithInput "setxkbmap" ["-query"] ""
758 let
759 args = case next of
760 (l, Just v) -> [l, v]
761 (l, Nothing) -> [l]
762 safeSpawn "setxkbmap" args
763 where
764 extract :: String -> Maybe (String, Maybe String)
765 extract str = listToMaybe $ do
766 ["layout:", l] <- str'
767 [(l, Just v) | ["variant:", v] <- str'] ++ pure (l, Nothing)
768 where
769 str' = map words $ lines str
770 getNext :: Maybe (String, Maybe String) -> (String, Maybe String)
771 getNext = maybe (head layouts) getNext'
772 getNext' x = case elemIndex x layouts of
773 Nothing -> getNext Nothing
774 Just i -> layouts !! ((i + 1) `mod` length layouts)
775
776mpvAll' :: MpvCommand -> IO [MpvResponse]
777mpvAll' = mpvAll "/var/media/.mpv-ipc"
778
779mpvOne' :: MpvCommand -> IO (Maybe MpvResponse)
780mpvOne' = mpvOne "/var/media/.mpv-ipc"
781
782mediaMpv :: MpvCommand -> X ()
783mediaMpv cmd = void . xfork $ print =<< mpvAll' cmd
784
785mediaMpvTogglePause :: X ()
786mediaMpvTogglePause = void . xfork $ do
787 paused <- mapM mpvResponse <=< mpvAll' $ MpvGetProperty "pause"
788 if
789 | and paused -> print <=< mpvAll' $ MpvSetProperty "pause" False
790 | otherwise -> print <=< mpvOne' $ MpvSetProperty "pause" True
791
792myKeys' conf host = Map.fromList $
793 -- launch a terminal
794 [ ((modm, xK_Return), spawn $ (XMonad.terminal conf) ++ " -e tmux")
795 , ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
796
797 -- launch dmenu
798 --, ((modm, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
799 , ((modm, xK_d ), shellPrompt "Run: " xPConfigMonospace)
800 , ((modm .|. shiftMask, xK_d ), prompt "Run in Terminal: " ("alacritty" ++ " -e") xPConfigMonospace)
801 , ((modm, xK_at ), sshPrompt (sshOverrides . Just $ hName host) xPConfigMonospace)
802
803 -- close focused window
804 , ((modm .|. shiftMask, xK_q ), kill)
805 , ((modm .|. controlMask .|. shiftMask, xK_q ), spawn "xkill")
806
807 -- Rotate through the available layout algorithms
808 , ((modm, xK_space ), sendMessage NextLayout)
809
810 -- Reset the layouts on the current workspace to default
811 , ((modm .|. controlMask, xK_r ), (setLayout $ XMonad.layoutHook conf) >> refresh)
812
813 -- Resize viewed windows to the correct size
814 , ((modm, xK_r ), refresh)
815
816 -- Move focus to the next window
817 , ((modm, xK_t ), windows W.focusDown)
818
819 -- Move focus to the previous window
820 , ((modm, xK_n ), windows W.focusUp )
821
822 -- Move focus to the master window
823 , ((modm, xK_m ), windows W.focusMaster )
824
825 -- Swap the focused window and the master window
826 , ((modm .|. shiftMask, xK_m ), windows W.swapMaster)
827
828 -- Swap the focused window with the next window
829 , ((modm .|. shiftMask, xK_t ), windows W.swapDown )
830
831 -- Swap the focused window with the previous window
832 , ((modm .|. shiftMask, xK_n ), windows W.swapUp )
833
834 -- Swap the focused window with the previous window
835 , ((modm .|. shiftMask .|. controlMask, xK_m), sendMessage SwapWindow)
836
837 , ((modm, xK_Right), sendMessage $ Go R)
838 , ((modm, xK_Left ), sendMessage $ Go L)
839 , ((modm, xK_Up ), sendMessage $ Go U)
840 , ((modm, xK_Down ), sendMessage $ Go D)
841 , ((modm .|. shiftMask , xK_Right), sendMessage $ Move R)
842 , ((modm .|. shiftMask , xK_Left ), sendMessage $ Move L)
843 , ((modm .|. shiftMask , xK_Up ), sendMessage $ Move U)
844 , ((modm .|. shiftMask , xK_Down ), sendMessage $ Move D)
845 -- , ((modm .|. controlMask, xK_Right), withFocused $ keysMoveWindow (10, 0))
846 -- , ((modm .|. controlMask, xK_Left ), withFocused $ keysMoveWindow (-10, 0))
847 -- , ((modm .|. controlMask, xK_Up ), withFocused $ keysMoveWindow (0, -10))
848 -- , ((modm .|. controlMask, xK_Down ), withFocused $ keysMoveWindow (0, 10))
849 -- Shrink the master area
850 , ((modm, xK_h ), sendMessage Shrink)
851
852 -- Expand the master area
853 , ((modm, xK_s ), sendMessage Expand)
854
855 -- Push window back into tiling
856 , ((modm .|. shiftMask, xK_space ), withFocused $ windows . W.sink)
857 , ((modm, xK_BackSpace), focusUrgent)
858 , ((modm .|. shiftMask, xK_BackSpace), clearUrgents)
859
860 -- Increment the number of windows in the master area
861 , ((modm , xK_comma ), sendMessage (IncMasterN 1))
862
863 -- Deincrement the number of windows in the master area
864 , ((modm , xK_period), sendMessage (IncMasterN (-1)))
865
866 , ((0, xF86XK_AudioRaiseVolume), safeSpawn "pamixer" ["-i", "2"])
867 , ((0, xF86XK_AudioLowerVolume), safeSpawn "pamixer" ["-d", "2"])
868 , ((0, xF86XK_AudioMute), safeSpawn "pamixer" ["-t"])
869 , ((0, xF86XK_AudioPause), mediaMpv $ MpvSetProperty "pause" False)
870 , ((0, {-xF86XK_AudioMicMute-} 269025202), safeSpawn "pulseaudio-ctl" ["mute-input"])
871 , ((0, xF86XK_AudioPlay), mediaMpvTogglePause)
872 , ((0, xK_Print), do
873 home <- liftIO getHomeDirectory
874 unGrab
875 safeSpawn "scrot" ["-s", "-F", home </> "screenshots" </> "%Y-%m-%dT%H:%M:%S.png", "-e", "xclip -selection clipboard -t image/png -i $f"]
876 )
877 , ((modm .|. mod1Mask, xK_space), mediaMpvTogglePause)
878
879 -- , ((0, xF86XK_MonBrightnessDown), backlight . cycleThrough $ reverse brCycle)
880 -- , ((0, xF86XK_MonBrightnessUp ), backlight $ cycleThrough brCycle)
881 , ((modm .|. shiftMask , xK_b), backlight . cycleThrough $ reverse brCycle)
882 , ((modm .|. shiftMask .|. controlMask, xK_b), backlight $ cycleThrough brCycle)
883
884 , ((modm , xK_Escape), cycleKbLayout (hKbLayouts host))
885 , ((modm .|. controlMask, xK_Escape), safeSpawn "setxkbmap" $ fst (head $ hKbLayouts host) : maybeToList (snd . head $ hKbLayouts host))
886
887 -- Toggle the status bar gap
888 -- Use this binding with avoidStruts from Hooks.ManageDocks.
889 -- See also the statusBar function from Hooks.DynamicLog.
890 --
891 , ((modm , xK_b ), sendMessage ToggleStruts)
892
893 , ((modm .|. shiftMask, xK_p ), safeSpawn "playerctl" ["-a", "pause"])
894
895 -- Quit xmonad
896 , ((modm .|. shiftMask, xK_e ), io (exitWith ExitSuccess))
897
898 -- Restart xmonad
899 -- , ((modm .|. shiftMask .|. controlMask, xK_r ), void . xfork $ recompile False >>= flip when (safeSpawn "xmonad" ["--restart"]))
900 , ((modm .|. shiftMask, xK_r ), void . liftIO $ executeFile "xmonad" True [] Nothing)
901 , ((modm .|. shiftMask, xK_l ), void . xfork $ do
902 sessId <- getEnv "XDG_SESSION_ID"
903 safeSpawn "loginctl" ["lock-session", sessId]
904 )
905 , ((modm .|. shiftMask, xK_s ), safeSpawn "systemctl" ["suspend"])
906 , ((modm .|. shiftMask, xK_h ), inputPromptWithCompl xPConfigMonospace "systemctl" powerActCompl ?+ powerAct)
907 , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible
908 , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
909 , ((modm .|. shiftMask, xK_g ), windowPrompt xPConfig Goto wsWindows)
910 , ((modm , xK_g ), windowPrompt xPConfig Bring allWindows)
911 ]
912 ++
913
914 --
915 -- mod-[1..9], Switch to workspace N
916 --
917 -- mod-[1..9], Switch to workspace N
918 -- mod-shift-[1..9], Move client to workspace N
919 --
920 [((m .|. modm, k), windows $ f i)
921 | (i, k) <- zip (XMonad.workspaces conf) $ numKeys
922 , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]
923 ]
924 ++
925 [((m .|. modm .|. controlMask, k), void . runMaybeT $
926 MaybeT (P.getScreen def i) >>= MaybeT . screenWorkspace >>= lift . windows . f
927 )
928 | (i, k) <- zip (hScreens host) [xK_g, xK_c, xK_r, xK_l]
929 , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]
930 ]
931 where
932 modm = XMonad.modMask conf
933
934 brCycle = [0, 1 % 500, 1 % 250, 1 % 100, 1 % 10, 1 % 4, 1 % 2, 3 % 4, 1]
935
936 powerActWords = ["poweroff", "reboot", "hibernate", "suspend"]
937 powerActCompl = mkComplFunFromList' xPConfigMonospace powerActWords
938 powerAct act | act `elem` powerActWords = safeSpawn "systemctl" $ pure act
939 | otherwise = return ()