summaryrefslogtreecommitdiff
path: root/accounts/gkleen@sif
diff options
context:
space:
mode:
Diffstat (limited to 'accounts/gkleen@sif')
-rw-r--r--accounts/gkleen@sif/autorandr-profiles/bstr.nix21
-rw-r--r--accounts/gkleen@sif/autorandr-profiles/def.nix13
-rw-r--r--accounts/gkleen@sif/backup-patterns26
-rw-r--r--accounts/gkleen@sif/default.nix251
-rw-r--r--accounts/gkleen@sif/dunst-settings.nix67
-rw-r--r--accounts/gkleen@sif/emacs.el178
-rw-r--r--accounts/gkleen@sif/firefox-chrome.css25
-rw-r--r--accounts/gkleen@sif/firefox-content.css12
-rwxr-xr-xaccounts/gkleen@sif/scripts/mute.zsh18
-rw-r--r--accounts/gkleen@sif/ssh-hosts.nix229
-rw-r--r--accounts/gkleen@sif/store.kdbx.lftp6
-rw-r--r--accounts/gkleen@sif/systemd.nix94
-rw-r--r--accounts/gkleen@sif/xmobar/default.nix7
-rw-r--r--accounts/gkleen@sif/xmobar/nixpkgs.nix9
-rw-r--r--accounts/gkleen@sif/xmobar/package.yaml13
-rw-r--r--accounts/gkleen@sif/xmobar/shell.nix28
-rw-r--r--accounts/gkleen@sif/xmobar/stack.nix17
-rw-r--r--accounts/gkleen@sif/xmobar/stack.yaml10
-rw-r--r--accounts/gkleen@sif/xmobar/stack.yaml.lock12
-rw-r--r--accounts/gkleen@sif/xmobar/stackage.nix31
-rw-r--r--accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix13
-rw-r--r--accounts/gkleen@sif/xmobar/xmobar.hs52
-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.yaml30
-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.hs902
-rw-r--r--accounts/gkleen@sif/xresources.nix46
-rw-r--r--accounts/gkleen@sif/zshrc410
35 files changed, 3151 insertions, 0 deletions
diff --git a/accounts/gkleen@sif/autorandr-profiles/bstr.nix b/accounts/gkleen@sif/autorandr-profiles/bstr.nix
new file mode 100644
index 00000000..527f8321
--- /dev/null
+++ b/accounts/gkleen@sif/autorandr-profiles/bstr.nix
@@ -0,0 +1,21 @@
1{
2 fingerprint = {
3 "eDP-1-1" = "00ffffffffffff004c83414100000000131d0104b5221378029491ae513eb7240b505400000001010101010101010101010101010101f0d40040f17018803020440058c21000001bf0d40040f17018803020440058c21000001b0000000f00ff093cff093c2c800000000000000000fe0041544e413536575230382d3020011502030f00e3058000e6060501736d0700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab";
4 "DP-1.3" = "00ffffffffffff000469a3289bdd00000b190104a53e22783a1cb5a3574fa0270d5054bfef00d1c0814081809500b300714f81c0010122cc0050f0703e80181035006d552100001a04740030f2705a80b0588a006d552100001a000000fd001e5018a03c041100f0f838f03c000000fc0041535553205042323837510a2001a8020327714f0102031112130414051f900e0f1d1e23091707830100006a030c0010000078200000565e00a0a0a02950302035006d552100001ee26800a0a0402e60302036006d552100001a011d00bc52d01e20b82855406d552100001e8c0ad090204031200c4055006d55210000180000000000000000000000000000000064";
5 };
6 config = {
7 "DP-1.3" = {
8 enable = true;
9 primary = true;
10 position = "3840x0";
11 rate = "60";
12 mode = "3840x2160";
13 };
14 eDP-1-1 = {
15 enable = true;
16 primary = false;
17 position = "0x0";
18 mode = "3840x2160";
19 };
20 };
21}
diff --git a/accounts/gkleen@sif/autorandr-profiles/def.nix b/accounts/gkleen@sif/autorandr-profiles/def.nix
new file mode 100644
index 00000000..304b4afe
--- /dev/null
+++ b/accounts/gkleen@sif/autorandr-profiles/def.nix
@@ -0,0 +1,13 @@
1{
2 fingerprint = {
3 eDP-1-1 = "00ffffffffffff004c83414100000000131d0104b5221378029491ae513eb7240b505400000001010101010101010101010101010101f0d40040f17018803020440058c21000001bf0d40040f17018803020440058c21000001b0000000f00ff093cff093c2c800000000000000000fe0041544e413536575230382d3020011502030f00e3058000e6060501736d0700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ab";
4 };
5 config = {
6 eDP-1-1 = {
7 enable = true;
8 primary = true;
9 position = "0x0";
10 mode = "3840x2160";
11 };
12 };
13}
diff --git a/accounts/gkleen@sif/backup-patterns b/accounts/gkleen@sif/backup-patterns
new file mode 100644
index 00000000..4436887b
--- /dev/null
+++ b/accounts/gkleen@sif/backup-patterns
@@ -0,0 +1,26 @@
1R .
2
3- pp:.cache
4- pp:.antigen
5+ pf:.cabal/config
6- pp:.cabal
7+ pf:.stack/config.yaml
8- pp:.stack
9- pp:.nox
10- pp:.local/share/Steam
11- sh:**/.stack-work*
12- sh:**/.~lock.*
13- sh:**/.gup
14+ pf:.config/pulse/default.pa
15- pp:.config/pulse
16- pp:.config/Zulip
17- pp:.config/discord
18- pp:.zplug/log
19- pp:.zplug/cache
20- pp:.compose-cache
21- pp:.undo-tree
22- pp:.saves
23- pp:.mozilla
24- pp:Downloads/tmp
25- pp:secret
26- pp:mail \ No newline at end of file
diff --git a/accounts/gkleen@sif/default.nix b/accounts/gkleen@sif/default.nix
new file mode 100644
index 00000000..440efa4e
--- /dev/null
+++ b/accounts/gkleen@sif/default.nix
@@ -0,0 +1,251 @@
1{ flake, userName, pkgs, customUtils, lib, config, ... }@inputs:
2let
3 cfg = config.home-manager.users.${userName};
4 xmonad = import ./xmonad pkgs.haskellPackages;
5 emacsclientDesktopItem = pkgs.makeDesktopItem {
6 name = "emacsclient";
7 genericName = "Text Editor";
8 desktopName = "emacsclient";
9 icon = "emacs";
10 mimeType = "text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;";
11 exec = "${config.home-manager.users.${userName}.programs.emacs.package}/bin/emacsclient -a \"\" %F";
12 };
13 emacsScratch = pkgs.stdenv.mkDerivation rec {
14 pname = "scratch";
15 version = "0077334cc299aa7885f804d88f52cdb1b35caf71";
16
17 src = pkgs.fetchFromGitHub {
18 owner = "ffevotte";
19 repo = "scratch.el";
20 rev = version;
21 sha256 = "sha256-FUkKJ+1COGzgllzzv51yUIjMZI6slOFVExdwWl2ZEBA=";
22 };
23
24 phases = [ "installPhase" ];
25
26 installPhase = ''
27 mkdir -p $out/share/emacs/site-lisp
28 cp $src/scratch.el $out/share/emacs/site-lisp/default.el
29 '';
30 };
31 muteScript = pkgs.stdenv.mkDerivation {
32 name = "mute";
33 src = ./scripts/mute.zsh;
34
35 buildInputs = with pkgs; [ makeWrapper ];
36
37 phases = [ "installPhase" ];
38
39 installPhase = ''
40 mkdir -p $out/bin
41 install -m 0755 $src $out/bin/mute
42 wrapProgram $out/bin/mute \
43 --prefix PATH : ${pkgs.zsh}/bin \
44 --prefix PATH : ${pkgs.findutils}/bin \
45 --prefix PATH : ${pkgs.util-linux}/bin \
46 --prefix PATH : ${pkgs.coreutils}/bin \
47 --prefix PATH : ${pkgs.pulseaudio}/bin
48 '';
49 };
50in {
51 imports = with flake.nixosModules.userProfiles.${userName}; [
52 mpv
53 ];
54
55 home-manager.users.${userName} = {
56 programs = {
57 ssh = {
58 matchBlocks = import ./ssh-hosts.nix; # customUtils.nixImport { dir = ./ssh-hosts; };
59 extraConfig = ''
60 Match host uniworx3.ifi.lmu.de,uniworx4.ifi.lmu.de,uniworx5.ifi.lmu.de,uni2workgw.ifi.lmu.de,blackbeard.tcs.ifi.lmu.de,gitlab2.rz.ifi.lmu.de,oregon.tcs.ifi.lmu.de !exec "nc -z -w 1 %h %p &>/dev/null"
61 ProxyJump remote.cip.ifi.lmu.de
62
63 Host *
64 '';
65 };
66
67 emacs = {
68 enable = true;
69 extraPackages = epkgs: with epkgs; [
70 evil evil-dvorak undo-tree magit haskell-mode
71 nix-mode yaml-mode json-mode shakespeare-mode
72 smart-mode-line highlight-parentheses highlight-symbol
73 notmuch ag sass-mode lua-mode fira-code-mode use-package
74 use-package-ensure-system-package git-gutter emacsScratch
75 ];
76 };
77 firefox = {
78 enable = true;
79 profiles.default = {
80 settings = {
81 "layout.css.devPixelsPerPx" = "1.75";
82 "browser.tabs.drawInTitlebar" = false;
83 "toolkit.legacyUserProfileCustomizations.stylesheets" = true;
84 "dom.security.https_only_mode" = true;
85 };
86 };
87 };
88
89 urxvt = {
90 enable = true;
91 package = pkgs.rxvt_unicode-with-plugins;
92 fonts = [ "xft:FiraCode Nerd Font Mono:style=Regular:pixelsize=21" ];
93 scroll = {
94 lines = 0;
95 bar.enable = false;
96 };
97 extraConfig = {
98 urgentOnBell = false;
99 print-pipe = "cat >/dev/null";
100 perl-ext-common = "52-osc,url-select";
101 "url-select.launcher" = "firefox";
102 "url-select.underline" = true;
103 };
104 keybindings = {
105 "M-u" = "perl:url-select:select_next";
106 };
107 };
108
109 zathura = {
110 enable = true;
111 package = pkgs.zathura.override { useMupdf = false; };
112 };
113
114 mpv.config = {
115 demuxer-max-bytes = 1073741824;
116 demuxer-max-back-bytes = 268435456;
117 };
118
119 autorandr = {
120 enable = true;
121 hooks.postswitch = {
122 # "restart-compton" = "${pkgs.systemd}/bin/systemctl --user try-restart picom";
123 "restart-trays" = ''
124 ${pkgs.coreutils}/bin/sleep 5
125 ${pkgs.systemd}/bin/systemctl --user try-restart trayer xmobar
126 '';
127 };
128 profiles = customUtils.nixImport { dir = ./autorandr-profiles; };
129 };
130
131 zsh.initExtra = "source ${./zshrc}";
132 zsh.dirHashes = {
133 u2w = "$HOME/projects/uni2work";
134 docs = "$HOME/documents";
135 dl = "$HOME/Downloads";
136 flk = "$HOME/config/nixos-flakes";
137 fsk-timi = "$HOME/projects/21s/fsk-timi";
138 };
139
140 obs-studio = {
141 enable = true;
142 plugins = with pkgs; [obs-v4l2sink];
143 };
144 };
145
146 services = {
147 dunst = {
148 settings = import ./dunst-settings.nix inputs;
149 iconTheme = cfg.gtk.iconTheme;
150 enable = true;
151 };
152 emacs.enable = true;
153 gpg-agent = {
154 enable = true;
155 enableSshSupport = true;
156 extraConfig = ''
157 pinentry-program ${pkgs.pinentry-gtk2}/bin/pinentry
158 grab
159 '';
160 };
161 pasystray.enable = true;
162 udiskie = {
163 enable = true;
164 automount = false;
165 };
166 unclutter = {
167 enable = true;
168 timeout = 5;
169 };
170 network-manager-applet.enable = true;
171 blueman-applet.enable = true;
172
173 sxhkd = {
174 enable = true;
175 keybindings = {
176 "button8" = "${muteScript}/bin/mute unmute";
177 "@button8" = "${muteScript}/bin/mute mute";
178 "button9" = "${pkgs.pulseaudio}/bin/pacmd set-sink-mute @DEFAULT_SINK@ 1";
179 "@button9" = "${pkgs.pulseaudio}/bin/pacmd set-sink-mute @DEFAULT_SINK@ 0";
180 };
181 };
182 };
183
184 gtk = {
185 enable = true;
186 font.name = "DejaVu Sans 6";
187 theme = {
188 package = pkgs.equilux-theme;
189 name = "Equilux-compact";
190 };
191 iconTheme = {
192 package = pkgs.paper-icon-theme;
193 name = "Paper";
194 };
195 };
196
197 xsession = {
198 enable = true;
199
200 windowManager.command = "${xmonad}/bin/xmonad";
201
202 initExtra = let
203 lockScript = pkgs.writeScript "lock" ''
204 #!${pkgs.stdenv.shell}
205 ${pkgs.playerctl}/bin/playerctl -a pause
206 exec ${pkgs.xsecurelock}/bin/xsecurelock
207 '';
208 in ''
209 ${pkgs.coreutils}/bin/env XSECURELOCK_WANT_FIRST_KEYPRESS=1 XSECURELOCK_DIM_ALPHA=1 ${pkgs.xss-lock}/bin/xss-lock -l -n ${pkgs.xsecurelock}/libexec/xsecurelock/dimmer -- ${lockScript} &
210 ${pkgs.xorg.xinput}/bin/xinput disable 'Synaptics TM3512-010'
211 ${pkgs.xorg.xset}/bin/xset s 590 10
212 '';
213 };
214
215 xresources.properties = import ./xresources.nix;
216
217 home = {
218 packages = with pkgs; [
219 fira-code powerline-fonts nerdfonts pavucontrol keepassxc
220 youtube-dl sxiv xclip mumble pulseaudio-ctl libnotify synergy
221 xorg.xbacklight screen-message pidgin-with-plugins
222 google-play-music-desktop-player qt5ct playerctl evince
223 thunderbird zulip zoom-us steam steam-run wireshark skype
224 virt-manager rclone cached-nix-shell xournal discord xmonad
225 worktime fira-code-symbols emacsclientDesktopItem libreoffice
226 xournalpp
227 ];
228
229 file = {
230 ".emacs".source = ./emacs.el;
231 ".backup-munin".source = ./backup-patterns;
232 ".mozilla/firefox/default/chrome/userChrome.css".source = ./firefox-chrome.css;
233 ".mozilla/firefox/default/chrome/userContent.css".source = ./firefox-content.css;
234 };
235
236 sessionVariables = {
237 GDK_SCALE = 96.0 / 282.0;
238 QT_AUTO_SCREEN_SCALE_FACTOR = 1;
239 QT_QPA_PLATFORMTHEME = "qt5ct";
240 };
241
242 extraProfileCommands = ''
243 export XDG_DATA_DIRS="${pkgs.gsettings-desktop-schemas}/share/gsettings-schemas/${pkgs.gsettings-desktop-schemas.name}:${pkgs.gtk3}/share/gsettings-schemas/${pkgs.gtk3.name}''${XDG_DATA_DIRS:+:''${XDG_DATA_DIRS}}"
244 '';
245 };
246
247 fonts.fontconfig.enable = true;
248
249 systemd.user = import ./systemd.nix inputs;
250 };
251}
diff --git a/accounts/gkleen@sif/dunst-settings.nix b/accounts/gkleen@sif/dunst-settings.nix
new file mode 100644
index 00000000..8abdfc5a
--- /dev/null
+++ b/accounts/gkleen@sif/dunst-settings.nix
@@ -0,0 +1,67 @@
1{ pkgs, ... }:
2{
3 global = {
4 font = "Monospace 6";
5 markup = "full";
6 format = "<i>%s</i> %p\\n%b";
7 alignment = "left";
8 geometry = "1216x10-32+64";
9 shrink = true;
10 monitor = 0;
11 follow = "none";
12 padding = 6;
13 horizontal_padding = 6;
14 separator_height = 1;
15 separator_color = "frame";
16 idle_threshold = 0;
17
18 transparency = 10;
19
20 frame_width = 1;
21 frame_color = "#999999";
22
23 word_wrap = true;
24 show_age_threshold = 15;
25 show_indicators = false;
26 icon_position = "right";
27 sort = false;
28 sticky_history = false;
29
30 dmenu = "${pkgs.dmenu}/bin/dmenu";
31 };
32 shortcuts = {
33 close = "ctrl+space";
34 close_all = "ctrl+shift+space";
35 history = "ctrl+comma";
36 context = "ctrl+period";
37 };
38 urgency_low = {
39 background = "#000000";
40 foreground = "#999999";
41 timeout = 5;
42 };
43 urgency_normal = {
44 background = "#000000";
45 foreground = "#ffffff";
46 timeout = 15;
47 };
48 urgency_critical = {
49 background = "#900000";
50 foreground = "#ffffff";
51 timeout = 0;
52 };
53 pulseaudio-ctl = {
54 summary = "Volume *";
55 body = "Current is *";
56 set_stack_tag = "volume";
57 history_ignore = true;
58 };
59 mail = {
60 appname = "notmuch";
61 timeout = 0;
62 };
63 zulip = {
64 appname = "Zulip";
65 timeout = 0;
66 };
67}
diff --git a/accounts/gkleen@sif/emacs.el b/accounts/gkleen@sif/emacs.el
new file mode 100644
index 00000000..b22c00f5
--- /dev/null
+++ b/accounts/gkleen@sif/emacs.el
@@ -0,0 +1,178 @@
1(menu-bar-mode -1)
2(scroll-bar-mode -1)
3(tool-bar-mode -1)
4
5(setq inhibit-startup-message t)
6(defalias 'yes-or-no-p 'y-or-n-p)
7
8(set-face-attribute 'default nil :font "FiraCode Nerd Font Mono" :height 49)
9
10(require 'package)
11(setq package-archives nil)
12(package-initialize)
13(require 'use-package)
14(use-package use-package-ensure-system-package :ensure t)
15
16(require 'evil)
17(evil-mode 1)
18
19(global-subword-mode)
20(global-undo-tree-mode)
21(global-fira-code-mode)
22
23(evil-set-undo-system 'undo-tree)
24
25(global-set-key (kbd "RET") 'newline-and-indent)
26(global-set-key (kbd "M-g") 'magit-status)
27(global-set-key (kbd "M-?") 'vc-git-grep)
28
29(require 'git-gutter)
30(global-git-gutter-mode t)
31(custom-set-variables '(git-gutter:update-interval 2))
32(custom-set-variables '(git-gutter:hide-gutter t))
33
34;; (require 'scratch)
35(global-set-key (kbd "C-x B") 'scratch-create)
36(setq initial-major-mode 'scratch-mode)
37(setq initial-scratch-message "")
38
39(global-set-key (kbd "C-x K") 'kill-current-buffer)
40
41(setq backup-directory-alist `(("." . "~/.saves")))
42(setq undo-tree-history-directory-alist `(("." . "~/.undo")))
43(setq delete-old-versions t
44 kept-new-versions 6
45 kept-old-versions 2
46 version-control t)
47
48(setq undo-tree-visualizer-timestamps t
49 undo-tree-visualizer-diff t
50 ;; 10X bump of the undo limits to avoid issues with premature
51 ;; Emacs GC which truncages the undo history very aggresively
52 undo-limit 800000
53 undo-strong-limit 12000000
54 undo-outer-limit 120000000)
55
56(add-hook 'haskell-mode-hook 'haskell-indentation-mode)
57(add-hook 'haskell-mode-hook 'subword-mode)
58(add-hook 'haskell-mode-hook 'highlight-symbol-mode)
59(add-hook 'haskell-mode-hook 'highlight-paretheses-mode)
60
61(add-hook 'js-mode-hook 'highlight-symbol-mode)
62(add-hook 'js-mode-hook 'highlight-parentheses-mode)
63(defun my-js-mode-hook ()
64 "Custom `js-mode' behaviours."
65 (setq js-indent-level 2)
66 )
67(add-hook 'js-mode-hook 'my-js-mode-hook)
68
69(setq undo-tree-auto-save-history t)
70
71(defvar expand-file-name-custom-tilde-alist '(("u2w-dev1" . "/ssh:uni2work-dev1:/home/gkleen/projects/uni2work")))
72(defun my/add-to-tilde-alist (hash)
73 (let* ((tilde:dir (split-string hash "="))
74 (tilde (car tilde:dir))
75 (dir (cadr tilde:dir)))
76 (push (cons tilde dir) expand-file-name-custom-tilde-alist)))
77(mapc #'my/add-to-tilde-alist
78 (split-string (with-output-to-string
79 (call-process "zsh" nil standard-output nil "-ic" "hash -d"))
80 "\n" t))
81
82(defadvice expand-file-name (before expand-file-name-custom-tilde
83 (name &optional default-directory)
84 activate compile)
85 "User-defined expansions for ~NAME in file names."
86 (save-match-data
87 (when (string-match "\\`\\(\\(.*/\\)?~\\([^:/]+\\)\\)/" name)
88 (let ((replacement (assoc (match-string 3 name) expand-file-name-custom-tilde-alist)))
89 (when replacement
90 (setq name (replace-match (cdr replacement) t t name 1)))))))
91
92(setq notmuch-address-internal-completion '(received nil))
93(setq notmuch-always-prompt-for-sender t)
94(setq notmuch-command "notmuch-ssh")
95(setq notmuch-crypto-process-mime t)
96(setq notmuch-draft-tags '("+draft" "-inbox"))
97(setq notmuch-fcc-dirs nil)
98(setq notmuch-hello-sections '(notmuch-hello-insert-header notmuch-hello-insert-saved-searches))
99(setq notmuch-hello-thousands-separator " ")
100(setq notmuch-identities '("gkleen@yggdrasil.li" "g@141.li" "kleen@cip.ifi.lmu.de" "Gregor.Kleen@stud.ifi.lmu.de" "G.Kleen@campus.lmu.de" "G.Kleen@lmu.de" "gregor.kleen@ifi.lmu.de" "uni2work@ifi.lmu.de" "gregor@kleen.li"))
101(setq notmuch-message-headers '("Subject" "To" "Cc" "Date"))
102(setq notmuch-message-replied-tags '("+replied" "-unread" "-inbox"))
103(setq notmuch-saved-searches
104 (quote
105 ((:name "inbox" :query "tag:inbox" :key "i")
106 (:name "unread" :query "tag:unread AND tag:inbox" :key "u")
107 (:name "drafts" :query "tag:draft" :key "d")
108 (:name "all mail" :query "date:month.." :key "a" :count-query "*")
109 (:name "sent" :query "is:sent" :key "s" :count-query "is:sent")
110 )))
111(setq notmuch-search-oldest-first nil)
112(setq notmuch-show-all-tags-list t)
113(setq notmuch-show-logo nil)
114
115(setq send-mail-function 'sendmail-send-it)
116(setq mail-envelope-from 'header)
117(setq mail-specify-envelope-from 't)
118(setq mail-default-headers nil)
119(setq message-default-headers "")
120(setq message-default-mail-headers "")
121(setq message-sendmail-envelope-from 'header)
122
123(setq highlight-symbol-idle-delay 0)
124
125(setq indent-tabs-mode nil)
126
127(setq ido-enable-flex-matching t)
128(setq ido-everywhere t)
129(ido-mode 1)
130
131(setq tramp-default-method "ssh")
132(customize-set-variable 'tramp-use-ssh-controlmaster-options nil)
133
134(setq direnv-enabled-hosts '("uni2work-dev1"))
135
136(defun tramp-sh-handle-start-file-process@my-direnv (args)
137 "Enable Direnv for hosts in `direnv-enabled-hosts'."
138 (with-parsed-tramp-file-name (expand-file-name default-directory) nil
139 (if (member host direnv-enabled-hosts)
140 (pcase-let ((`(,name ,buffer ,program . ,args) args))
141 `(,name
142 ,buffer
143 "direnv"
144 "exec"
145 ,localname
146 ,program
147 ,@args))
148 args)))
149
150(with-eval-after-load "tramp-sh"
151 (advice-add 'tramp-sh-handle-start-file-process
152 :filter-args #'tramp-sh-handle-start-file-process@my-direnv))
153
154(setq mail-host-address "sif.midgard.yggdrasil")
155(setq user-full-name "Gregor Kleen")
156
157(defun tell-emacsclients-for-buffer-to-die ()
158 "Sends error exit command to every client for the current buffer."
159 (interactive)
160 (dolist (proc server-buffer-clients)
161 (server-send-string proc "-error die")))
162
163(defun kill-buffer-with-special-emacsclient-handling ()
164 "Wrapper around kill-buffer that ensures tell-emacsclients-for-buffer-to-die is on the hooks"
165 (interactive)
166 (add-hook 'kill-buffer-hook 'tell-emacsclients-for-buffer-to-die nil t)
167 (kill-buffer))
168
169;; (global-set-key (kbd "C-x k") 'kill-buffer)
170
171(defun install-emacsclient-wrapped-kill-buffer ()
172 "Installs wrapped kill-buffer with special emacsclient handling.
173Best not to install it unconditionally because the server is not
174necessarily running."
175 (interactive)
176 (global-set-key (kbd "C-x k") 'kill-buffer-with-special-emacsclient-handling))
177
178(add-hook 'server-switch-hook 'install-emacsclient-wrapped-kill-buffer)
diff --git a/accounts/gkleen@sif/firefox-chrome.css b/accounts/gkleen@sif/firefox-chrome.css
new file mode 100644
index 00000000..2d359771
--- /dev/null
+++ b/accounts/gkleen@sif/firefox-chrome.css
@@ -0,0 +1,25 @@
1@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
2
3* {
4 font-size:12px;
5}
6
7#sidebar {
8 min-width:20em !important;
9 max-width:20em !important;
10 border-right:1px solid rgba(30,30,30) !important;
11}
12
13#sidebar-header:nth-last-child(2) > *:last-child {
14 visibility: collapse;
15}
16
17#sidebar-splitter {
18 visibility: collapse;
19}
20
21#toolbar-menubar[inactive="true"] + #TabsToolbar {
22 visibility: collapse !important;
23}
24
25#sidebar-box[sidebarcommand="tabcenter-reborn_ariasuni-sidebar-action"] #sidebar-header { visibility: collapse !important; }
diff --git a/accounts/gkleen@sif/firefox-content.css b/accounts/gkleen@sif/firefox-content.css
new file mode 100644
index 00000000..3ac53a9d
--- /dev/null
+++ b/accounts/gkleen@sif/firefox-content.css
@@ -0,0 +1,12 @@
1@-moz-document url("about:blank") {
2 html {
3 background-color: rgb(40,40,40);
4 }
5}
6
7@-moz-document url(about:newtab) {
8 body, #newtab-customize-overlay {
9 background-color: rgb(40,40,40) !important;
10 color: rgb(166,166,166) !important;
11 }
12}
diff --git a/accounts/gkleen@sif/scripts/mute.zsh b/accounts/gkleen@sif/scripts/mute.zsh
new file mode 100755
index 00000000..1b30ad67
--- /dev/null
+++ b/accounts/gkleen@sif/scripts/mute.zsh
@@ -0,0 +1,18 @@
1#!/usr/bin/env zsh
2
3lockFile=~/.mute.flock
4
5case $1 in
6 mute)
7 (
8 flock -n 9 || exit 1
9 sleep 0.2
10 pacmd set-source-mute @DEFAULT_SOURCE@ 1
11 ) 9<>${lockFile} &
12 ;;
13 unmute)
14 set -o pipefail
15 while fuser ${lockFile} 2>/dev/null | cut -d ':' -f 2- | xargs -r -- kill; do sleep 0.001; done
16 pacmd set-source-mute @DEFAULT_SOURCE@ 0
17 ;;
18esac
diff --git a/accounts/gkleen@sif/ssh-hosts.nix b/accounts/gkleen@sif/ssh-hosts.nix
new file mode 100644
index 00000000..58e4957f
--- /dev/null
+++ b/accounts/gkleen@sif/ssh-hosts.nix
@@ -0,0 +1,229 @@
1{
2 "git.ymir" =
3 { hostname = "ymir.yggdrasil.li";
4 user = "gitolite";
5 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
6 };
7 "git.yggdrasil.li" =
8 { user = "gitolite";
9 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
10 };
11 "git.rheperire.org" =
12 { user = "gitolite";
13 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
14 };
15 "borg.munin" =
16 { hostname = "u120515.your-storagebox.de";
17 user = "u120515";
18 identityFile = "~/.ssh/borg.munin";
19 port = 23;
20 };
21 "munin" =
22 { hostname = "u120515.your-storagebox.de";
23 user = "u120515";
24 identityFile = "~/.ssh/munin";
25 };
26 "ymir" =
27 { hostname = "ymir.yggdrasil.li";
28 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
29 };
30 "surtr" =
31 { hostname = "surtr.yggdrasil.li";
32 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
33 };
34 "odin" =
35 { hostname = "odin.asgard.yggdrasil";
36 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
37 };
38 "init.odin" =
39 { hostname = "odin.asgard.yggdrasil";
40 user = "root";
41 identityFile = "~/.ssh/rsa.gkleen@hel.midgard.yggdrasil";
42 extraOptions = {
43 StrictHostKeyChecking = "off";
44 };
45 };
46 "heimdallr" =
47 { hostname = "heimdallr.asgard.yggdrasil";
48 user = "root";
49 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
50 };
51 "gitlab2.rz.ifi.lmu.de" =
52 { user = "git";
53 identityFile = "~/.ssh/gkleen@gitlab2.rz.ifi.lmu.de";
54 };
55 "gitlab2.cip.ifi.lmu.de" =
56 { user = "git";
57 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
58 };
59 "hel".hostname = "hel.midgard.yggdrasil";
60 "blackbeard" =
61 { hostname = "blackbeard.tcs.ifi.lmu.de";
62 user = "pi";
63 identityFile = "~/.ssh/blackbeard";
64 };
65 "github.com" =
66 { user = "git";
67 identityFile = "~/.ssh/gkleen@github.com";
68 };
69 "ullr.playat.ch" =
70 { hostname = "ullr.playat.ch";
71 user = "minecraft";
72 identityFile = "~/.ssh/minecraft@ullr.playat.ch";
73 };
74 "ullr" =
75 { hostname = "185.170.112.70";
76 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
77 };
78 "testworx" =
79 { hostname = "testworx.tcs.ifi.lmu.de";
80 user = "root";
81 port = 30363;
82 identityFile = "~/.ssh/testworx";
83 };
84 "remote.cip.ifi.lmu.de" =
85 { user = "kleen";
86 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
87 };
88 "uniworx3" =
89 { hostname = "uniworx3.ifi.lmu.de";
90 user = "root";
91 identityFile = "~/.ssh/uni2work";
92 };
93 "uniworx4" =
94 { hostname = "uniworx4.ifi.lmu.de";
95 user = "root";
96 identityFile = "~/.ssh/uni2work";
97 };
98 "jump.uniworx4" =
99 { hostname = "uniworx4.ifi.lmu.de";
100 user = "sshjump";
101 identityFile = "~/.ssh/sshjump.uni2work";
102 };
103 "uni2workgw" =
104 { hostname = "uni2workgw.ifi.lmu.de";
105 user = "root";
106 identityFile = "~/.ssh/uni2work";
107 };
108 "uniworxdb2" =
109 { hostname = "uniworxdb2";
110 proxyJump = "uniworx4";
111 user = "root";
112 identityFile = "~/.ssh/uni2work";
113 };
114 "uniworx5" =
115 { hostname = "uniworx5.ifi.lmu.de";
116 user = "root";
117 identityFile = "~/.ssh/uni2work";
118 };
119 "gate2" =
120 { hostname = "gate2.tcs.ifi.lmu.de";
121 user = "gkleen";
122 identityFile = "~/.ssh/tcs";
123 serverAliveInterval = 0;
124 };
125 "proxy.gate2" =
126 { hostname = "gate2.tcs.ifi.lmu.de";
127 user = "gkleen";
128 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
129 dynamicForwards = [ { port = 8118; } ];
130 serverAliveInterval = 0;
131 extraOptions = {
132 ExitOnForwardFailure = "yes";
133 };
134 };
135 "jump.gate2" =
136 { hostname = "gate2.tcs.ifi.lmu.de";
137 user = "gkleen";
138 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
139 serverAliveInterval = 0;
140 extraOptions = {
141 ExitOnForwardFailure = "yes";
142 };
143 };
144 "gate" =
145 { hostname = "gate.tcs.ifi.lmu.de";
146 user = "gkleen";
147 identityFile = "~/.ssh/tcs";
148 };
149 "proxy.gate" =
150 { hostname = "gate.tcs.ifi.lmu.de";
151 user = "gkleen";
152 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
153 dynamicForwards = [ { port = 8118; } ];
154 extraOptions = {
155 ExitOnForwardFailure = "yes";
156 };
157 };
158 "jump.gate" =
159 { hostname = "gate.tcs.ifi.lmu.de";
160 user = "gkleen";
161 identityFile = "~/.ssh/proxy.gkleen@tcs.ifi.lmu.de";
162 extraOptions = {
163 ExitOnForwardFailure = "yes";
164 };
165 };
166 "oregon" =
167 { hostname = "oregon.tcs.ifi.lmu.de";
168 user = "root";
169 identityFile = "~/.ssh/tcs";
170 };
171 "proxy.oregon" =
172 { hostname = "oregon.tcs.ifi.lmu.de";
173 user = "root";
174 identityFile = "~/.ssh/tcs";
175 dynamicForwards = [ { port = 8113; } ];
176 extraOptions = {
177 ExitOnForwardFailure = "yes";
178 };
179 };
180 "witbank" =
181 { hostname = "witbank.tcs.ifi.lmu.de";
182 user = "uni2work";
183 identityFile = "~/.ssh/letz";
184 };
185 "git.odin" =
186 { hostname = "odin.asgard.yggdrasil";
187 user = "gitolite";
188 };
189 "notmuch.odin" =
190 { hostname = "odin.asgard.yggdrasil";
191 identityFile = "~/.ssh/notmuch.odin.asgard.yggdrasil";
192 };
193 "status.odin" =
194 { hostname = "odin.asgard.yggdrasil";
195 identityFile = "~/.ssh/status.odin.asgard.yggdrasil";
196 extraOptions.ControlPath = "~/.ssh/status-%r@%n:%p";
197 };
198 "moden" =
199 { hostname = "oristano.tcs.ifi.lmu.de";
200 user = "gkleen";
201 port = 30363;
202 identityFile = "~/.ssh/gkleen@oristano.tcs.ifi.lmu.de";
203 };
204 "ubuntu1804" =
205 { hostname = "192.168.122.30";
206 identityFile = "~/.ssh/gkleen@sif.midgard.yggdrasil";
207 forwardAgent = true;
208 };
209 "gitlab.haskell.org" =
210 { hostname = "gitlab.haskell.org";
211 identityFile = "~/.ssh/gkleen@gitlab.haskell.org";
212 };
213 "gitlab.lrz.de" =
214 { hostname = "gitlab.lrz.de";
215 user = "git";
216 identityFile = "~/.ssh/gkleen@gitlab.lrz.de";
217 };
218 "uni2work-dev1" =
219 { hostname = "uni2work-dev1.ifi.lmu.de";
220 user = "gkleen";
221 identityFile = "~/.ssh/uni2work";
222 proxyJump = "jump.uniworx4";
223 localForwards = [
224 { bind = { address = "localhost"; port = 3940; };
225 host = { address = "localhost"; port = 3940; };
226 }
227 ];
228 };
229}
diff --git a/accounts/gkleen@sif/store.kdbx.lftp b/accounts/gkleen@sif/store.kdbx.lftp
new file mode 100644
index 00000000..4447aded
--- /dev/null
+++ b/accounts/gkleen@sif/store.kdbx.lftp
@@ -0,0 +1,6 @@
1open ftp://gkleen.keepass@yggdrasil.li/
2
3lcd /home/gkleen
4
5mirror -v --only-newer -f store.kdbx
6mirror -v --reverse --only-newer -f store.kdbx \ No newline at end of file
diff --git a/accounts/gkleen@sif/systemd.nix b/accounts/gkleen@sif/systemd.nix
new file mode 100644
index 00000000..a5b71417
--- /dev/null
+++ b/accounts/gkleen@sif/systemd.nix
@@ -0,0 +1,94 @@
1{ pkgs, config, userName, ... }:
2let
3 xmobar = import ./xmobar pkgs.haskellPackages;
4 cfg = config.home-manager.users.${userName};
5in {
6 services = {
7 sync-keepass = {
8 Service = {
9 Type = "oneshot";
10 WorkingDirectory = "~";
11 ExecStart = "${pkgs.lftp}/bin/lftp -f ${./store.kdbx.lftp}";
12 };
13 };
14 urxvtd = {
15 Service = {
16 Type = "simple";
17 WorkingDirectory = "~";
18 ExecStart = "${cfg.programs.urxvt.package}/bin/urxvtd";
19 Restart = "always";
20 };
21 Unit = {
22 After = ["graphical-session.target"];
23 };
24 Install = {
25 WantedBy = ["graphical-session.target"];
26 };
27 };
28 emacs = {
29 Unit = {
30 After = ["graphical-session-pre.target"];
31 };
32 };
33 trayer = {
34 Service = {
35 Type = "simple";
36 WorkingDirectory = "~";
37 ExecStart = "${pkgs.trayer}/bin/trayer --edge top --align right --SetDockType true --SetPartialStrut true --expand true --width 8 --tint 0x000000 --alpha 0 --transparent true --height 32 --monitor primary";
38 Restart = "always";
39 };
40 Install = {
41 WantedBy = ["graphical-session.target"];
42 };
43 };
44 xmobar = {
45 Service = {
46 Type = "simple";
47 WorkingDirectory = "~";
48 ExecStart = "${xmobar}/bin/xmobar";
49 Restart = "always";
50 Environment = "PATH=${pkgs.worktime}/bin:${pkgs.openssh}/bin";
51
52 };
53 Install = {
54 WantedBy = ["graphical-session.target"];
55 };
56 };
57 dunst = {
58 Service = {
59 Restart = "always";
60 };
61 Install = {
62 WantedBy = ["graphical-session.target"];
63 };
64 };
65 xiccd = {
66 Service = {
67 Type = "simple";
68 WorkingDirectory = "~";
69 ExecStart = "${pkgs.xiccd}/bin/xiccd";
70 Restart = "always";
71 };
72 };
73 };
74 timers = {
75 sync-keepass = {
76 Timer = {
77 OnActiveSec = "1m";
78 OnUnitActiveSec = "1m";
79 };
80
81 Install = {
82 WantedBy = ["default.target"];
83 };
84 };
85 };
86 targets = {
87 graphical-session = {
88 Unit = {
89 BindsTo = ["default.target"];
90 After = ["basic.target"];
91 };
92 };
93 };
94}
diff --git a/accounts/gkleen@sif/xmobar/default.nix b/accounts/gkleen@sif/xmobar/default.nix
new file mode 100644
index 00000000..fcac5e60
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/default.nix
@@ -0,0 +1,7 @@
1argumentPackages@{ ... }:
2
3let
4 # defaultPackages = (import ./stackage.nix {});
5 # haskellPackages = defaultPackages // argumentPackages;
6 haskellPackages = argumentPackages;
7in haskellPackages.callPackage ./xmobar-yggdrasil.nix {}
diff --git a/accounts/gkleen@sif/xmobar/nixpkgs.nix b/accounts/gkleen@sif/xmobar/nixpkgs.nix
new file mode 100644
index 00000000..783ede00
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/nixpkgs.nix
@@ -0,0 +1,9 @@
1{ nixpkgs ? import <nixpkgs>
2}:
3
4import ((nixpkgs {}).fetchFromGitHub {
5 owner = "NixOS";
6 repo = "nixpkgs";
7 rev = "10e61bf5be57736035ec7a804cb0bf3d083bf2cf";
8 sha256 = "0fplfm2zx4vk7gs8bdcxnvzkdmpx2w0llqwf8475z9dz9cl132rm";
9})
diff --git a/accounts/gkleen@sif/xmobar/package.yaml b/accounts/gkleen@sif/xmobar/package.yaml
new file mode 100644
index 00000000..b638b6ac
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/package.yaml
@@ -0,0 +1,13 @@
1name: xmobar-yggdrasil
2
3executables:
4 xmobar:
5 dependencies:
6 - base
7 - xmobar
8
9 main: xmobar.hs
10 source-dirs:
11 - .
12
13 ghc-options: -threaded
diff --git a/accounts/gkleen@sif/xmobar/shell.nix b/accounts/gkleen@sif/xmobar/shell.nix
new file mode 100644
index 00000000..16beb322
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/shell.nix
@@ -0,0 +1,28 @@
1{ nixpkgs ? import ./nixpkgs.nix {} }:
2
3let
4 inherit (nixpkgs {}) pkgs;
5 haskellPackages = import ./stackage.nix { inherit nixpkgs; };
6
7 drv = haskellPackages.callPackage ./xmobar-yggdrasil.nix {};
8 override = oldAttrs: {
9 nativeBuildInputs = oldAttrs.nativeBuildInputs ++ (with pkgs; []) ++ (with haskellPackages; [ stack cabal-install cabal2nix ]);
10 shellHook = ''
11 export PROMPT_INFO="${oldAttrs.name}"
12
13 if [ -n "$ZSH_VERSION" ]; then
14 autoload -U +X compinit && compinit
15 autoload -U +X bashcompinit && bashcompinit
16 fi
17 eval "$(stack --bash-completion-script stack)"
18
19 ${oldAttrs.shellHook}
20 '';
21 };
22
23 dummy = pkgs.stdenv.mkDerivation {
24 name = "interactive-xmobar-environment";
25 shellHook = "";
26 };
27in pkgs.lib.overrideDerivation dummy override
28 #pkgs.lib.overrideDerivation drv.env override
diff --git a/accounts/gkleen@sif/xmobar/stack.nix b/accounts/gkleen@sif/xmobar/stack.nix
new file mode 100644
index 00000000..17a49e04
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stack.nix
@@ -0,0 +1,17 @@
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/xmobar/stack.yaml b/accounts/gkleen@sif/xmobar/stack.yaml
new file mode 100644
index 00000000..b8ed1147
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stack.yaml
@@ -0,0 +1,10 @@
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/xmobar/stack.yaml.lock b/accounts/gkleen@sif/xmobar/stack.yaml.lock
new file mode 100644
index 00000000..fcc2f5f3
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stack.yaml.lock
@@ -0,0 +1,12 @@
1# This file was autogenerated by Stack.
2# You should not edit this file by hand.
3# For more information, please see the documentation at:
4# https://docs.haskellstack.org/en/stable/lock_files
5
6packages: []
7snapshots:
8- completed:
9 size: 498180
10 url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/13/21.yaml
11 sha256: eff2de19a6d4691ccbf6edc1fba858f1918683047dce0f09adede874bbd2a8f3
12 original: lts-13.21
diff --git a/accounts/gkleen@sif/xmobar/stackage.nix b/accounts/gkleen@sif/xmobar/stackage.nix
new file mode 100644
index 00000000..c162ca2c
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/stackage.nix
@@ -0,0 +1,31 @@
1{ nixpkgs ? import ./nixpkgs.nix {}
2, snapshot ? "lts-13.21"
3}:
4
5let
6 stackage = import (fetchTarball {
7 url = "https://stackage.serokell.io/zb36jsy3r5h4ydz0pnp00g9vk94dvv03-stackage/default.nix.tar.gz";
8 sha256 = "0h6f80gds0ds77y51hhiadh2h2k8njqq8n0gayp729ana9m9agma";
9 });
10
11 overlays =
12 [ stackage."${snapshot}"
13 (self: super: {
14 haskell = super.haskell // {
15 packages = super.haskell.packages // {
16 "${snapshot}" = super.haskell.packages."${snapshot}".override {
17 overrides = hself: hsuper: {
18 zip-archive = self.haskell.lib.overrideCabal hsuper.zip-archive (old: {
19 testToolDepends = old.testToolDepends ++ (with self; [ unzip which ]);
20 });
21 alex = self.haskell.lib.dontCheck hsuper.alex;
22 };
23 };
24 };
25 };
26 }
27 )
28 ];
29
30 inherit (nixpkgs { inherit overlays; }) pkgs;
31in pkgs.haskell.packages."${snapshot}"
diff --git a/accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix b/accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix
new file mode 100644
index 00000000..852fb8e5
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/xmobar-yggdrasil.nix
@@ -0,0 +1,13 @@
1{ mkDerivation, base, hpack, lib, xmobar }:
2mkDerivation {
3 pname = "xmobar-yggdrasil";
4 version = "0.0.0";
5 src = ./.;
6 isLibrary = false;
7 isExecutable = true;
8 libraryToolDepends = [ hpack ];
9 executableHaskellDepends = [ base xmobar ];
10 preConfigure = "hpack";
11 license = "unknown";
12 hydraPlatforms = lib.platforms.none;
13}
diff --git a/accounts/gkleen@sif/xmobar/xmobar.hs b/accounts/gkleen@sif/xmobar/xmobar.hs
new file mode 100644
index 00000000..74ce7347
--- /dev/null
+++ b/accounts/gkleen@sif/xmobar/xmobar.hs
@@ -0,0 +1,52 @@
1import Xmobar
2
3import Data.List (intercalate)
4
5
6main :: IO ()
7main = xmobar config
8 where
9 config = defaultConfig
10 { font = "xft:FiraCode Nerd Font Mono:style=Regular:pixelsize=21"
11 , position = OnScreen 0 $ TopP 0 307
12 , bgColor = "black"
13 , fgColor = "#808080"
14 , overrideRedirect = False
15 , template =
16 let left = intercalate " | "
17 [ "%XMonadWorkspaces%"
18 , "%XMonadLayout%"
19 , "%XMonadTitle%"
20 ]
21 right = intercalate " | "
22 [ {- "%status%"
23 , -} "%battery%"
24 , "%kbd%"
25 , "%worktime%"
26 , "%worktime-today%"
27 , "%time%"
28 , "%date%"
29 ]
30 in left <> "}{" <> right
31 , commands =
32 [ Run $ NamedXPropertyLog "_XMONAD_WORKSPACES" "XMonadWorkspaces"
33 , Run $ NamedXPropertyLog "_XMONAD_LAYOUT" "XMonadLayout"
34 , Run $ NamedXPropertyLog "_XMONAD_TITLE" "XMonadTitle"
35 , Run $ Date "%H:%M" "time" 50
36 , Run $ Date "%a %b %_d" "date" 50
37 , Run $ Com "worktime" [] "worktime" 1500
38 , Run $ Com "worktime" ["today"] "worktime-today" 1500
39 , Run $ Com "ssh" ["status.odin"] "status" 600
40 , Run $ Kbd [("us(dvp)", "dvp")]
41 , Run $ Battery
42 [ "--template", "<watts> <left> (<timeleft>) AC <acstatus>"
43 , "--suffix", "On"
44 , "--Low", "10"
45 , "--High", "80"
46 , "--low", "darkred"
47 , "--normal", "darkorange"
48 , "--high", "darkgreen"
49 , "-p", "3"
50 ] 50
51 ]
52 }
diff --git a/accounts/gkleen@sif/xmonad/.gitignore b/accounts/gkleen@sif/xmonad/.gitignore
new file mode 100644
index 00000000..c11891cd
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/.gitignore
@@ -0,0 +1,4 @@
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
new file mode 100644
index 00000000..8790c12f
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/default.nix
@@ -0,0 +1,7 @@
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
new file mode 100644
index 00000000..e6accdcc
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Mpv.hs
@@ -0,0 +1,127 @@
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
new file mode 100644
index 00000000..1caefae5
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyPass.hs
@@ -0,0 +1,94 @@
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
new file mode 100644
index 00000000..c268f87d
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MyShell.hs
@@ -0,0 +1,105 @@
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
new file mode 100644
index 00000000..729941aa
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/lib/XMonad/Prompt/MySsh.hs
@@ -0,0 +1,246 @@
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 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
new file mode 100644
index 00000000..48de1a53
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/package.yaml
@@ -0,0 +1,30 @@
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
27 main: xmonad.hs
28 source-dirs:
29 - .
30 - lib
diff --git a/accounts/gkleen@sif/xmonad/stack.nix b/accounts/gkleen@sif/xmonad/stack.nix
new file mode 100644
index 00000000..17a49e04
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/stack.nix
@@ -0,0 +1,17 @@
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
new file mode 100644
index 00000000..b8ed1147
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/stack.yaml
@@ -0,0 +1,10 @@
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
new file mode 100644
index 00000000..d3d72310
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/xmonad-yggdrasil.nix
@@ -0,0 +1,21 @@
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
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
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
new file mode 100644
index 00000000..579456ad
--- /dev/null
+++ b/accounts/gkleen@sif/xmonad/xmonad.hs
@@ -0,0 +1,902 @@
1{-# LANGUAGE TupleSections, ViewPatterns, OverloadedStrings, FlexibleInstances, UndecidableInstances, MultiWayIf #-}
2
3import XMonad
4import XMonad.Hooks.DynamicLog
5import XMonad.Hooks.ManageDocks
6import XMonad.Util.Run
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 Control.Monad (sequence, liftM, liftM2, join, void)
28import XMonad.Util.WorkspaceCompare
29import XMonad.Layout.NoBorders
30import XMonad.Layout.PerWorkspace
31import XMonad.Layout.SimplestFloat
32import XMonad.Layout.Renamed
33import XMonad.Layout.Reflect
34import XMonad.Layout.OnHost
35import XMonad.Layout.Combo
36import XMonad.Layout.ComboP
37import XMonad.Layout.Column
38import XMonad.Layout.TwoPane
39import XMonad.Layout.IfMax
40import XMonad.Layout.LayoutBuilder
41import XMonad.Layout.WindowNavigation
42import XMonad.Layout.Dwindle
43import XMonad.Layout.TrackFloating
44import System.Process
45import System.Directory (removeFile)
46import System.Posix.Files
47import System.FilePath ((</>))
48import Control.Concurrent
49import System.Posix.Process (getProcessID)
50import System.IO.Error
51import System.IO
52import XMonad.Hooks.ManageHelpers hiding (CW)
53import XMonad.Hooks.UrgencyHook as U
54import XMonad.Hooks.EwmhDesktops
55import XMonad.StackSet (RationalRect (..))
56import Control.Monad (when, filterM, (<=<))
57import Graphics.X11.ExtraTypes.XF86
58import XMonad.Util.Cursor
59import XMonad.Actions.Warp
60import XMonad.Actions.FloatKeys
61import XMonad.Util.SpawnOnce
62import System.Directory
63import System.FilePath
64import XMonad.Actions.CopyWindow
65import XMonad.Hooks.ServerMode
66import XMonad.Actions.Commands
67import XMonad.Actions.CycleWS
68import XMonad.Actions.RotSlaves
69import XMonad.Actions.UpdatePointer
70import XMonad.Prompt.Window
71import Data.IORef
72import Data.Monoid
73import Data.String
74import qualified XMonad.Actions.PhysicalScreens as P
75
76import XMonad.Layout.IM
77
78import XMonad.Prompt.MyShell
79import XMonad.Prompt.MyPass
80import XMonad.Prompt.MySsh
81
82import XMonad.Mpv
83
84import Network.HostName
85
86import Control.Applicative ((<$>))
87
88import Libnotify as Notify hiding (appName)
89import qualified Libnotify as Notify (appName)
90import Libnotify (Notification)
91-- import System.Information.Battery
92
93import Data.Int (Int32)
94
95import System.Posix.Process
96import System.Posix.Signals
97import System.Posix.IO as Posix
98import Control.Exception
99
100import System.IO.Unsafe
101
102import Control.Monad.Trans.Class
103import Control.Monad.Trans.Maybe
104
105import Data.Fixed (Micro)
106
107import qualified Data.Text as Text
108import Data.Ord (comparing)
109import Debug.Trace
110
111instance MonadIO m => IsString (m ()) where
112 fromString = spawn
113
114type KeyMap = Map (ButtonMask, KeySym) (X ())
115
116data Host = Host
117 { hName :: HostName
118 , hManageHook :: ManageHook
119 , hWsp :: Integer -> WorkspaceId
120 , hCoWsp :: String -> Maybe WorkspaceId
121 , hKeysMod :: XConfig Layout -> (KeyMap -> KeyMap)
122 , hScreens :: [P.PhysicalScreen]
123 , hKbLayouts :: [(String, Maybe String)]
124 , hCmds :: X [(String, X ())]
125 , hKeyUpKeys :: XConfig Layout -> KeyMap
126 }
127
128defaultHost = Host { hName = "unkown"
129 , hManageHook = composeOne [manageScratchTerm]
130 , hWsp = show
131 , hCoWsp = const Nothing
132 , hKeysMod = const id
133 , hScreens = [0,1..]
134 , hKbLayouts = [ ("us", Just "dvp")
135 , ("us", Nothing)
136 , ("de", Nothing)
137 ]
138 , hCmds = return []
139 , hKeyUpKeys = const Map.empty
140 }
141
142browser :: String
143browser = "env MOZ_USE_XINPUT2=1 firefox"
144
145hostFromName :: HostName -> Host
146hostFromName h@("vali") = defaultHost { hName = h
147 , hManageHook = composeOne $ catMaybes [ Just manageScratchTerm
148 , assign "web" $ className =? ".dwb-wrapped"
149 , assign "web" $ className =? "Chromium"
150 , assign "work" $ className =? "Emacs"
151 , assign "media" $ className =? "mpv"
152 ]
153 , hWsp = hWsp
154 , hCoWsp = hCoWsp
155 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_d, ["chromium", "chromium $(xclip -o)"])
156 , (xK_e, ["emacsclient -c"])
157 ])
158 `Map.union`
159 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), scratchpadSpawnActionCustom $ (XMonad.terminal conf) ++ " -name scratchpad -title scratchpad -e tmux new-session -D -s scratch")
160 ] )
161 , hScreens = hScreens defaultHost
162 }
163 where
164 workspaceNames = Map.fromList [ (2, "web")
165 , (3, "work")
166 , (10, "media")
167 ]
168 hWsp = wspFromMap workspaceNames
169 hCoWsp = coWspFromMap workspaceNames
170 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
171hostFromName h
172 | h `elem` ["hel", "sif"] = defaultHost { hName = h
173 , hManageHook = namedScratchpadManageHook scratchpads <+> composeOne (catMaybes
174 [ assign "mpv" $ className =? "mpv"
175 , assign "mpv" $ (className =? "URxvt" <&&> title =? "irssi")
176 , assign "mpv" $ (className =? "URxvt" <&&> resource =? "presentation")
177 , assign "mpv" $ stringProperty "WM_WINDOW_ROLE" =? "presentation"
178 , assign "read" $ stringProperty "WM_WINDOW_ROLE" =? "presenter"
179 , assign "mpv" $ className =? "factorio"
180 , assign "web" $ className =? "chromium-browser"
181 , assign "web" $ className =? "Google-chrome"
182 , assign "work" $ (appName =? "Devtools" <&&> className =? "Firefox")
183 , assign "work" $ className =? "Postman"
184 , assign "web" $ className =? "Firefox"
185 , assign "comm" $ (className =? "URxvt" <&&> resource =? "comm")
186 , assign "comm" $ (className =? "Emacs" <&&> title =? "Mail")
187 , assign "comm" $ className =? "Zulip"
188 , assign "comm" $ className =? "Discord"
189 , assign "media" $ (className =? "URxvt" <&&> resource =? "media")
190 , assign "media" $ (className =? "URxvt" <&&> title =? "streamlink")
191 , assign "media" $ (className =? "URxvt" <&&> title =? "mpv")
192 , assign "monitor" $ (className =? "URxvt" <&&> fmap ("monitor" `isInfixOf`) title)
193 , assign "monitor" $ className =? "Grafana"
194 , Just $ (className =? "URxvt" <&&> resource =? "htop") -?> centerFloat
195 , Just $ (className =? "Scp-dbus-service.py") -?> centerFloat
196 , Just $ (className =? "URxvt" <&&> resource =? "log") -?> centerFloat
197 , assign "work" $ className =? "URxvt"
198 , assign' ["work", "uni"] $ (className =? "Emacs" <&&> appName /=? "Edit_with_Emacs_FRAME")
199 , assign' ["work", "uni"] $ className =? "jetbrains-idea-ce"
200 , assign "read" $ className =? "llpp"
201 , assign "read" $ className =? "Evince"
202 , assign "read" $ className =? "Zathura"
203 , assign "read" $ className =? "MuPDF"
204 , assign "read" $ className =? "Xournal"
205 , assign "read" $ appName =? "com-trollworks-gcs-app-GCS"
206 , assign "read" $ appName =? "Tux.py"
207 , assign "read" $ className =? "Gnucash"
208 , assign "comm" $ className =? "Skype"
209 , assign "comm" $ className =? "Daily"
210 , assign "comm" $ className =? "Pidgin"
211 , assign "comm" $ className =? "Slack"
212 , Just $ (resource =? "xvkbd") -?> doRectFloat $ RationalRect (1 % 8) (3 % 8) (6 % 8) (4 % 8)
213 , Just $ (stringProperty "_NET_WM_WINDOW_TYPE" =? "_NET_WM_WINDOW_TYPE_DIALOG") -?> doFloat
214 , Just $ (className =? "Dunst") -?> doFloat
215 , Just $ (className =? "Xmessage") -?> doCenterFloat
216 , Just $ (className =? "Nm-openconnect-auth-dialog") -?> centerFloat
217 , Just $ (className =? "Pinentry") -?> doCenterFloat
218 , Just $ (className =? "pinentry") -?> doCenterFloat
219 , Just $ (appName =? "Edit_with_Emacs_FRAME") -?> centerFloat
220 , Just $ (stringProperty "WM_WINDOW_ROLE" =? "GtkFileChooseDialog") -?> centerFloatSmall
221 , Just $ (className =? "Nvidia-settings") -?> doCenterFloat
222 , Just $ fmap ("Minetest" `isInfixOf`) title -?> doIgnore
223 , Just $ fmap ("Automachef" `isInfixOf`) title -?> doIgnore
224 , assign "call" $ className =? "zoom"
225 ])
226 , hWsp = hWsp
227 , hCoWsp = hCoWsp
228 , hKeysMod = \conf -> Map.union $ (Map.fromList $ join $ map (spawnBindings conf) [ (xK_e, ["emacsclient -c"])
229 , (xK_d, [fromString browser, fromString $ browser ++ " $(xclip -o)", fromString $ "notmuch-links"])
230 , (xK_f, ["urxvtc -name comm -title Feeds -e mosh odin -- tmux new-session -ADs comm"])
231 , (xK_c, [ inputPrompt xPConfig "dc" ?+ dc ])
232 , (xK_g, ["pidgin"])
233 , (xK_s, ["skype"])
234 -- , (xK_p, [mkPassPrompt "Type password" pwType xPConfig, mkPassPrompt "Show password" pwShow xPConfig, mkPassPrompt "Copy password" pwClip xPConfig])
235 , (xK_w, ["sudo rewacom"])
236 , (xK_y, [ "tmux new-window -dt media /var/media/link.hs $(xclip -o)"
237 , "tmux new-window -dt media /var/media/download.hs $(xclip -o)"
238 , "urxvtc -name media -e tmuxp load /var/media"
239 ])
240 , (xK_l, [ "tmux new-window -dt media mpv $(xclip -o)"
241 , "tmux new-window -dt media streamlink --retry-open 10 $(xclip -o)"
242 ])
243 , (xK_m, [ "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch)'"
244 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e '(notmuch-mua-new-mail)'"
245 , "emacsclient -c -F \"'(title . \\\"Mail\\\")\" -e \"(browse-url-mail \"$(xclip -o)\")\""
246 ])
247 , (xK_Return, ["keynav start,windowzoom", "keynav start"])
248 , (xK_t, [inputPrompt xPConfig "fuzzytime timer" ?+ fuzzytime, fuzzytime "unset", work_fuzzytime])
249 , (xK_a, [inputPrompt xPConfig "adjmix" ?+ adjmix])
250 , (xK_s, [ inputPromptWithCompl xPConfig "start synergy" synergyCompl ?+ synergyStart
251 , inputPromptWithCompl xPConfig "stop synergy" synergyCompl ?+ synergyStop
252 ])
253 , (xK_h, [ "urxvtc -name htop -e htop"
254 , "urxvtc -name log -e journalctl -xef"
255 ])
256 , (xK_x, [ "autorandr -c"
257 , "autorandr -fl def"
258 ])
259 , (xK_z, [ "zulip -- --force-device-scale-factor=2"
260 ])
261 ])
262 `Map.union`
263 ( Map.fromList [ ((XMonad.modMask conf .|. controlMask, xK_Return), namedScratchpadAction scratchpads "term")
264 , ((XMonad.modMask conf .|. controlMask, xK_a), namedScratchpadAction scratchpads "pavucontrol")
265 , ((XMonad.modMask conf .|. controlMask, xK_w), namedScratchpadAction scratchpads "alarms")
266 , ((XMonad.modMask conf .|. controlMask, xK_b), namedScratchpadAction scratchpads "blueman")
267 , ((XMonad.modMask conf .|. controlMask, xK_p), namedScratchpadAction scratchpads "keepassxc")
268 , ((XMonad.modMask conf .|. controlMask, xK_t), namedScratchpadAction scratchpads "toggl")
269 , ((XMonad.modMask conf .|. controlMask, xK_e), namedScratchpadAction scratchpads "emacs")
270 , ((XMonad.modMask conf .|. controlMask, xK_m), namedScratchpadAction scratchpads "calendar")
271 , ((XMonad.modMask conf .|. controlMask, xK_f), namedScratchpadAction scratchpads "music")
272 , ((XMonad.modMask conf .|. mod1Mask, xK_Up), rotate U)
273 , ((XMonad.modMask conf .|. mod1Mask, xK_Down), rotate D)
274 , ((XMonad.modMask conf .|. mod1Mask, xK_Left), rotate L)
275 , ((XMonad.modMask conf .|. mod1Mask, xK_Right), rotate R)
276 -- , ((XMonad.modMask conf .|. shiftMask, xK_a), startMute "hel")
277 ] )
278 , hKeyUpKeys = \conf -> Map.fromList [ -- ((XMonad.modMask conf .|. shiftMask, xK_a), stopMute "hel")
279 ]
280 , hScreens = hScreens defaultHost
281 , hCmds = return [ ("prev-workspace", prevWS)
282 , ("next-workspace", nextWS)
283 , ("prev-window", rotAllDown)
284 , ("next-window", rotAllUp)
285 , ("banish", banishScreen LowerRight)
286 , ("update-gpg-tty", safeSpawn "gpg-connect-agent" ["UPDATESTARTUPTTY", "/bye"])
287 , ("rescreen", rescreen)
288 , ("repanel", do
289 spawn "nm-applet"
290 spawn "blueman-applet"
291 spawn "pasystray"
292 spawn "kdeconnect-indicator"
293 spawn "dunst -print"
294 spawn "udiskie"
295 spawn "autocutsel -s PRIMARY"
296 spawn "autocutsel -s CLIPBOARD"
297 )
298 , ("pause", mediaMpv $ MpvSetProperty "pause" True)
299 , ("unpause", mediaMpv $ MpvSetProperty "pause" False)
300 , ("exit", io $ exitWith ExitSuccess)
301 ]
302 }
303 where
304 withGdkScale act = void . xfork $ setEnv "GDK_SCALE" "2" >> act
305 workspaceNames = Map.fromList [ (1, "comm")
306 , (2, "web")
307 , (3, "work")
308 , (4, "read")
309 , (5, "monitor")
310 , (6, "uni")
311 , (8, "call")
312 , (9, "media")
313 , (10, "mpv")
314 ]
315 scratchpads = [ NS "term" "urxvtc -name scratchpad -title scratchpad -e tmux new-session -AD -s scratch" (resource =? "scratchpad") centerFloat
316 , NS "pavucontrol" "pavucontrol" (resource =? "pavucontrol") centerFloat
317 , NS "alarms" "alarm-clock-applet" (className =? "Alarm-clock-applet" <&&> title =? "Alarms") centerFloat
318 , NS "blueman" "blueman-manager" (className =? ".blueman-manager-wrapped") centerFloat
319 , NS "keepassxc" "keepassxc" (className =? "KeePassXC") centerFloat
320 , NS "toggl" "toggldesktop" (className =? "Toggl Desktop") centerFloat
321 , NS "calendar" "minetime -- --force-device-scale-factor=1.6" (className =? "MineTime") centerFloat
322 , NS "emacs" "emacsclient -c -F \"'(title . \\\"Scratchpad\\\")\"" (className =? "Emacs" <&&> title =? "Scratchpad") centerFloat
323 , NS "music" "google-play-music-desktop-player --force-device-scale-factor=1.6" (className =? "Google Play Music Desktop Player") centerFloat
324 ]
325 centerFloat = customFloating $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
326 centerFloatSmall = customFloating $ RationalRect (1 % 4) (1 % 4) (1 % 2) (1 % 2)
327 hWsp = wspFromMap workspaceNames
328 hCoWsp = coWspFromMap workspaceNames
329 assign wsp test = (\wsp -> test -?> doShift wsp) <$> hCoWsp wsp
330 assign' :: [String] -> Query Bool -> Maybe MaybeManageHook
331 assign' wsps test = do
332 wsIds <- mapM hCoWsp wsps
333 return $ test -?> go wsIds
334 where
335 go :: [WorkspaceId] -> ManageHook
336 go wsps = do
337 visWsps <- liftX $ (\wset -> W.tag . W.workspace <$> W.current wset : W.visible wset) <$> gets windowset
338 case (filter (`elem` visWsps) wsps, wsps) of
339 (wsp : _, _) -> doShift wsp
340 (_, wsp : _) -> doShift wsp
341 ([], []) -> return mempty
342 rotate rot = do
343 safeSpawn "xrandr" ["--output", "eDP-1", "--rotate", xrandrDir]
344 mapM_ rotTouch touchscreens
345 where
346 xrandrDir = case rot of
347 U -> "normal"
348 L -> "left"
349 R -> "right"
350 D -> "inverted"
351 matrix = case rot of
352 U -> [ [ 1, 0, 0]
353 , [ 0, 1, 0]
354 , [ 0, 0, 1]
355 ]
356 L -> [ [ 0, -1, 1]
357 , [ 1, 0, 0]
358 , [ 0, 0, 1]
359 ]
360 R -> [ [ 0, 1, 0]
361 , [-1, 0, 1]
362 , [ 0, 0, 1]
363 ]
364 D -> [ [-1, 0, 1]
365 , [ 0, -1, 1]
366 , [ 0, 0, 1]
367 ]
368 touchscreens = [ "Wacom Co.,Ltd. Pen and multitouch sensor Finger touch"
369 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen stylus"
370 , "Wacom Co.,Ltd. Pen and multitouch sensor Pen eraser"
371 ]
372 rotTouch screen = do
373 safeSpawn "xinput" $ ["set-prop", screen, "Coordinate Transformation Matrix"] ++ map (\n -> show n ++ ",") (concat matrix)
374 safeSpawn "xinput" ["map-to-output", screen, "eDP-1"]
375 withPw f label = io . void . forkProcess $ do
376 uninstallSignalHandlers
377 void $ createSession
378 (dropWhileEnd isSpace -> pw) <- readCreateProcess (proc "pass" ["show", label]) ""
379 void $ f pw
380 pwType :: String -> X ()
381 pwType = withPw $ readCreateProcess (proc "xdotool" ["type", "--clearmodifiers", "--file", "-"])
382 pwClip label = safeSpawn "pass" ["show", "--clip", label]
383 pwShow :: String -> X ()
384 pwShow = withPw $ \pw -> do
385 xmessage <- fromMaybe "xmessage" <$> liftIO (lookupEnv "XMONAD_XMESSAGE")
386 readCreateProcess (proc xmessage ["-file", "-"]) pw
387 fuzzytime str = safeSpawn "fuzzytime" $ "timer" : words str
388 work_fuzzytime = io . void . forkProcess $ do
389 readCreateProcess (proc "worktime" []) "" >>= safeSpawn "fuzzytime" . ("timer" : ) . pure
390 adjmix str = safeSpawn "adjmix" $ words str
391 dc expr = void . xfork $ do
392 result <- readProcess "dc" [] $ expr ++ "f"
393 let
394 (first : rest) = filter (not . null) $ lines result
395 notification = Notify.summary first <> Notify.body (unlines rest) <> Notify.timeout Infinite <> Notify.urgency Normal <> Notify.appName "dc"
396 void $ Notify.display notification
397 synergyCompl = mkComplFunFromList' ["mathw86"]
398 synergyStart host = safeSpawn "systemctl" ["--user", "start", "synergy-rtunnel@" ++ host ++ ".service"]
399 synergyStop host = safeSpawn "systemctl" ["--user", "stop", "synergy-rtunnel@" ++ host ++ ".service"]
400
401hostFromName _ = defaultHost
402
403-- muteRef :: IORef (Maybe (String, Notification))
404-- {-# NOINLINE muteRef #-}
405-- muteRef = unsafePerformIO $ newIORef Nothing
406
407-- startMute, stopMute :: String -> X ()
408-- startMute sink = liftIO $ do
409-- muted <- isJust <$> readIORef muteRef
410-- when (not muted) $ do
411-- let
412-- notification = Notify.summary "Muted" <> Notify.timeout Infinite <> Notify.urgency Normal
413-- level = "0.0dB"
414-- -- level <- runProcessWithInput "ssh" ["bragi", "cat", "/dev/shm/mix/" ++ sink ++ "/level"] ""
415-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", "0"]
416-- hPutStrLn stderr "Mute"
417-- writeIORef muteRef . Just . (level, ) =<< Notify.display notification
418-- stopMute sink = liftIO $ do
419-- let
420-- unmute (Just (level, notification)) = do
421-- hPutStrLn stderr "Unmute"
422-- -- callProcess "ssh" ["bragi", "adjmix", "-t", sink, "-o", level]
423-- Notify.close notification
424-- unmute Nothing = return ()
425-- muted <- isJust <$> readIORef muteRef
426-- when muted . join . atomicModifyIORef muteRef $ (Nothing, ) . unmute
427
428wspFromMap workspaceNames = \i -> case Map.lookup i workspaceNames of
429 Just str -> show i ++ " " ++ str
430 Nothing -> show i
431
432coWspFromMap workspaceNames = \str -> case filter ((== str) . snd) $ Map.toList workspaceNames of
433 [] -> Nothing
434 [(i, _)] -> Just $ wspFromMap workspaceNames i
435 _ -> Nothing
436
437spawnModifiers = [0, controlMask, shiftMask .|. controlMask]
438spawnBindings :: XConfig layout -> (KeySym, [X ()]) -> [((KeyMask, KeySym), X ())]
439spawnBindings conf (k, cmds) = zipWith (\m cmd -> ((modm .|. mod1Mask .|. m, k), cmd)) spawnModifiers cmds
440 where
441 modm = XMonad.modMask conf
442
443manageScratchTerm = (resource =? "scratchpad" <||> resource =? "keysetup") -?> doRectFloat $ RationalRect (1 % 16) (1 % 16) (7 % 8) (7 % 8)
444
445tabbedLayout t = renamed [Replace "Tabbed"] $ reflectHoriz $ t CustomShrink $ tabbedTheme
446tabbedLayoutHoriz t = renamed [Replace "Tabbed Horiz"] $ reflectVert $ t CustomShrink $ tabbedTheme
447tabbedTheme = def
448 { activeColor = "black"
449 , inactiveColor = "black"
450 , urgentColor = "black"
451 , activeBorderColor = "grey"
452 , inactiveBorderColor = "#202020"
453 , urgentBorderColor = "#bb0000"
454 , activeTextColor = "grey"
455 , inactiveTextColor = "grey"
456 , urgentTextColor = "grey"
457 , decoHeight = 32
458 , fontName = "xft:Fira Mono for Powerline:style=Medium:pixelsize=22.5"
459 }
460
461main :: IO ()
462main = do
463 arguments <- either (const []) id <$> tryIOError getArgs
464 case arguments of
465 ["--command", s] -> do
466 d <- openDisplay ""
467 rw <- rootWindow d $ defaultScreen d
468 a <- internAtom d "XMONAD_COMMAND" False
469 m <- internAtom d s False
470 allocaXEvent $ \e -> do
471 setEventType e clientMessage
472 setClientMessageEvent e rw a 32 m currentTime
473 sendEvent d rw False structureNotifyMask e
474 sync d False
475 _ -> do
476 -- batteryMon <- xfork $ monitorBattery Nothing Nothing
477 hostname <- getHostName
478 let
479 host = hostFromName hostname
480 setEnv "HOST" hostname
481 let myConfig = withHostUrgency . ewmh $ docks def
482 { manageHook = hManageHook host
483 , terminal = "urxvtc"
484 , layoutHook = smartBorders . avoidStruts $ windowNavigation layout'
485 , logHook = do
486 dynamicLogString xmobarPP' >>= writeProps
487 updatePointer (99 % 100, 98 % 100) (0, 0)
488 , modMask = mod4Mask
489 , keys = \conf -> hKeysMod host conf $ myKeys' conf host
490 , workspaces = take (length numKeys) $ map wsp [1..]
491 , startupHook = setDefaultCursor xC_left_ptr
492 , normalBorderColor = "#202020"
493 , focusedBorderColor = "grey"
494 , handleEventHook = fullscreenEventHook <+> (serverModeEventHookCmd' $ hCmds host) <+> keyUpEventHook
495 }
496 writeProps str = do
497 let encodeCChar = map $ fromIntegral . fromEnum
498 atoms = [ "_XMONAD_WORKSPACES"
499 , "_XMONAD_LAYOUT"
500 , "_XMONAD_TITLE"
501 ]
502 (flip mapM_) (zip atoms (lines str)) $ \(atom', content) -> do
503 ustring <- getAtom "UTF8_STRING"
504 atom <- getAtom atom'
505 withDisplay $ \dpy -> io $ do
506 root <- rootWindow dpy $ defaultScreen dpy
507 changeProperty8 dpy root atom ustring propModeReplace $ encodeCChar content
508 sync dpy True
509 wsp = hWsp host
510 -- We can´t define per-host layout modifiers because we lack dependent types
511 layout' = onHost "skadhi" ( onWorkspace (wsp 1) (Full ||| withIM (1%5) (Title "Buddy List") tabbedLayout') $
512 onWorkspace (wsp 10) Full $
513 onWorkspace (wsp 2) (Full ||| tabbedLayout') $
514 onWorkspace (wsp 5) tabbedLayout' $
515 onWorkspace (wsp 8) (withIM (1%5) (Title "Friends") tabbedLayout') $
516 defaultLayouts
517 ) $
518 onHost "vali" ( onWorkspace (wsp 2) (Full ||| tabbedLayout' ||| combineTwo (TwoPane 0.01 0.57) Full tabbedLayout') $
519 onWorkspace (wsp 3) workLayouts $
520 defaultLayouts
521 ) $
522 onHost "hel" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
523 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
524 onWorkspace (wsp 3) workLayouts $
525 onWorkspace (wsp 6) workLayouts $
526 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
527 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
528 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
529 defaultLayouts
530 ) $
531 onHost "sif" ( onWorkspace (wsp 1) (withIM (1 % 8) (Title "Buddy List") $ trackFloating tabbedLayout') $
532 onWorkspace (wsp 2) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
533 onWorkspace (wsp 3) workLayouts $
534 onWorkspace (wsp 6) workLayouts $
535 onWorkspace (wsp 4) (tabbedLayout' ||| tabbedLayoutHoriz' ||| Dwindle R CW 1 (5 % 100)) $
536 onWorkspace (wsp 5) (tabbedLayout''' ||| Dwindle R CW 1 (5 % 100)) $
537 onWorkspace (wsp 8) tabbedLayout''' $
538 onWorkspace (wsp 10) (tabbedLayout''' ||| combineTwoP (TwoPane (1 % 100) (3 % 4)) tabbedLayout''' tabbedLayout''' (ClassName "mpv") ||| Dwindle R CW 1 (5 % 100)) $
539 defaultLayouts
540 ) $
541 defaultLayouts
542 -- tabbedLayout''' = renamed [Replace "Tabbed'"] $ IfMax 1 (noBorders Full) (tabbedLayout tabbedBottomAlways)
543 tabbedLayout''' = tabbedLayout tabbedBottom
544 tabbedLayout' = tabbedLayout tabbedBottomAlways
545 tabbedLayoutHoriz' = tabbedLayoutHoriz tabbedLeftAlways
546 defaultLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW 1 (5 % 100) ||| tabbedLayout' ||| Full
547 -- workLayouts = {- spiralWithDir East CW (1 % 2) -} Dwindle R CW (2 % 1) (5 % 100) ||| tabbedLayout' ||| Full
548 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)
549 sqrtTwo = approxRational (sqrt 2) (1 / 2560)
550 xmobarPP' = xmobarPP { ppTitle = shorten 80
551 , ppSort = (liftM2 (.)) getSortByIndex $ return scratchpadFilterOutWorkspace
552 , ppUrgent = wrap "(" ")" . xmobarColor "#800000" ""
553 , ppHiddenNoWindows = xmobarColor "#202020" "" . wrap "(" ")"
554 , ppVisible = wrap "(" ")" . xmobarColor "#808000" ""
555 , ppCurrent = wrap "(" ")" . xmobarColor "#008000" ""
556 , ppHidden = wrap "(" ")"
557 , ppWsSep = " "
558 , ppSep = "\n"
559 }
560 withHostUrgency = case hostname of
561 "hel" -> withUrgencyHookC urgencyHook' $ urgencyConfig { suppressWhen = U.Never, remindWhen = Dont }
562 "sif" -> withUrgencyHookC urgencyHook' $ urgencyConfig { suppressWhen = U.Never, remindWhen = Dont }
563 _ -> id
564 urgencyHook' window = do
565 runQuery ((resource =? "comm" <||> resource =? "Pidgin" <||> className =? "Gajim" <||> className =? "Skype") --> safeSpawn "thinklight" ["Blink", "100"]) window
566 urgencyHook (BorderUrgencyHook { urgencyBorderColor = "#bb0000" }) window
567 shutdown :: SomeException -> IO a
568 shutdown e = do
569 let pids = [ -- batteryMon
570 ]
571 mapM_ (signalProcess sigTERM) pids
572 mapM_ (getProcessStatus False False) pids
573 throw e
574 keyUpEventHook :: Event -> X All
575 keyUpEventHook event = handle event >> return (All True)
576 where
577 handle (KeyEvent { ev_event_type = t, ev_state = m, ev_keycode = code })
578 | t == keyRelease = withDisplay $ \dpy -> do
579 s <- io $ keycodeToKeysym dpy code 0
580 mClean <- cleanMask m
581 ks <- asks $ hKeyUpKeys host . config
582 userCodeDef () $ whenJust (Map.lookup (mClean, s) ks) id
583 | otherwise = return ()
584 handle _ = return ()
585 handle shutdown $ launch myConfig
586
587secs :: Int -> Int
588secs = (* 1000000)
589
590-- monitorBattery :: Maybe BatteryContext -> Maybe Notification -> IO ()
591-- monitorBattery Nothing n = do
592-- ctx <- batteryContextNew
593-- case ctx of
594-- Nothing -> threadDelay (secs 10) >> monitorBattery Nothing n
595-- Just _ -> monitorBattery ctx n
596-- monitorBattery ctx@(Just ctx') n = do
597-- batInfo <- getBatteryInfo ctx'
598-- case batInfo of
599-- Nothing -> threadDelay (secs 1) >> monitorBattery ctx n
600-- Just batInfo -> do
601-- let n'
602-- | batteryState batInfo == BatteryStateDischarging
603-- , timeLeft <= 1200
604-- , timeLeft > 0 = Just $ summary "Discharging" <> hint "value" percentage <> urgency u <> body (duz timeLeft ++ "left")
605-- | otherwise = Nothing
606-- u
607-- | timeLeft <= 600 = Critical
608-- | timeLeft <= 1800 = Normal
609-- | otherwise = Low
610-- timeLeft = batteryTimeToEmpty batInfo
611-- percentage :: Int32
612-- percentage = round $ batteryPercentage batInfo
613-- ts = [("s", 60), ("m", 60), ("h", 24), ("d", 365), ("y", 1)]
614-- duz ms = ss
615-- 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
616-- case n' of
617-- Just n' -> Notify.display (maybe mempty reuse n <> Notify.appName "monitorBattery" <> n') >>= (\n -> threadDelay (secs 2) >> monitorBattery ctx (Just n))
618-- Nothing -> threadDelay (secs 30) >> monitorBattery ctx n
619
620disableTouchpad, disableTrackpoint, enableTrackpoint, enableTouchpad :: X ()
621enableTouchpad = safeSpawn "xinput" ["enable", "SynPS/2 Synaptics TouchPad"]
622disableTouchpad = safeSpawn "xinput" ["disable", "SynPS/2 Synaptics TouchPad"]
623enableTrackpoint = safeSpawn "xinput" ["enable", "TPPS/2 IBM TrackPoint"]
624disableTrackpoint = safeSpawn "xinput" ["disable", "TPPS/2 IBM TrackPoint"]
625
626isDisabled :: String -> X Bool
627isDisabled str = do
628 out <- runProcessWithInput "xinput" ["list", str] ""
629 return $ "disabled" `isInfixOf` out
630
631
632spawnKeychain :: X ()
633spawnKeychain = do
634 home <- liftIO getHomeDirectory
635 let keys = (map ((home </>) . (".ssh/" ++)) ["id", "id-rsa"]) ++ ["6B13AA67"]
636 liftIO (maybe (return ()) (setEnv "SSH_ASKPASS") =<< findAskpass)
637 safeSpawn "keychain" . (["--agents", "gpg,ssh"] ++)=<< liftIO (filterM doesFileExist keys)
638 where
639 findAskpass = filter `liftM` readFile "/etc/zshrc"
640 filter = listToMaybe . catMaybes . map (stripPrefix "export SSH_ASKPASS=") . lines
641
642assimilateKeychain :: X ()
643assimilateKeychain = liftIO $ assimilateKeychain' >> return ()
644assimilateKeychain' = tryIOError $ do
645 -- pid <- getProcessID
646 -- tmpDir <- lookupEnv "TMPDIR"
647 -- let tmpDir' = fromMaybe "/tmp" tmpDir
648 -- tmpFile = tmpDir' </> "xmonad-keychain" ++ (show pid) ++ ".env"
649 env <- runProcessWithInput "sh" ["-c", "eval $(keychain --eval --noask --agents gpg,ssh); env"] "" -- > " ++ tmpFile] ""
650 -- env <- readFile tmpFile
651 let envVars = Map.fromList $ map (\(k, v) -> (k, tail' v)) $ map (span (/= '=')) $ envLines
652 envVars' = Map.filterWithKey (\k _ -> k `elem` transfer) envVars
653 transfer = ["SSH_AUTH_SOCK", "SSH_AGENT_PID", "GPG_AGENT_INFO"]
654 envLines = filter (elem '=') $ lines env :: [String]
655 sequence $ map (\(k, c) -> setEnv k c) $ Map.toList envVars'
656 -- removeFile tmpFile
657 where
658 tail' [] = []
659 tail' (x:xs) = xs
660
661
662numKeys = [xK_parenleft, xK_parenright, xK_braceright, xK_plus, xK_braceleft, xK_bracketright, xK_bracketleft, xK_exclam, xK_equal, xK_asterisk]
663
664instance Shrinker CustomShrink where
665 shrinkIt _ "" = [""]
666 shrinkIt s cs
667 | length cs >= 4 = cs : shrinkIt s ((reverse . drop 4 . reverse $ cs) ++ "...")
668 | otherwise = cs : shrinkIt s (init cs)
669
670xPConfig :: XPConfig
671xPConfig = def
672 { font = "xft:Fira Mono for Powerline:style=Medium:pixelsize=22.5"
673 , height = 32
674 , bgColor = "black"
675 , fgColor = "grey"
676 , fgHLight = "green"
677 , bgHLight = "black"
678 , borderColor = "grey"
679 , searchPredicate = (\needle haystack -> all (`isInfixOf` map toLower haystack) . map (map toLower) $ words needle)
680 , position = Top
681 }
682
683sshOverrides host = map (\h -> mkOverride { oHost = h, oCommand = moshCmd . inTmux host} )
684 [ "odin"
685 , "ymir"
686 , "surtr"
687 ]
688 ++
689 map (\h -> mkOverride { oHost = h, oCommand = moshCmd' "/run/current-system/sw/bin/mosh-server" . withEnv [("TERM", "xterm")] . inTmux host} )
690 [ "bragi", "bragi.asgard.yggdrasil"
691 ]
692 ++
693 map (\h -> mkOverride { oHost = h, oCommand = sshCmd . inTmux host } )
694 [ "uni2work-dev1"
695 ]
696 ++
697 map (\h -> mkOverride { oHost = h, oCommand = sshCmd . withEnv [("TERM", "xterm")] . inTmux host } )
698 [ "remote.cip.ifi.lmu.de"
699 , "uniworx3", "uniworx4", "uniworx5", "uniworxdb2"
700 , "testworx"
701 ]
702
703backlight :: (Rational -> Rational) -> X ()
704backlight f = void . xfork . liftIO $ do
705 [ _device
706 , _class
707 , read . Text.unpack -> currentBright
708 , _currentPercentage
709 , read . Text.unpack -> maximumBright
710 ] <- Text.splitOn "," . Text.pack <$> readProcess "brightnessctl" ["-m"] ""
711 let current = currentBright % maximumBright
712 new' = f current * fromIntegral maximumBright
713 new :: Integer
714 new | floor new' < 0 = 0
715 | ceiling new' > maximumBright = maximumBright
716 | new' >= maximumBright % 2 = ceiling new'
717 | otherwise = floor new'
718 callProcess "brightnessctl" ["-m", "s", show new]
719
720cycleThrough :: [Rational] -> (Rational -> Rational)
721cycleThrough opts current = fromMaybe currentOpt $ listToMaybe next'
722 where currentOpt = minimumBy (comparing $ abs . subtract current) opts
723 (_, _ : next') = break (== currentOpt) opts
724
725cycleKbLayout :: [(String, Maybe String)] -> X ()
726cycleKbLayout [] = return ()
727cycleKbLayout layouts = liftIO $ do
728 next <- (getNext . extract) `liftM` runProcessWithInput "setxkbmap" ["-query"] ""
729 let
730 args = case next of
731 (l, Just v) -> [l, v]
732 (l, Nothing) -> [l]
733 safeSpawn "setxkbmap" args
734 where
735 extract :: String -> Maybe (String, Maybe String)
736 extract str = listToMaybe $ do
737 ["layout:", l] <- str'
738 [(l, Just v) | ["variant:", v] <- str'] ++ pure (l, Nothing)
739 where
740 str' = map words $ lines str
741 getNext :: Maybe (String, Maybe String) -> (String, Maybe String)
742 getNext = maybe (head layouts) getNext'
743 getNext' x = case elemIndex x layouts of
744 Nothing -> getNext Nothing
745 Just i -> layouts !! ((i + 1) `mod` length layouts)
746
747mpvAll' :: MpvCommand -> IO [MpvResponse]
748mpvAll' = mpvAll "/var/media/.mpv-ipc"
749
750mpvOne' :: MpvCommand -> IO (Maybe MpvResponse)
751mpvOne' = mpvOne "/var/media/.mpv-ipc"
752
753mediaMpv :: MpvCommand -> X ()
754mediaMpv cmd = void . xfork $ print =<< mpvAll' cmd
755
756mediaMpvTogglePause :: X ()
757mediaMpvTogglePause = void . xfork $ do
758 paused <- mapM mpvResponse <=< mpvAll' $ MpvGetProperty "pause"
759 if
760 | and paused -> print <=< mpvAll' $ MpvSetProperty "pause" False
761 | otherwise -> print <=< mpvOne' $ MpvSetProperty "pause" True
762
763myKeys' conf host = Map.fromList $
764 -- launch a terminal
765 [ ((modm, xK_Return), spawn $ (XMonad.terminal conf) ++ " -e tmux")
766 , ((modm .|. shiftMask, xK_Return), spawn $ XMonad.terminal conf)
767
768 -- launch dmenu
769 --, ((modm, xK_d ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"")
770 , ((modm, xK_d ), shellPrompt "Run: " xPConfig)
771 , ((modm .|. shiftMask, xK_d ), prompt "Run in Terminal: " ("urxvtc" ++ " -e") xPConfig)
772 , ((modm, xK_at ), sshPrompt (sshOverrides . Just $ hName host) xPConfig)
773
774 -- close focused window
775 , ((modm .|. shiftMask, xK_q ), kill)
776 , ((modm .|. controlMask .|. shiftMask, xK_q ), spawn "xkill")
777
778 -- Rotate through the available layout algorithms
779 , ((modm, xK_space ), sendMessage NextLayout)
780
781 -- Reset the layouts on the current workspace to default
782 , ((modm .|. controlMask, xK_r ), (setLayout $ XMonad.layoutHook conf) >> refresh)
783
784 -- Resize viewed windows to the correct size
785 , ((modm, xK_r ), refresh)
786
787 -- Move focus to the next window
788 , ((modm, xK_t ), windows W.focusDown)
789
790 -- Move focus to the previous window
791 , ((modm, xK_n ), windows W.focusUp )
792
793 -- Move focus to the master window
794 , ((modm, xK_m ), windows W.focusMaster )
795
796 -- Swap the focused window and the master window
797 , ((modm .|. shiftMask, xK_m ), windows W.swapMaster)
798
799 -- Swap the focused window with the next window
800 , ((modm .|. shiftMask, xK_t ), windows W.swapDown )
801
802 -- Swap the focused window with the previous window
803 , ((modm .|. shiftMask, xK_n ), windows W.swapUp )
804
805 -- Swap the focused window with the previous window
806 , ((modm .|. shiftMask .|. controlMask, xK_m), sendMessage SwapWindow)
807
808 , ((modm, xK_Right), sendMessage $ Go R)
809 , ((modm, xK_Left ), sendMessage $ Go L)
810 , ((modm, xK_Up ), sendMessage $ Go U)
811 , ((modm, xK_Down ), sendMessage $ Go D)
812 , ((modm .|. shiftMask , xK_Right), sendMessage $ Move R)
813 , ((modm .|. shiftMask , xK_Left ), sendMessage $ Move L)
814 , ((modm .|. shiftMask , xK_Up ), sendMessage $ Move U)
815 , ((modm .|. shiftMask , xK_Down ), sendMessage $ Move D)
816 -- , ((modm .|. controlMask, xK_Right), withFocused $ keysMoveWindow (10, 0))
817 -- , ((modm .|. controlMask, xK_Left ), withFocused $ keysMoveWindow (-10, 0))
818 -- , ((modm .|. controlMask, xK_Up ), withFocused $ keysMoveWindow (0, -10))
819 -- , ((modm .|. controlMask, xK_Down ), withFocused $ keysMoveWindow (0, 10))
820 -- Shrink the master area
821 , ((modm, xK_h ), sendMessage Shrink)
822
823 -- Expand the master area
824 , ((modm, xK_s ), sendMessage Expand)
825
826 -- Push window back into tiling
827 , ((modm .|. shiftMask, xK_space ), withFocused $ windows . W.sink)
828 , ((modm, xK_BackSpace), focusUrgent)
829 , ((modm .|. shiftMask, xK_BackSpace), clearUrgents)
830
831 -- Increment the number of windows in the master area
832 , ((modm , xK_comma ), sendMessage (IncMasterN 1))
833
834 -- Deincrement the number of windows in the master area
835 , ((modm , xK_period), sendMessage (IncMasterN (-1)))
836
837 , ((0, xF86XK_AudioRaiseVolume), safeSpawn "pulseaudio-ctl" ["up", "2"])
838 , ((0, xF86XK_AudioLowerVolume), safeSpawn "pulseaudio-ctl" ["down", "2"])
839 , ((0, xF86XK_AudioMute), safeSpawn "pulseaudio-ctl" ["mute"])
840 , ((0, xF86XK_AudioPause), mediaMpv $ MpvSetProperty "pause" False)
841 , ((0, {-xF86XK_AudioMicMute-} 269025202), safeSpawn "pulseaudio-ctl" ["mute-input"])
842 , ((0, xF86XK_AudioPlay), mediaMpvTogglePause)
843 , ((modm .|. mod1Mask, xK_space), mediaMpvTogglePause)
844
845 , ((0, xF86XK_MonBrightnessDown), backlight (subtract 5))
846 , ((0, xF86XK_MonBrightnessUp), backlight (+ 5))
847
848 , ((modm , xK_Escape), cycleKbLayout (hKbLayouts host))
849 , ((modm .|. controlMask, xK_Escape), safeSpawn "setxkbmap" $ fst (head $ hKbLayouts host) : maybeToList (snd . head $ hKbLayouts host))
850
851 -- Toggle the status bar gap
852 -- Use this binding with avoidStruts from Hooks.ManageDocks.
853 -- See also the statusBar function from Hooks.DynamicLog.
854 --
855 , ((modm , xK_b ), sendMessage ToggleStruts)
856
857 , ((modm .|. shiftMask, xK_p ), safeSpawn "playerctl" ["-a", "pause"])
858
859 -- Quit xmonad
860 , ((modm .|. shiftMask, xK_e ), io (exitWith ExitSuccess))
861
862 -- Restart xmonad
863 -- , ((modm .|. shiftMask .|. controlMask, xK_r ), void . xfork $ recompile False >>= flip when (safeSpawn "xmonad" ["--restart"]))
864 , ((modm .|. shiftMask, xK_r ), void . liftIO $ executeFile "xmonad" True [] Nothing)
865 , ((modm .|. shiftMask, xK_l ), void . xfork $ do
866 sessId <- getEnv "XDG_SESSION_ID"
867 safeSpawn "loginctl" ["lock-session", sessId]
868 )
869 , ((modm .|. shiftMask, xK_s ), safeSpawn "systemctl" ["suspend"])
870 , ((modm .|. shiftMask, xK_h ), safeSpawn "systemctl" ["hibernate"])
871 , ((modm .|. shiftMask, xK_b ), backlight $ cycleThrough [1, 3 % 4, 1 % 2, 1 % 4, 1 % 10, 1 % 100, 0]
872 )
873 , ((modm .|. shiftMask .|. controlMask, xK_b), backlight $ cycleThrough [0, 1 % 100, 1 % 10, 1 % 4, 1 % 2, 3 % 4, 1]
874 )
875 , ((modm, xK_v ), windows copyToAll) -- @@ Make focused window always visible
876 , ((modm .|. shiftMask, xK_v ), killAllOtherCopies) -- @@ Toggle window state back
877 , ((modm .|. shiftMask, xK_g ), windowPrompt xPConfig Goto wsWindows)
878 , ((modm .|. shiftMask .|. controlMask, xK_g ), windowPrompt xPConfig Bring allWindows)
879 ]
880 ++
881
882 --
883 -- mod-[1..9], Switch to workspace N
884 --
885 -- mod-[1..9], Switch to workspace N
886 -- mod-shift-[1..9], Move client to workspace N
887 --
888 [((m .|. modm, k), windows $ f i)
889 | (i, k) <- zip (XMonad.workspaces conf) $ numKeys
890 , (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask)]
891 ]
892 ++
893 [((m .|. modm .|. controlMask, k), void . runMaybeT $
894 MaybeT (P.getScreen def i) >>= MaybeT . screenWorkspace >>= lift . windows . f
895 )
896 | (i, k) <- zip (hScreens host) [xK_g, xK_c, xK_r, xK_l]
897 , (f, m) <- [(W.view, 0), (W.shift, shiftMask)]
898 ]
899 where
900 modm = XMonad.modMask conf
901
902
diff --git a/accounts/gkleen@sif/xresources.nix b/accounts/gkleen@sif/xresources.nix
new file mode 100644
index 00000000..3bd9af2c
--- /dev/null
+++ b/accounts/gkleen@sif/xresources.nix
@@ -0,0 +1,46 @@
1{
2 "Xft.dpi" = 282;
3 "Xft.autohint" = false;
4 "Xft.lcdfilter" = "lcddefault";
5 "Xft.hintstyle" = "hintfull";
6 "Xft.hinting" = true;
7 "Xft.antialias" = true;
8 "Xft.rgba" = "rgb";
9
10 # special
11 "*.foreground" = "#d9d9d9";
12 "*.background" = "#000000";
13 "*.cursorColor" = "#d9d9d9";
14
15 # black
16 "*.color0" = "#000000";
17 "*.color8" = "#757a80";
18
19 # red
20 "*.color1" = "#bf4949";
21 "*.color9" = "#e66e6e";
22
23 # green
24 "*.color2" = "#9fb346";
25 "*.color10" = "#cbd676";
26
27 # yellow
28 "*.color3" = "#e69650";
29 "*.color11" = "#ffa74f";
30
31 # blue
32 "*.color4" = "#759fbf";
33 "*.color12" = "#98b8d9";
34
35 # magenta
36 "*.color5" = "#9b79a6";
37 "*.color13" = "#ceadd9";
38
39 # cyan
40 "*.color6" = "#79a69b";
41 "*.color14" = "#a3d9ce";
42
43 # white
44 "*.color7" = "#d9d9d9";
45 "*.color15" = "#ffffff";
46} \ No newline at end of file
diff --git a/accounts/gkleen@sif/zshrc b/accounts/gkleen@sif/zshrc
new file mode 100644
index 00000000..d4d75073
--- /dev/null
+++ b/accounts/gkleen@sif/zshrc
@@ -0,0 +1,410 @@
1filebin() {
2 basePath=/srv/www/files
3 ssh ymir find /srv/www/files -type f -printf "$'%T@ %TY-%Tm-%TdT%TH:%TM %P\\\\0'" | sort -zn | cut -z -d ' ' -f 2- \
4 | while IFS= read -r -d $'\0' l; do
5 IFS=' ' read -r t p <<<"${l}"
6 printf "%s https://f.141.li/%s\n" "${t}" "${p}"
7 done
8}
9
10push2bin() {
11 if [[ ${#@} -eq 1 && ! -r ${1} ]]; then
12 uux -p 'ymir!push2bin' $(echo -n "${1:t}" | tr -c $'[:alnum:]+-=.' '_')
13 else
14 for f (${@}); do
15 uux -p 'ymir!push2bin' $(echo -n "${f:t}" | tr -c $'[:alnum:]+-=.' '_') <${f}
16 done
17 fi
18}
19
20genmail() {
21 local baseName=""
22 local target=""
23 if [[ ${#@} -ge 1 ]]; then
24 target=${1}
25 shift
26 fi
27
28 if [[ ${#@} -ge 1 ]]; then
29 baseName=$(pwgen ${@})
30 else
31 baseName=$(pwgen -v -A -0 16 1)
32 fi
33 baseName=$(tr -cd $'[:alnum:]!#$%&*+-/=?^_{|}~.' <<<${baseName})
34 address=${baseName}@141.li
35 insertAddr() {
36 echo "${baseName} gkleen" | ssh ymir tee -a /srv/mail/spm 1>/dev/null \
37 }
38
39 printf "%s\n" ${address}
40 read -q 'cont?Continue [y/N]? ' || return
41
42 insertAddr
43}
44
45s() {
46 dir=$(pwd)
47 [[ ${#@} -ge 1 ]] && dir=$1
48
49 shellFile=$(findNix ${@})
50 [[ ${#@} -ge 1 ]] && shift
51
52 typeset -a cmd
53 if [[ -d ${dir}/.nix-gc-roots ]]; then
54 cmd=(persistent-nix-shell ${shellFile} ${S_EXTRA_ARGS} ${@})
55 else
56 cmd=(nix-shell ${shellFile} ${S_EXTRA_ARGS} ${@})
57 fi
58
59 if [[ -n "${S_SYSTEMD}" ]]; then
60 systemd-run --user --slice=development.slice --collect -E PATH=${PATH} -p WorkingDirectory=${dir} -- ${cmd}
61 else
62 (
63 cd ${dir}
64
65 exec ${cmd}
66 )
67 fi
68}
69
70sz() {
71 typeset -a S_EXTRA_ARGS
72 S_EXTRA_ARGS=(--run "env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
73}
74st() {
75 typeset -a S_EXTRA_ARGS
76 S_EXTRA_ARGS=(--run "tmux new-session env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
77}
78stt() {
79 typeset -a S_EXTRA_ARGS
80 S_SYSTEMD=true S_EXTRA_ARGS=(--run "urxvt -e tmux -S .tmux.sock new-session env __ETC_ZSHENV_SOURCED=1 zsh") s ${@}
81}
82se() {
83 typeset -a S_EXTRA_ARGS
84 S_SYSTEMD=true S_EXTRA_ARGS=(--run "emacs") s ${@}
85}
86
87findNix() {
88 if [[ $#@ -eq 0 ]]; then
89 findNix $(pwd)
90 elif [[ -f "$1" ]]; then
91 print ${1:a}
92 elif [[ -d "$1" && -f "$1"/shell.nix ]]; then
93 print ${1:a}/shell.nix
94 elif [[ -d "$1" && -f "$1"/default.nix ]]; then
95 print ${1:a}/default.nix
96 elif [[ -d "$1" && "$1" != "/" ]]; then
97 findNix ${1:h}
98 else
99 printf "Traversed directories to ‘/’ and found no shell specification\n" >&2
100 return 1
101 fi
102}
103
104dir() {
105 curlArchive=false
106 templateArchive=""
107 repoUrl=""
108 nixShell=""
109 findNix=false
110 dir=""
111 forceShell=false
112 wormhole=false
113 gitWorktree=""
114 notmuchMsg=""
115 quickserve=false
116
117 while getopts ':t:a:s:Sd:ir:wqg:n:' arg; do
118 case $arg in
119 "t") ;;
120 "a")
121 if [[ ${OPTARG} =~ "://" ]]; then
122 templateArchive=${OPTARG}
123 curlArchive=true
124 else
125 templateArchive=${OPTARG:a}
126 fi
127 ;;
128 "s") nixShell=${OPTARG:a} ;;
129 "S") findNix=true ;;
130 "d") dir=${OPTARG} ;;
131 "i") forceShell=true ;;
132 "r") repoUrl=${OPTARG} ;;
133 "w") wormhole=true ;;
134 "g") gitWorktree=${OPTARG} ;;
135 "n") notmuchMsg=${OPTARG} ;;
136 "q") quickserve=true ;;
137 *) printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
138 esac
139 done
140
141 shift $((OPTIND - 1))
142
143 if [[ -z ${dir} && ${#@} -ge 1 ]]; then
144 dir=${1}
145 shift
146 fi
147
148 [[ -n ${dir} ]] || return 2;
149
150 if [[ ! -e ${dir} ]]; then
151 if [[ -z "${gitWorktree}" ]]; then
152 mkdir -vp ${dir}
153 else
154 git -C ${gitWorktree} worktree add ${dir}
155 fi
156 else
157 gitWorktree=""
158 fi
159
160 (
161 cd ${dir}
162 export dir;
163
164 ${findNix} && { nixShell=$(findNix) || return $? }
165
166 [[ -n ${repoUrl} ]] && git clone -- ${repoUrl} .
167
168 if [[ -n ${templateArchive} ]]; then
169 (
170 archiveFile=""
171 cleanup() {
172 [[ -n "${archiveFile}" ]] && rm -fv ${archiveFile}
173 }
174 trap cleanup EXIT
175
176 if ${curlArchive}; then
177 archiveFile=$(mktemp -t "archive.XXXXXXXXXX.${templateArchive:t}")
178
179 curl -L -o ${archiveFile} ${templateArchive}
180
181 templateArchive=${archiveFile}
182 fi
183
184 case $(file --brief --mime-type ${templateArchive}) in
185 application/zip) unzip ${templateArchive} ;;
186 *) tar -xvaf ${templateArchive} ;;
187 esac
188 )
189 fi
190
191
192 if [[ -n ${notmuchMsg} ]]; then
193 getMimeTypes() {
194 nix-shell -p mailcap --run "find \${buildInputs} -path '*/etc/mime.types' | head -n 1 | xargs -- cat"
195 }
196
197 typeset -a messages
198 messages=(${(z)$(notmuch search --output=messages ${notmuchMsg})})
199
200 for message (${messages}); do
201 typeset -A notmuchAtts
202 notmuchAtts=()
203
204 while IFS= read -r -d $'\n' line; do
205 [[ ${line} =~ '(attachment|part)\{ ID: ([0-9]+)' ]] || continue
206 attId=${match[2]}
207
208 [[ ${line} =~ 'Content-type: multipart/' ]] && continue
209
210 fName="part_${attId}"
211 [[ ${line} =~ 'Filename: (([^,]|,[^ ])+)' ]] && fName=${match[1]}
212
213 if [[ ${#messages} -gt 1 ]]; then
214 fName="${message}/${fName}"
215 fi
216
217 fExt="${fName:e}"
218 [[ -n "${fExt}" ]] && fName="${fName:r}"
219
220 if [[ -z "${fExt}" && ${line} =~ 'Content-type: (([^,]|,[^ ])+)$' ]]; then
221 fExt=$(getMimeTypes | grep ${match[1]}$'\t' | head -n 1 | awk '{ print $2; }')
222 fi
223
224 mkdir -p ${fName:h}
225 if [[ -n "${fExt}" ]]; then
226 fName=$(mktemp -p . "${fName}.XXXXXX.${fExt}")
227 else
228 fName=$(mktemp -p . "${fName}.XXXXXX")
229 fi
230
231 notmuchAtts[${attId}]=${fName}
232 done <<(notmuch show --decrypt=false -- ${message} | tr -d $'\f')
233
234 for attId fName in ${(kv)notmuchAtts}; do
235 [[ -d ${fName:h} ]] || mkdir -p ${fName:h}
236 printf "#%d → ‘%s’\n" "${attId}" "${fName}" >&2
237
238 notmuch show --decrypt=false --part=${attId} -- ${message} | pv -W -D 2 -i 0.1 >${fName}
239 done
240 done
241 fi
242
243
244 ${wormhole} && wormhole receive
245
246 if ${quickserve}; then
247 quickserve --root . --upload . --show-hidden --tar gz
248 fi
249
250
251 if [[ ${#@} -eq 0 ]] || ${forceShell}; then
252 if [[ ${#@} -gt 0 ]]; then
253 if [[ -z ${nixShell} ]]; then
254 ${@}
255 else
256 nix-shell ${nixShell} --run "${@}"
257 fi
258 fi
259
260 cd $(pwd) # Needed for mounting to work
261
262 isSingleDir() {
263 typeset -a contents
264 contents=(*(N) .*(N))
265
266 if [[ ${#contents} -eq 1 && -d ${contents[1]} ]]; then
267 print ${contents[1]}
268 return 0
269 else
270 return 1
271 fi
272 }
273 while d=$(isSingleDir); do cd ${d}; done
274
275
276 if [[ -z ${nixShell} ]]; then
277 exec -- zsh
278 else
279 exec -- nix-shell ${nixShell} --run zsh
280 fi
281 else
282 if [[ -z ${nixShell} ]]; then
283 exec -- ${@}
284 else
285 exec -- nix-shell ${nixShell} --run "${@}"
286 fi
287 fi
288 )
289}
290
291tmpdir() {
292 cleanup()
293 {
294 cd /
295 unmount() {
296 printf "Unmounting %s\n" ${1} >&2
297 fusermount -u ${1} || umount ${1} || sudo umount ${1}
298 }
299
300 if mountpoint -q -- ${dir}; then
301 unmount ${dir} || return $?
302 else
303 while read -d $'\0' subDir; do
304 mountpoint -q -- ${subDir} || continue
305 unmount ${subDir} || return $?
306 done <<<$(find ${dir} -xdev -type d -print0 | sort -zr)
307 fi
308
309 rm -rfv --one-file-system -- ${dir}
310 }
311
312 local tmpdir=""
313
314 while getopts ':t:a:s:Sd:ir:wqg:n:' arg; do
315 case $arg in
316 "t") tmpdir="=${OPTARG}" ;;
317 "?"|":") printf "Invalid option: %s\n" $arg >&2; exit 2 ;;
318 esac
319 done
320
321 (
322 trap cleanup EXIT
323
324
325 local dir=$(mktemp -ud --tmpdir${tmpdir} ${0}.XXXXXXXXXX || return $?)
326
327 dir -d ${dir} ${@}
328 )
329}
330
331inhibit-sleep() {
332 if systemctl --user is-active prevent-suspend.service 1>/dev/null; then
333 echo "Allowing suspend"
334 systemctl --user stop prevent-suspend.service
335 else
336 echo "Inhibiting suspend"
337 systemctl --user start prevent-suspend.service
338 fi
339}
340
341qr() {
342 qrencode -l M -o - -t ANSIUTF8 $@
343}
344
345clock() {
346 tty-clock -sSbc -C 7 -d 0 -a 100000000
347}
348
349public-ip() {
350 curl -s -H 'Accept: application/json' $@ ifconfig.co | jq -r '.ip'
351}
352
353nix-ghci() {
354 pkgExpr=""
355 if [[ ${#@} -gt 0 ]]; then
356 pkgExpr="${1}"
357 shift
358 fi
359
360 nix-shell -p "with (import <nixpkgs> {}); pkgs.haskellPackages.ghcWithPackages (p: with p; [${pkgExpr}])" --run "ghci ${@}"
361}
362
363swap() {
364 f1=${1}
365 f2=${2}
366
367 if [[ -z "${f1}" || ! -e "${f1}" ]]; then
368 printf "‘%s’ does not exist\n" "${f1}" >&2
369 return 2
370 fi
371 if [[ -z "${f2}" || ! -e "${f2}" ]]; then
372 printf "‘%s’ does not exist\n" "${f2}" >&2
373 return 2
374 fi
375
376 tmpfile=$(mktemp --dry-run --tmpdir=${f1:h} .swap.XXXXXXXXXX)
377 mv -v ${f1} ${tmpfile}
378 mv -v ${f2} ${f1}
379 mv -v ${tmpfile} ${f2}
380}
381
382l() {
383 exa --binary --git --time-style=iso --long --all --header --group-directories-first --colour=always $@ | less --mouse -FR
384}
385
386re() {
387 systemctl --restart $@
388}
389
390ure() {
391 systemctl --user --restart $@
392}
393
394u2wdb() {
395 ssh -t postgres@uniworxdb2 psql uni2work
396}
397
398alias '..'='cd ..'
399alias -g L='| less'
400alias -g S='&> /dev/null'
401alias -g G='| grep'
402alias -g B='&> /dev/null &'
403alias -g BB='&> /dev/null &!'
404
405export DEFAULT_USER=gkleen
406export EDITOR=emacsclient
407
408bindkey -e
409bindkey ';5C' emacs-forward-word
410bindkey ';5D' emacs-backward-word \ No newline at end of file