@ -0,0 +1,7 @@ | |||
config/doom/private.el | |||
config/doom/config.el | |||
config/doom/blog.el | |||
config/doom/packages.el | |||
*.elc | |||
system/wg-privkey | |||
config/mpdscribble/* |
@ -0,0 +1,16 @@ | |||
etc | |||
--- | |||
My personal dotfiles. | |||
This branch is dedicated to my experiments with NixOS. It will likely be merged | |||
into master momentarily, as my new system runs NixOS. | |||
To install it: | |||
- Copy things *selectively* in `system/` to `/etc/nixos` -- much of this is | |||
only relevant if you are on my machine | |||
- ln -s "$(pwd)" ~/.config/nixpkgs | |||
- `home-manager switch` | |||
...but why would you install my dotfiles? They're mine, not yours. Go away. | |||
 |
@ -0,0 +1,24 @@ | |||
This is free and unencumbered software released into the public domain. | |||
Anyone is free to copy, modify, publish, use, compile, sell, or | |||
distribute this software, either in source code form or as a compiled | |||
binary, for any purpose, commercial or non-commercial, and by any | |||
means. | |||
In jurisdictions that recognize copyright laws, the author or authors | |||
of this software dedicate any and all copyright interest in the | |||
software to the public domain. We make this dedication for the benefit | |||
of the public at large and to the detriment of our heirs and | |||
successors. We intend this dedication to be an overt act of | |||
relinquishment in perpetuity of all present and future rights to this | |||
software under copyright law. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |||
OTHER DEALINGS IN THE SOFTWARE. | |||
For more information, please refer to <http://unlicense.org/> |
@ -0,0 +1,36 @@ | |||
! xft font rendering | |||
Xft.antialias: 1 | |||
Xft.hinting: 1 | |||
Xft.hintstyle: hintfull | |||
Xft.lcdfilter: lcddefault | |||
Xft.rgba: rgb | |||
! gruvbox | |||
! hard contrast: *background: #f9f5d7 | |||
*background: #fbf1c7 | |||
! soft contrast: *background: #f2e5bc | |||
*foreground: #3c3836 | |||
! Black + DarkGrey | |||
*color0: #fdf4c1 | |||
*color8: #928374 | |||
! DarkRed + Red | |||
*color1: #cc241d | |||
*color9: #9d0006 | |||
! DarkGreen + Green | |||
*color2: #98971a | |||
*color10: #79740e | |||
! DarkYellow + Yellow | |||
*color3: #d79921 | |||
*color11: #b57614 | |||
! DarkBlue + Blue | |||
*color4: #458588 | |||
*color12: #076678 | |||
! DarkMagenta + Magenta | |||
*color5: #b16286 | |||
*color13: #8f3f71 | |||
! DarkCyan + Cyan | |||
*color6: #689d6a | |||
*color14: #427b58 | |||
! LightGrey + White | |||
*color7: #7c6f64 | |||
*color15: #3c3836 |
@ -0,0 +1,202 @@ | |||
## hazel's alacritty configuration | |||
window: | |||
dimensions: | |||
columns: 80 | |||
lines: 24 | |||
padding: | |||
x: 10 | |||
y: 10 | |||
decorations: full | |||
# When true, bold text is drawn using the bright variant of colors. | |||
draw_bold_text_with_bright_colors: true | |||
font: | |||
normal: | |||
family: FiraCode | |||
style: Regular | |||
bold: | |||
family: FiraCode | |||
style: Regular | |||
italic: | |||
family: FiraCode | |||
style: Regular | |||
size: 10.0 | |||
offset: | |||
x: 0 | |||
y: 0 | |||
glyph_offset: | |||
x: 0 | |||
y: 0 | |||
# Colors (Gruvbox light) | |||
colors: | |||
# Default colors | |||
primary: | |||
# hard contrast: background = '0xf9f5d7' | |||
background: '0xfbf1c7' | |||
# soft contrast: background = '0xf2e5bc' | |||
foreground: '0x3c3836' | |||
# Normal colors | |||
normal: | |||
black: '0xfbf1c7' | |||
red: '0xcc241d' | |||
green: '0x98971a' | |||
yellow: '0xd79921' | |||
blue: '0x458588' | |||
magenta: '0xb16286' | |||
cyan: '0x689d6a' | |||
white: '0x7c6f64' | |||
# Bright colors | |||
bright: | |||
black: '0x928374' | |||
red: '0x9d0006' | |||
green: '0x79740e' | |||
yellow: '0xb57614' | |||
blue: '0x076678' | |||
magenta: '0x8f3f71' | |||
cyan: '0x427b58' | |||
white: '0x3c3836' | |||
visual_bell: | |||
animation: EaseOutExpo | |||
duration: 0 | |||
background_opacity: 1.0 | |||
mouse_bindings: | |||
- { mouse: Middle, action: PasteSelection } | |||
mouse: | |||
double_click: { threshold: 300 } | |||
triple_click: { threshold: 300 } | |||
faux_scrolling_lines: 1 | |||
hide_when_typing: true | |||
url: | |||
launcher: | |||
program: xdg-open | |||
args: [] | |||
selection: | |||
semantic_escape_chars: ",โ`|:\"' ()[]{}<>" | |||
cursor: | |||
style: Underline | |||
unfocused_hollow: true | |||
live_config_reload: true | |||
key_bindings: | |||
- { key: V, mods: Control|Shift, action: Paste } | |||
- { key: C, mods: Control|Shift, action: Copy } | |||
- { key: Paste, action: Paste } | |||
- { key: Copy, action: Copy } | |||
- { key: Q, mods: Command, action: Quit } | |||
- { key: W, mods: Command, action: Quit } | |||
- { key: Insert, mods: Shift, action: PasteSelection } | |||
- { key: Key0, mods: Control, action: ResetFontSize } | |||
- { key: Equals, mods: Control, action: IncreaseFontSize } | |||
- { key: Subtract, mods: Control, action: DecreaseFontSize } | |||
- { key: Home, chars: "\x1bOH", mode: AppCursor } | |||
- { key: Home, chars: "\x1b[H", mode: ~AppCursor } | |||
- { key: End, chars: "\x1bOF", mode: AppCursor } | |||
- { key: End, chars: "\x1b[F", mode: ~AppCursor } | |||
- { key: PageUp, mods: Shift, chars: "\x1b[5;2~" } | |||
- { key: PageUp, mods: Control, chars: "\x1b[5;5~" } | |||
- { key: PageUp, chars: "\x1b[5~" } | |||
- { key: PageDown, mods: Shift, chars: "\x1b[6;2~" } | |||
- { key: PageDown, mods: Control, chars: "\x1b[6;5~" } | |||
- { key: PageDown, chars: "\x1b[6~" } | |||
- { key: Tab, mods: Shift, chars: "\x1b[Z" } | |||
- { key: Back, chars: "\x7f" } | |||
- { key: Back, mods: Alt, chars: "\x1b\x7f" } | |||
- { key: Insert, chars: "\x1b[2~" } | |||
- { key: Delete, chars: "\x1b[3~" } | |||
- { key: Left, mods: Shift, chars: "\x1b[1;2D" } | |||
- { key: Left, mods: Control, chars: "\x1b[1;5D" } | |||
- { key: Left, mods: Alt, chars: "\x1b[1;3D" } | |||
- { key: Left, chars: "\x1b[D", mode: ~AppCursor } | |||
- { key: Left, chars: "\x1bOD", mode: AppCursor } | |||
- { key: Right, mods: Shift, chars: "\x1b[1;2C" } | |||
- { key: Right, mods: Control, chars: "\x1b[1;5C" } | |||
- { key: Right, mods: Alt, chars: "\x1b[1;3C" } | |||
- { key: Right, chars: "\x1b[C", mode: ~AppCursor } | |||
- { key: Right, chars: "\x1bOC", mode: AppCursor } | |||
- { key: Up, mods: Shift, chars: "\x1b[1;2A" } | |||
- { key: Up, mods: Control, chars: "\x1b[1;5A" } | |||
- { key: Up, mods: Alt, chars: "\x1b[1;3A" } | |||
- { key: Up, chars: "\x1b[A", mode: ~AppCursor } | |||
- { key: Up, chars: "\x1bOA", mode: AppCursor } | |||
- { key: Down, mods: Shift, chars: "\x1b[1;2B" } | |||
- { key: Down, mods: Control, chars: "\x1b[1;5B" } | |||
- { key: Down, mods: Alt, chars: "\x1b[1;3B" } | |||
- { key: Down, chars: "\x1b[B", mode: ~AppCursor } | |||
- { key: Down, chars: "\x1bOB", mode: AppCursor } | |||
- { key: F1, chars: "\x1bOP" } | |||
- { key: F2, chars: "\x1bOQ" } | |||
- { key: F3, chars: "\x1bOR" } | |||
- { key: F4, chars: "\x1bOS" } | |||
- { key: F5, chars: "\x1b[15~" } | |||
- { key: F6, chars: "\x1b[17~" } | |||
- { key: F7, chars: "\x1b[18~" } | |||
- { key: F8, chars: "\x1b[19~" } | |||
- { key: F9, chars: "\x1b[20~" } | |||
- { key: F10, chars: "\x1b[21~" } | |||
- { key: F11, chars: "\x1b[23~" } | |||
- { key: F12, chars: "\x1b[24~" } | |||
- { key: F1, mods: Shift, chars: "\x1b[1;2P" } | |||
- { key: F2, mods: Shift, chars: "\x1b[1;2Q" } | |||
- { key: F3, mods: Shift, chars: "\x1b[1;2R" } | |||
- { key: F4, mods: Shift, chars: "\x1b[1;2S" } | |||
- { key: F5, mods: Shift, chars: "\x1b[15;2~" } | |||
- { key: F6, mods: Shift, chars: "\x1b[17;2~" } | |||
- { key: F7, mods: Shift, chars: "\x1b[18;2~" } | |||
- { key: F8, mods: Shift, chars: "\x1b[19;2~" } | |||
- { key: F9, mods: Shift, chars: "\x1b[20;2~" } | |||
- { key: F10, mods: Shift, chars: "\x1b[21;2~" } | |||
- { key: F11, mods: Shift, chars: "\x1b[23;2~" } | |||
- { key: F12, mods: Shift, chars: "\x1b[24;2~" } | |||
- { key: F1, mods: Control, chars: "\x1b[1;5P" } | |||
- { key: F2, mods: Control, chars: "\x1b[1;5Q" } | |||
- { key: F3, mods: Control, chars: "\x1b[1;5R" } | |||
- { key: F4, mods: Control, chars: "\x1b[1;5S" } | |||
- { key: F5, mods: Control, chars: "\x1b[15;5~" } | |||
- { key: F6, mods: Control, chars: "\x1b[17;5~" } | |||
- { key: F7, mods: Control, chars: "\x1b[18;5~" } | |||
- { key: F8, mods: Control, chars: "\x1b[19;5~" } | |||
- { key: F9, mods: Control, chars: "\x1b[20;5~" } | |||
- { key: F10, mods: Control, chars: "\x1b[21;5~" } | |||
- { key: F11, mods: Control, chars: "\x1b[23;5~" } | |||
- { key: F12, mods: Control, chars: "\x1b[24;5~" } | |||
- { key: F1, mods: Alt, chars: "\x1b[1;6P" } | |||
- { key: F2, mods: Alt, chars: "\x1b[1;6Q" } | |||
- { key: F3, mods: Alt, chars: "\x1b[1;6R" } | |||
- { key: F4, mods: Alt, chars: "\x1b[1;6S" } | |||
- { key: F5, mods: Alt, chars: "\x1b[15;6~" } | |||
- { key: F6, mods: Alt, chars: "\x1b[17;6~" } | |||
- { key: F7, mods: Alt, chars: "\x1b[18;6~" } | |||
- { key: F8, mods: Alt, chars: "\x1b[19;6~" } | |||
- { key: F9, mods: Alt, chars: "\x1b[20;6~" } | |||
- { key: F10, mods: Alt, chars: "\x1b[21;6~" } | |||
- { key: F11, mods: Alt, chars: "\x1b[23;6~" } | |||
- { key: F12, mods: Alt, chars: "\x1b[24;6~" } | |||
- { key: F1, mods: Super, chars: "\x1b[1;3P" } | |||
- { key: F2, mods: Super, chars: "\x1b[1;3Q" } | |||
- { key: F3, mods: Super, chars: "\x1b[1;3R" } | |||
- { key: F4, mods: Super, chars: "\x1b[1;3S" } | |||
- { key: F5, mods: Super, chars: "\x1b[15;3~" } | |||
- { key: F6, mods: Super, chars: "\x1b[17;3~" } | |||
- { key: F7, mods: Super, chars: "\x1b[18;3~" } | |||
- { key: F8, mods: Super, chars: "\x1b[19;3~" } | |||
- { key: F9, mods: Super, chars: "\x1b[20;3~" } | |||
- { key: F10, mods: Super, chars: "\x1b[21;3~" } | |||
- { key: F11, mods: Super, chars: "\x1b[23;3~" } | |||
- { key: F12, mods: Super, chars: "\x1b[24;3~" } |
@ -0,0 +1,2 @@ | |||
--theme="ansi-dark" | |||
--style="numbers,changes,header" |
@ -0,0 +1,23 @@ | |||
#!/bin/sh | |||
send_notification() { | |||
backlight=$(brightnessctl -m | cut -d, -f4 | sed 's/%//' | awk '{print int($1 / 2.9)}') | |||
# Make the bar with the special character โ (it's not dash -) | |||
# https://en.wikipedia.org/wiki/Box-drawing_character | |||
bar=$(seq -s "โ" "$backlight" | sed 's/[0-9]//g') | |||
# Send the notification | |||
makoctl dismiss | |||
notify-send -t 1000 "mon: $bar" | |||
} | |||
case $1 in | |||
up) | |||
brightnessctl -q set +10% | |||
send_notification | |||
;; | |||
down) | |||
brightnessctl -q set 10%- | |||
send_notification | |||
;; | |||
*) echo "usage: backlight [up|down]" ;; | |||
esac |
@ -0,0 +1,23 @@ | |||
#!/bin/sh | |||
## TODO: figure out whatever the fuck is happening here | |||
#MONITORS="$(swaymsg -t get_outputs | jq -r 'length')" | |||
#FOCUSED="$(swaymsg -t get_workspaces | jq -r '.. | select(.focused?) | .output')" | |||
#case $FOCUSED in | |||
# eDP1) | |||
#esac | |||
BG="$(xrq background)" | |||
FG="$(xrq foreground)" | |||
BLK="$(xrq color0)" | |||
HL="$(xrq color1)" | |||
FONT="FiraCode 9" | |||
PROMPT=">" | |||
LINES=10 | |||
LH=20 | |||
bemenu -m -1 -n -p "$PROMPT" -l "$LINES" --fn "$FONT" -H "$LH" --scrollbar always \ | |||
--tb "$HL" --tf "$BLK" --nb "$BG" --nf "$FG" \ | |||
--hb "$BG" --hf "$HL" --fb "$BLK" --ff "$FG" \ | |||
--scb "$BLK" --scf "$HL" |
@ -0,0 +1,24 @@ | |||
#!/usr/bin/env bash | |||
# Show the terminal colourscheme as blocks | |||
# Inspired by u/dkeg | |||
# Cheers! Addy | |||
f=3 b=4 | |||
for j in f b; do | |||
for i in {0..7}; do | |||
printf -v $j$i %b "\e[${!j}${i}m" | |||
done | |||
done | |||
d=$'\e[1m' | |||
t=$'\e[0m' | |||
v=$'\e[7m' | |||
cat << EOF | |||
$f1โโโโโโ$dโโ$t $f2โโโโโโ$dโโ$t $f3โโโโโโ$dโโ$t $f4โโโโโโ$dโโ$t $f5โโโโโโ$dโโ$t $f6โโโโโโ$dโโ$t | |||
$f1โโโโโโ$dโโ$t $f2โโโโโโ$dโโ$t $f3โโโโโโ$dโโ$t $f4โโโโโโ$dโโ$t $f5โโโโโโ$dโโ$t $f6โโโโโโ$dโโ$t | |||
$f1โโโโโโ$dโโ$t $f2โโโโโโ$dโโ$t $f3โโโโโโ$dโโ$t $f4โโโโโโ$dโโ$t $f5โโโโโโ$dโโ$t $f6โโโโโโ$dโโ$t | |||
$ftโโโโโโ$d$f7โโ$t $ftโโโโโโ$d$f7โโ$t $ftโโโโโโ$d$f7โโ$t $ftโโโโโโ$d$f7โโ$t $ftโโโโโโ$d$f7โโ$t $ftโโโโโโ$d$f7โโ$t | |||
EOF |
@ -0,0 +1,3 @@ | |||
#!/bin/sh | |||
emacsclient -c --socket-name hazel-emacsd $@ |
@ -0,0 +1,14 @@ | |||
#!/bin/sh | |||
if [ -z "$@" ]; then | |||
echo "usage: import-gsettings <gsettings key>:<settings.ini key> <gsettings key>:<settings.ini key> ..." | |||
return 1 | |||
fi | |||
expression="" | |||
for pair in "$@"; do | |||
IFS=:; set -- $pair | |||
expressions="$expressions -e 's:^$2=(.*)$:gsettings set org.gnome.desktop.interface $1 \1:e'" | |||
done | |||
IFS= | |||
eval exec sed -E $expressions "${XDG_CONFIG_HOME:-$HOME/.config}"/gtk-3.0/settings.ini >/dev/null |
@ -0,0 +1,64 @@ | |||
#!/usr/bin/env bash | |||
# | |||
# Control keyboard layout and backlight (Lenovo ThinkPad tested only) | |||
# | |||
# Author: Dave Eddy <dave@daveeddy.com> | |||
# Date: June 25, 2018 | |||
# License: MIT | |||
TMPFILE=/tmp/keyboard-status | |||
# taken from https://wiki.archlinux.org/index.php/Keyboard_backlight | |||
setKeyboardLight() { | |||
echo "$1" > "$TMPFILE" | |||
dbus-send --system --type=method_call \ | |||
--dest='org.freedesktop.UPower' \ | |||
'/org/freedesktop/UPower/KbdBacklight' \ | |||
'org.freedesktop.UPower.KbdBacklight.SetBrightness' \ | |||
"int32:$1" | |||
} | |||
# cycle the current state of the keyboard backlight | |||
cycleKeyboardLight() { | |||
local cur | |||
cur="$(cat $TMPFILE)" | |||
case "$cur" in | |||
0) setKeyboardLight 1;; | |||
1) setKeyboardLight 2;; | |||
2) setKeyboardLight 0;; | |||
*) exit 1;; | |||
esac | |||
} | |||
usage() { | |||
local prog=${0##*/} | |||
cat <<-EOF | |||
Usage: $prog <command> | |||
Examples | |||
# control backlight | |||
$prog [off | dim | bright/on] | |||
# print help | |||
$prog help | |||
EOF | |||
} | |||
if [[ ! -f "$TMPFILE" ]]; then | |||
echo '0' > "$TMPFILE" | |||
fi | |||
case "$1" in | |||
# lights | |||
off) setKeyboardLight 0;; | |||
dim) setKeyboardLight 1;; | |||
on|bright) setKeyboardLight 2;; | |||
cycle) cycleKeyboardLight;; | |||
help|-h) usage; exit 0;; | |||
*) usage >&2; exit 1;; | |||
esac |
@ -0,0 +1,68 @@ | |||
#!/usr/bin/env bash | |||
GDBUS_MONITOR_PID=/tmp/notify-action-dbus-monitor.$$.pid | |||
GDBUS_MONITOR=(gdbus monitor --session --dest org.freedesktop.Notifications --object-path /org/freedesktop/Notifications) | |||
NOTIFICATION_ID="$1" | |||
if [[ -z "$NOTIFICATION_ID" ]]; then | |||
echo "no notification id passed: $@" | |||
exit 1; | |||
fi | |||
shift | |||
ACTION_COMMANDS=("$@") | |||
if [[ -z "$ACTION_COMMANDS" ]]; then | |||
echo "no action commands passed: $@" | |||
exit 1; | |||
fi | |||
cleanup() { | |||
rm -f "$GDBUS_MONITOR_PID" | |||
} | |||
create_pid_file(){ | |||
rm -f "$GDBUS_MONITOR_PID" | |||
umask 077 | |||
touch "$GDBUS_MONITOR_PID" | |||
} | |||
invoke_action() { | |||
invoked_action_id="$1" | |||
local action="" cmd="" | |||
for index in "${!ACTION_COMMANDS[@]}"; do | |||
if [[ $((index % 2)) == 0 ]]; then | |||
action="${ACTION_COMMANDS[$index]}" | |||
else | |||
cmd="${ACTION_COMMANDS[$index]}" | |||
if [[ "$action" == "$invoked_action_id" ]]; then | |||
bash -c "${cmd}" & | |||
fi | |||
fi | |||
done | |||
} | |||
monitor() { | |||
create_pid_file | |||
( "${GDBUS_MONITOR[@]}" & echo $! >&3 ) 3>"$GDBUS_MONITOR_PID" | while read -r line | |||
do | |||
local closed_notification_id="$(sed '/^\/org\/freedesktop\/Notifications: org.freedesktop.Notifications.NotificationClosed (uint32 \([0-9]\+\), uint32 [0-9]\+)$/!d;s//\1/' <<< "$line")" | |||
if [[ -n "$closed_notification_id" ]]; then | |||
if [[ "$closed_notification_id" == "$NOTIFICATION_ID" ]]; then | |||
invoke_action close | |||
break | |||
fi | |||
else | |||
local action_invoked="$(sed '/\/org\/freedesktop\/Notifications: org.freedesktop.Notifications.ActionInvoked (uint32 \([0-9]\+\), '\''\(.*\)'\'')$/!d;s//\1:\2/' <<< "$line")" | |||
IFS=: read invoked_id action_id <<< "$action_invoked" | |||
if [[ "$invoked_id" == "$NOTIFICATION_ID" ]]; then | |||
invoke_action "$action_id" | |||
break | |||
fi | |||
fi | |||
done | |||
kill $(<"$GDBUS_MONITOR_PID") | |||
cleanup | |||
} | |||
monitor |
@ -0,0 +1,312 @@ | |||
#!/usr/bin/env bash | |||
# notify-send.sh - drop-in replacement for notify-send with more features | |||
# Copyright (C) 2015-2019 notify-send.sh authors (see AUTHORS file) | |||
# This program is free software: you can redistribute it and/or modify | |||
# it under the terms of the GNU General Public License as published by | |||
# the Free Software Foundation, either version 3 of the License, or | |||
# (at your option) any later version. | |||
# This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | |||
# You should have received a copy of the GNU General Public License | |||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
# Desktop Notifications Specification | |||
# https://developer.gnome.org/notification-spec/ | |||
VERSION=1.0 | |||
NOTIFY_ARGS=(--session | |||
--dest org.freedesktop.Notifications | |||
--object-path /org/freedesktop/Notifications) | |||
EXPIRE_TIME=-1 | |||
APP_NAME="${0##*/}" | |||
REPLACE_ID=0 | |||
URGENCY=1 | |||
HINTS=() | |||
SUMMARY_SET=n | |||
help() { | |||
cat <<EOF | |||
Usage: | |||
notify-send.sh [OPTION...] <SUMMARY> [BODY] - create a notification | |||
Help Options: | |||
-?|--help Show help options | |||
Application Options: | |||
-u, --urgency=LEVEL Specifies the urgency level (low, normal, critical). | |||
-t, --expire-time=TIME Specifies the timeout in milliseconds at which to expire the notification. | |||
-f, --force-expire Forcefully closes the notification when the notification has expired. | |||
-a, --app-name=APP_NAME Specifies the app name for the icon. | |||
-i, --icon=ICON[,ICON...] Specifies an icon filename or stock icon to display. | |||
-c, --category=TYPE[,TYPE...] Specifies the notification category. | |||
-h, --hint=TYPE:NAME:VALUE Specifies basic extra data to pass. Valid types are int, double, string and byte. | |||
-o, --action=LABEL:COMMAND Specifies an action. Can be passed multiple times. LABEL is usually a button's label. COMMAND is a shell command executed when action is invoked. | |||
-d, --default-action=COMMAND Specifies the default action which is usually invoked by clicking the notification. | |||
-l, --close-action=COMMAND Specifies the action invoked when notification is closed. | |||
-p, --print-id Print the notification ID to the standard output. | |||
-r, --replace=ID Replace existing notification. | |||
-R, --replace-file=FILE Store and load notification replace ID to/from this file. | |||
-s, --close=ID Close notification. | |||
-v, --version Version of the package. | |||
EOF | |||
} | |||
convert_type() { | |||
case "$1" in | |||
int) echo int32 ;; | |||
double|string|byte) echo "$1" ;; | |||
*) echo error; return 1 ;; | |||
esac | |||
} | |||
make_action_key() { | |||
echo "$(tr -dc _A-Z-a-z-0-9 <<< \"$1\")${RANDOM}" | |||
} | |||
make_action() { | |||
local action_key="$1" | |||
printf -v text "%q" "$2" | |||
echo "\"$action_key\", \"$text\"" | |||
} | |||
make_hint() { | |||
type=$(convert_type "$1") | |||
[[ ! $? = 0 ]] && return 1 | |||
name="$2" | |||
[[ "$type" = string ]] && command="\"$3\"" || command="$3" | |||
echo "\"$name\": <$type $command>" | |||
} | |||
concat_actions() { | |||
local result="$1" | |||
shift | |||
for s in "$@"; do | |||
result="$result, $s" | |||
done | |||
echo "[$result]" | |||
} | |||
concat_hints() { | |||
local result="$1" | |||
shift | |||
for s in "$@"; do | |||
result="$result, $s" | |||
done | |||
echo "{$result}" | |||
} | |||
parse_notification_id(){ | |||
sed 's/(uint32 \([0-9]\+\),)/\1/g' | |||
} | |||
notify() { | |||
local actions="$(concat_actions "${ACTIONS[@]}")" | |||
local hints="$(concat_hints "${HINTS[@]}")" | |||
NOTIFICATION_ID=$(gdbus call "${NOTIFY_ARGS[@]}" \ | |||
--method org.freedesktop.Notifications.Notify \ | |||
"$APP_NAME" "$REPLACE_ID" "$ICON" "$SUMMARY" "$BODY" \ | |||
"${actions}" "${hints}" "int32 $EXPIRE_TIME" \ | |||
| parse_notification_id) | |||
if [[ -n "$STORE_ID" ]] ; then | |||
echo "$NOTIFICATION_ID" > $STORE_ID | |||
fi | |||
if [[ -n "$PRINT_ID" ]] ; then | |||
echo "$NOTIFICATION_ID" | |||
fi | |||
if [[ -n "$FORCE_EXPIRE" ]] ; then | |||
type bc &> /dev/null || { echo "bc command not found. Please install bc package."; exit 1; } | |||
SLEEP_TIME="$(bc <<< "scale=3; $EXPIRE_TIME / 1000")" | |||
( sleep "$SLEEP_TIME" ; notify_close "$NOTIFICATION_ID" ) & | |||
fi | |||
maybe_run_action_handler | |||
} | |||
notify_close () { | |||
gdbus call "${NOTIFY_ARGS[@]}" --method org.freedesktop.Notifications.CloseNotification "$1" >/dev/null | |||
} | |||
process_urgency() { | |||
case "$1" in | |||
low) URGENCY=0 ;; | |||
normal) URGENCY=1 ;; | |||
critical) URGENCY=2 ;; | |||
*) echo "Unknown urgency $URGENCY specified. Known urgency levels: low, normal, critical." | |||
exit 1 | |||
;; | |||
esac | |||
} | |||
process_category() { | |||
IFS=, read -a categories <<< "$1" | |||
for category in "${categories[@]}"; do | |||
hint="$(make_hint string category "$category")" | |||
HINTS=("${HINTS[@]}" "$hint") | |||
done | |||
} | |||
process_hint() { | |||
IFS=: read type name command <<< "$1" | |||
if [[ -z "$name" ]] || [[ -z "$command" ]] ; then | |||
echo "Invalid hint syntax specified. Use TYPE:NAME:VALUE." | |||
exit 1 | |||
fi | |||
hint="$(make_hint "$type" "$name" "$command")" | |||
if [[ ! $? = 0 ]] ; then | |||
echo "Invalid hint type \"$type\". Valid types are int, double, string and byte." | |||
exit 1 | |||
fi | |||
HINTS=("${HINTS[@]}" "$hint") | |||
} | |||
maybe_run_action_handler() { | |||
if [[ -n "$NOTIFICATION_ID" ]] && [[ -n "$ACTION_COMMANDS" ]]; then | |||
local notify_action="$(dirname ${BASH_SOURCE[0]})/notify-action.sh" | |||
if [[ -x "$notify_action" ]] ; then | |||
"$notify_action" "$NOTIFICATION_ID" "${ACTION_COMMANDS[@]}" & | |||
exit 0 | |||
else | |||
echo "executable file not found: $notify_action" | |||
exit 1 | |||
fi | |||
fi | |||
} | |||
process_action() { | |||
IFS=: read name command <<<"$1" | |||
if [[ -z "$name" ]] || [[ -z "$command" ]]; then | |||
echo "Invalid action syntax specified. Use NAME:COMMAND." | |||
exit 1 | |||
fi | |||
local action_key="$(make_action_key "$name")" | |||
ACTION_COMMANDS=("${ACTION_COMMANDS[@]}" "$action_key" "$command") | |||
local action="$(make_action "$action_key" "$name")" | |||
ACTIONS=("${ACTIONS[@]}" "$action") | |||
} | |||
process_special_action() { | |||
action_key="$1" | |||
command="$2" | |||
if [[ -z "$action_key" ]] || [[ -z "$command" ]]; then | |||
echo "Command must not be empty" | |||
exit 1 | |||
fi | |||
ACTION_COMMANDS=("${ACTION_COMMANDS[@]}" "$action_key" "$command") | |||
if [[ "$action_key" != close ]]; then | |||
local action="$(make_action "$action_key" "$name")" | |||
ACTIONS=("${ACTIONS[@]}" "$action") | |||
fi | |||
} | |||
process_posargs() { | |||
if [[ "$1" = -* ]] && ! [[ "$positional" = yes ]] ; then | |||
echo "Unknown option $1" | |||
exit 1 | |||
else | |||
if [[ "$SUMMARY_SET" = n ]]; then | |||
SUMMARY="$1" | |||
SUMMARY_SET=y | |||
else | |||
BODY="$1" | |||
fi | |||
fi | |||
} | |||
while (( $# > 0 )) ; do | |||
case "$1" in | |||
-\?|--help) | |||
help | |||
exit 0 | |||
;; | |||
-v|--version) | |||
echo "${0##*/} $VERSION" | |||
exit 0 | |||
;; | |||
-u|--urgency|--urgency=*) | |||
[[ "$1" = --urgency=* ]] && urgency="${1#*=}" || { shift; urgency="$1"; } | |||
process_urgency "$urgency" | |||
;; | |||
-t|--expire-time|--expire-time=*) | |||
[[ "$1" = --expire-time=* ]] && EXPIRE_TIME="${1#*=}" || { shift; EXPIRE_TIME="$1"; } | |||
;; | |||
-f|--force-expire) | |||
FORCE_EXPIRE=yes | |||
;; | |||
-a|--app-name|--app-name=*) | |||
[[ "$1" = --app-name=* ]] && APP_NAME="${1#*=}" || { shift; APP_NAME="$1"; } | |||
;; | |||
-i|--icon|--icon=*) | |||
[[ "$1" = --icon=* ]] && ICON="${1#*=}" || { shift; ICON="$1"; } | |||
;; | |||
-c|--category|--category=*) | |||
[[ "$1" = --category=* ]] && category="${1#*=}" || { shift; category="$1"; } | |||
process_category "$category" | |||
;; | |||
-h|--hint|--hint=*) | |||
[[ "$1" = --hint=* ]] && hint="${1#*=}" || { shift; hint="$1"; } | |||
process_hint "$hint" | |||
;; | |||
-o | --action | --action=*) | |||
[[ "$1" == --action=* ]] && action="${1#*=}" || { shift; action="$1"; } | |||
process_action "$action" | |||
;; | |||
-d | --default-action | --default-action=*) | |||
[[ "$1" == --default-action=* ]] && default_action="${1#*=}" || { shift; default_action="$1"; } | |||
process_special_action default "$default_action" | |||
;; | |||
-l | --close-action | --close-action=*) | |||
[[ "$1" == --close-action=* ]] && close_action="${1#*=}" || { shift; close_action="$1"; } | |||
process_special_action close "$close_action" | |||
;; | |||
-p|--print-id) | |||
PRINT_ID=yes | |||
;; | |||
-r|--replace|--replace=*) | |||
[[ "$1" = --replace=* ]] && REPLACE_ID="${1#*=}" || { shift; REPLACE_ID="$1"; } | |||
;; | |||
-R|--replace-file|--replace-file=*) | |||
[[ "$1" = --replace-file=* ]] && filename="${1#*=}" || { shift; filename="$1"; } | |||
if [[ -s "$filename" ]]; then | |||
REPLACE_ID="$(< $filename)" | |||
fi | |||
STORE_ID="$filename" | |||
;; | |||
-s|--close|--close=*) | |||
[[ "$1" = --close=* ]] && close_id="${1#*=}" || { shift; close_id="$1"; } | |||
notify_close "$close_id" | |||
exit $? | |||
;; | |||
--) | |||
positional=yes | |||
;; | |||
*) | |||
process_posargs "$1" | |||
;; | |||
esac | |||
shift | |||
done | |||
# urgency is always set | |||
HINTS=("$(make_hint byte urgency "$URGENCY")" "${HINTS[@]}") | |||
if [[ "$SUMMARY_SET" = n ]] ; then | |||
help | |||
exit 1 | |||
else | |||
notify | |||
fi |
@ -0,0 +1,48 @@ | |||
#!/bin/sh | |||
SCROT_FILE="/home/hazel/usr/img/scrots/$(date +"scr_%Y-%m-%d_%H-%M-%S").png" | |||
send_notification() { | |||
if [ -n "$1" ]; then | |||
wl-copy "$1" | |||
notify-send -i "$SCROT_FILE" "Screenshot saved!" "URL: $1" -t 2500 | |||
else | |||
# only copy the image if we aren't already copying the URL | |||
wl-copy -t image/png < "$SCROT_FILE" | |||
notify-send -i "$SCROT_FILE" "Screenshot saved!" -t 2500 | |||
fi | |||
} | |||
grab_selection() { | |||
SCROT_AREA="$(swaymsg -t get_tree | jq -r '.. | (.nodes? // empty)[] | select(.pid and .visible) | .rect | "\(.x),\(.y) \(.width)x\(.height)"' | slurp)" | |||
grim -g "$SCROT_AREA" "$SCROT_FILE" | |||
} | |||
grab_full() { | |||
grim "$SCROT_FILE" | |||
} | |||
upload_scrot() { | |||
API_KEY="$(jq -r '.apikey' < "$HOME"/.config/linx-client.conf)" # reinventing the wheel | |||
curl -s -H "Linx-Api-Key: $API_KEY" -H "Linx-Randomize: yes" -H "Accept: application/json" \ | |||
-T "$SCROT_FILE" https://p.qtp2t.club/upload/ | jq -r '.url' | |||
} | |||
case "$1" in | |||
selup) | |||
grab_selection && send_notification "$(upload_scrot)" | |||
;; | |||
sel) | |||
grab_selection && send_notification | |||
;; | |||
fullup) | |||
grab_full && send_notification "$(upload_scrot)" | |||
;; | |||
full) | |||
grab_full && send_notification | |||
;; | |||
*) | |||
echo "usage: scrot [full|sel|fullup|selup]" | |||
return 1 | |||
;; | |||
esac |
@ -0,0 +1,37 @@ | |||
#!/bin/sh | |||
send_notification() { | |||
volume=$(ponymix --sink get-volume | awk '{print int($1 / 2.9)}') | |||
# Make the bar with the special character โ (it's not dash -) | |||
# https://en.wikipedia.org/wiki/Box-drawing_character | |||
bar=$(seq -s "โ" "$volume" | sed 's/[0-9]//g') | |||
# Send the notification | |||
makoctl dismiss | |||
notify-send -t 1000 "vol: $bar" | |||
} | |||
case $1 in | |||
up) | |||
# Set the volume on (if it was muted) | |||
ponymix --sink unmute | |||
# Up the volume (+ 5%) | |||
ponymix --sink increase 5 | |||
send_notification | |||
;; | |||
down) | |||
ponymix --sink unmute | |||
ponymix --sink decrease 5 | |||
send_notification | |||
;; | |||
mute) | |||
# Toggle mute | |||
ponymix --sink toggle | |||
if ponymix --sink is-muted; then | |||
makoctl dismiss | |||
notify-send -t 1000 "volume muted" | |||
else | |||
send_notification | |||
fi | |||
;; | |||
*) echo "usage: volume [up|down|mute]" ;; | |||
esac |
@ -0,0 +1,86 @@ | |||
#!/bin/sh | |||
# https://github.com/BanchouBoo/dots/blob/master/bin/xbps-menu | |||
# ^ not licensed but... | |||
# sets XDG_CACHE_HOME to it's default if not set | |||
: "${XDG_CACHE_HOME:=$HOME/.cache}" | |||
cache_file="$XDG_CACHE_HOME/xbps-menu/pkglist" | |||
options="install | |||
update | |||
sync | |||
remove | |||
autoremove | |||
clean" | |||
install_preview='pkg={}; pkg="${pkg%% *}"; xbps-query "$pkg" --property state > /dev/null && | |||
# stat="Status: \033[2K\033[00;32mInstalled\033[0m" || | |||
# stat="Status: \033[2K\033[0;31mNot installed\033[0m" | |||
stat="Status: Installed" || | |||
stat="Status: Not installed" | |||
depends=$(xbps-query -R "$pkg" --property run_depends | sed "2,99 s/^/\t\t/") | |||
printf "$stat \tDependencies:\t%s\n" "$depends" | column --separator " " --table' | |||
remove_preview='pkg={}; pkg="${pkg%% *}"; revdeps=$(xbps-query --revdeps "$pkg" | sed "2,99 s/^/\t\t/") | |||
printf "Dependents:\t%s\n" "$revdeps"' | |||
log() { | |||
printf "\n[\033[00;34m..\033[0m] %s\n" "$@" | |||
} | |||
install() { | |||
if [ ! -f "$cache_file" ]; then | |||
log "Cache file doesn't exist, syncing repos and generating cache" | |||
sync | |||
fi | |||
packages=$( | |||
sk --multi --preview "$install_preview" \ | |||
--preview-window "up" < "$cache_file" | \ | |||
while read -r package; do | |||
echo "${package%% *}" | |||
done) | |||
[ "$packages" ] && doas xbps-install -f $packages | |||
} | |||
update() { | |||
doas xbps-install -u | |||
} | |||
# syncs the repos and updates the cache | |||
sync() { | |||
log "Syncing remote repositories" | |||
doas xbps-install -S | |||
mkdir -p "$XDG_CACHE_HOME/xbps-menu" | |||
xbps-query -Rs "*" | while read -r line; do | |||
echo "${line#????}" | |||
done > "$cache_file" | |||
} | |||
remove() { | |||
packages=$( | |||
xbps-query -s "*" | while read -r line; do | |||
echo "${line#????}" | |||
done | \ | |||
sk --multi --preview "$remove_preview" \ | |||
--preview-window "up" | \ | |||
while read -r package; do | |||
echo "${package%% *}" | |||
done) | |||
[ "$packages" ] && doas xbps-remove -R $packages | |||
} | |||
autoremove() { | |||
log "Removing orphaned packages" | |||
doas xbps-remove -o | |||
} | |||
clean() { | |||
log "Cleaning cached packages" | |||
doas xbps-remove -O | |||
} | |||
while :; do | |||
selection=$(echo "$options" | sk) | |||
[ "$selection" ] || exit | |||
"$selection" | |||
done |
@ -0,0 +1,191 @@ | |||
#+TITLE: The Blag | |||
#+AUTHOR: Hazel Levine | |||
#+EMAIL: rose.hazel@protonmail.ch | |||
#+STARTUP: nofold | |||
This is the source code powering my blog, available at [[https://blog.qtp2t.club][blog.qtp2t.club]]. It sure | |||
does exist. More info can be found on the blog itself [[https://blog.qtp2t.club/2020-01-13-blorgging.html][here]], but basically this | |||
uses pure Org mode with no external dependencies. | |||
* Global variables | |||
These are defined for later configuration and to avoid repetition, as well as to | |||
modularize things such that if I ever change my home directory structure it | |||
doesn't break everything. | |||
We define where we want to store the files: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq blog/site-project-path "/home/hazel/usr/doc/blog/") | |||
#+END_SRC | |||
Where we want the exported HTML to go: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq blog/site-publish-path (concat blog/site-project-path "build/")) | |||
#+END_SRC | |||
As well as extra content to put in the header and footer. Note that setting | |||
these as variables means =blog.el= has to be reloaded whenever you change the | |||
contents of these files, which has bitten me in the butt /multiple/ times. | |||
#+BEGIN_SRC emacs-lisp | |||
(setq blog/site-extra-head-file (concat blog/site-project-path "templates/headext.html")) | |||
(setq blog/site-header-file (concat blog/site-project-path "templates/header.html")) | |||
(setq blog/site-footer-file (concat blog/site-project-path "templates/footer.html")) | |||
#+END_SRC | |||
Finally, we host MathJax locally, to avoid using CDNs, as well as the fact that | |||
Org doesn't tango nice with MathJax 3.x for some reason. We use the Neo-Euler | |||
font because I think it looks cool. Note that when deploying this, you probably | |||
don't want to =scp= MathJax; it'll take forever. | |||
#+BEGIN_SRC emacs-lisp | |||
(setq blog/mathjax-options | |||
'((path "./static/mj/MathJax.js?config=TeX-AMS-MML_HTMLorMML") | |||
(scale "100") (align "left") (indent "2em") (tagside "right") | |||
(mathml nil) (font "Neo-Euler"))) | |||
#+END_SRC | |||
* Functions | |||
We define a means to add HTML files as part of a file, such that we can add our | |||
custom headers and footers: | |||
#+BEGIN_SRC emacs-lisp | |||
(defun blog/add-html-file (arg) | |||
(with-temp-buffer | |||
(insert-file-contents arg) | |||
(buffer-string))) | |||
#+END_SRC | |||
We then take our previous variables defining the header and footer of the site | |||
and convert them into raw strings for usage in Org's exporter: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq blog/site-extra-head-raw (blog/add-html-file blog/site-extra-head-file)) | |||
(setq blog/site-header-raw (blog/add-html-file blog/site-header-file)) | |||
(setq blog/site-footer-raw (blog/add-html-file blog/site-footer-file)) | |||
#+END_SRC | |||
Additionally, we define a way to format a site entry in our table of contents: | |||
#+BEGIN_SRC emacs-lisp | |||
(defun blog/site-format-entry (entry style project) | |||
(format "[[file:%s][%s]] --- %s" | |||
entry | |||
(org-publish-find-title entry project) | |||
(format-time-string "%Y-%m-%d" (org-publish-find-date entry project)))) | |||
#+END_SRC | |||
* Org-publish | |||
This is the meat of it; what tells Org that we're actually looking to make a | |||
website out of miscalleneous files scattered in my documents folder. It's one | |||
giant call to =setq=. | |||
We tell Org that the site is a concatenation of all of its components, that we | |||
want to export HTML, and to look recursively. We also tell Org to put stuff in | |||
that's defined later. | |||
#+BEGIN_SRC emacs-lisp :noweb yes | |||
(setq org-publish-project-alist | |||
`(("site" | |||
:components ("site-static", "site-images", "site-articles", "site-dl", "site-rss")) | |||
<<static>> | |||
<<rss>> | |||
<<html1>> | |||
<<html2>> | |||
<<html3>> | |||
:publishing-function org-html-publish-to-html | |||
:recursive t))) | |||
#+END_SRC | |||
** Static | |||
Static resources, such as MathJax and CSS, should be copied directly to their | |||
respective folders. This also applies for images and public files (=dl=). We | |||
send these files to =blog/site-project-path=. The built-in publishing function | |||
=org-publish-attachment= copies the file directly, and we also tell Org to | |||
search recursively in case there are any subfolders. | |||
#+NAME: static | |||
#+BEGIN_SRC emacs-lisp :tangle no | |||
("site-static" | |||
:base-directory ,(concat blog/site-project-path "static/") | |||
:base-extension ".*" | |||
:publishing-directory ,(concat blog/site-publish-path "static/") | |||
:publishing-function org-publish-attachment | |||
:recursive t) | |||
("site-images" | |||
:base-directory ,(concat blog/site-project-path "img") | |||
:base-extension ".*" | |||
:publishing-directory ,(concat blog/site-publish-path "img/") | |||
:publishing-function org-publish-attachment | |||
:recursive t) | |||
("site-dl" | |||
:base-directory ,(concat blog/site-project-path "dl") | |||
:base-extension ".*" | |||
:publishing-directory ,(concat blog/site-publish-path "dl/") | |||
:publishing-function org-publish-attachment | |||
:recursive t) | |||
#+END_SRC | |||
** RSS | |||
I still like RSS. Also, one person asked for it, so I'm happy to oblige. For | |||
some reason, Org likes to repeat the XML header three times (???) at the | |||
beginning of the resulting XML file, so I go back and manually delete it every | |||
time. | |||
We tell it to look over all the articles, tell it where the blog is, and tell it | |||
to look over =index.org= (which is our table of contents) and nothing else. We | |||
also use =org-rss-publish-to-rss=, which pretty much does what it says it does. | |||
#+NAME: rss | |||
#+BEGIN_SRC emacs-lisp :tangle no | |||
("site-rss" | |||
:base-directory ,(concat blog/site-project-path "articles/") | |||
:base-extension "org" | |||
:publishing-directory ,blog/site-publish-path | |||
:publishing-function org-rss-publish-to-rss | |||
:html-link-home "https://blog.qtp2t.club" | |||
:html-link-use-abs-url t | |||
:title "Ziodyne" | |||
:section-numbers nil | |||
:exclude ".*" | |||
:include ("index.org") | |||
:table-of-contents nil) | |||
#+END_SRC | |||
** Articles (HTML) | |||
We tell Org to look over our articles directory, only search through Org files, | |||
and put everything in the site's publish path: | |||
#+NAME: html1 | |||
#+BEGIN_SRC emacs-lisp :tangle no | |||
("site-articles" | |||
:base-directory ,(concat blog/site-project-path "articles/") | |||
:base-extension "org" | |||
:publishing-directory ,blog/site-publish-path | |||
#+END_SRC | |||
We tell it that we're using HTML5, to include the extra headers, preamble, and | |||
postamble, and that we're operating on the root of the subdomain, as well as | |||
define MathJax to be itself: | |||
#+NAME: html2 | |||
#+BEGIN_SRC emacs-lisp :tangle no | |||
:html-doctype "html5" | |||
:html-link-home "/" | |||
:html-head nil | |||
:html-head-extra ,blog/site-extra-head-raw | |||
:html-head-include-default-style nil | |||
:html-head-include-scripts nil | |||
:html-home/up-format "" | |||
:html-preamble ,blog/site-header-raw | |||
:html-postamble ,blog/site-footer-raw | |||
:html-mathjax-options ,blog/mathjax-options | |||
#+END_SRC | |||
We generate the sitemap, tell it to save the result to =index.org=, and to list | |||
anti-chronologically. We also use the previously defined function | |||
=blog/site-format-entry= to... format entries. | |||
#+NAME: html3 | |||
#+BEGIN_SRC emacs-lisp :tangle no | |||
:makeindex nil | |||
:auto-sitemap t | |||
:sitemap-filename "index.org" | |||
:sitemap-title "Ramblings" | |||
:sitemap-style list | |||
:sitemap-sort-files anti-chronologically | |||
:sitemap-format-entry blog/site-format-entry | |||
:with-toc nil | |||
:section-numbers nil | |||
#+END_SRC |
@ -0,0 +1,306 @@ | |||
#+TITLE: Hazel's DOOM Emacs configuration | |||
#+AUTHOR: Hazel Levine | |||
#+EMAIL: rose.hazel@protonmail.ch | |||
#+STARTUP: nofold | |||
Whee, literate programming, whoo. I get to type words here. These are words on a | |||
screen. Typing. With /haaaaaaands/. | |||
In this file, we tangle the following: | |||
#+BEGIN_SRC emacs-lisp | |||
;;; config.el -*- lexical-binding: t -*- | |||
#+END_SRC | |||
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el | |||
;;; packages.el -*- lexical-binding: t -*- | |||
#+END_SRC | |||
Any calls to =package!= generally get tangled to =packages.el=, whereas | |||
everything else goes to =config.el=. | |||
* Introduction | |||
** =whoami= | |||
#+BEGIN_SRC emacs-lisp | |||
(setq user-full-name "Hazel Levine" | |||
user-mail-address "me@qtp2t.club") | |||
#+END_SRC | |||
** Custom Elisp | |||
Load up where Void installs the =maxima-emacs= package, for later: | |||
#+BEGIN_SRC emacs-lisp | |||
(add-to-list 'load-path "/usr/share/emacs/site-lisp") | |||
#+END_SRC | |||
Additionally, load the source code powering | |||
[[https://blog.qtp2t.club][my blog.]] | |||
#+BEGIN_SRC emacs-lisp | |||
(org-babel-load-file (concat doom-private-dir "blog.org")) | |||
#+END_SRC | |||
* UI | |||
** Fonts | |||
I've had font rendering issues with Emacs for /years/, ever since I picked it | |||
up. Namely, Emacs' font always looked just different enough from that of | |||
whatever terminal emulator I'm using to be annoying. Thankfully, [[https://github.com/rocx/.emacs.d][rocx's emacs | |||
config]] held the solution by forcing Emacs to use XFT (which is what =urxvt=, my | |||
terminal emulator, also uses). | |||
I first define a function to create an XFT font string from whatever font, size, | |||
and options I want: | |||
#+BEGIN_SRC emacs-lisp | |||
(defun hzl/xft-font-string (name size &optional properties) | |||
;; https://keithp.com/~keithp/render/Xft.tutorial | |||
(concat (format "%s-%d" name size) | |||
(when properties | |||
(apply #'concat (mapcar (lambda (prop) | |||
(format ":%s=%s" (car prop) (cdr prop))) | |||
properties))))) | |||
#+END_SRC | |||
Across my system, I use [[https://github.com/NerdyPepper/curie][curie]] for anything "system-related" (notifications, | |||
bars, etc...), [[https://github.com/tonsky/FiraCode][Fira Code]] for monospace (read: programming) font with [[https://www.nerdfonts.com/][Nerd Fonts]] | |||
patches, and [[https://fonts.adobe.com/fonts/source-sans][Source Sans Pro]] everywhere else. Size 9 seems to be readable on | |||
both my 1920x1080 external monitor and 1600x900 ThinkPad. | |||
#+BEGIN_SRC emacs-lisp | |||
(defvar hzl/monospace-font | |||
(hzl/xft-font-string "FiraCode" 10 | |||
'((hintstyle . 3) | |||
(hinting . true) | |||
(lcdfilter . 3) | |||
(antialias . true)))) | |||
(defvar hzl/variable-pitch-font | |||
(hzl/xft-font-string "Source Sans Pro" 10 | |||
'((hintstyle . 3) | |||
(autohint . true) | |||
(lcdfilter . 3) | |||
(antialias . true)))) | |||
(setq doom-font hzl/monospace-font) | |||
(setq doom-variable-pitch-font hzl/variable-pitch-font) | |||
#+END_SRC | |||
** Colorscheme | |||
I use [[http://sourcerer.xero.nu/][xero's sourcerer]] because it's readable, easy on the eyes, and plays well | |||
with a lot of wallpapers (spoken like a true ricer). | |||
#+BEGIN_SRC emacs-lisp | |||
(setq doom-theme 'doom-gruvbox-light) | |||
#+END_SRC | |||
** Everything else | |||
Enable line numbers, because those are nice, and make 'em relative: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq display-line-numbers-type 'relative) | |||
#+END_SRC | |||
Set the startup screen image to something comfy: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq fancy-splash-image (concat doom-private-dir "nice.png")) | |||
#+END_SRC | |||
* Functionality | |||
For the most part, DOOM Emacs handles 99% of the things I want to do in a text | |||
editor and more, which is why this section is pretty brief. | |||
Pretty much all of the functions I write end up under the "namespace" | |||
=hzl/whatever=, on the merit that I want to avoid any clashes anywhere. | |||
** Void package templates | |||
Void packages are written in a single shell script, so bind that mode to any | |||
file with the name =template=. | |||
#+BEGIN_SRC emacs-lisp | |||
(add-to-list 'auto-mode-alist '("/template\\'" . sh-mode)) | |||
#+END_SRC | |||
** MPDel music player | |||
I usually use =ncmpcpp= for music, but sometimes when I'm working I use this. | |||
It's all MPD, so it's not like they conflict. | |||
We grab both MPDel and its Ivy interface: | |||
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el | |||
(package! mpdel) | |||
(package! ivy-mpdel) | |||
#+END_SRC | |||
We also disable evil in MPDel because its keybindings are contingent on doing | |||
so. | |||
#+BEGIN_SRC emacs-lisp | |||
(use-package! mpdel | |||
:config | |||
(mpdel-mode) | |||
(set-evil-initial-state! '(mpdel-playlist-mode | |||
mpdel-browser-mode | |||
mpdel-song-mode | |||
mpdel-tablist-mode) | |||
'emacs)) | |||
#+END_SRC | |||
* Productivity, papers, etc | |||
For the boring stuff that's not /quite/ programming. | |||
** TeXcount | |||
This is a binding to a Perl script installed via =tlmgr= that... counts words. | |||
Considering most of the papers I write have hard minimum/maximum limits, this | |||
comes in pretty useful pretty often. | |||
#+BEGIN_SRC emacs-lisp | |||
(defun hzl/texcount () | |||
;; Counts words in a TeX file. | |||
(interactive) | |||
(let* | |||
((this-file (buffer-file-name)) | |||
(enc-str (symbol-name buffer-file-coding-system)) | |||
(enc-opt | |||
(cond | |||
((string-match "utf-8" enc-str) "-utf8") | |||
((string-match "latin" enc-str) "-latin1") | |||
("-encoding=guess"))) | |||
(word-count | |||
(with-output-to-string | |||
(with-current-buffer standard-output | |||
(call-process "texcount" nil t nil "-0" enc-opt this-file))))) | |||
(message word-count))) | |||
#+END_SRC | |||
...and then, actually bind it to =C-c w= in LaTeX mode. | |||
#+BEGIN_SRC emacs-lisp | |||
(add-hook 'LaTeX-mode-hook (lambda () (define-key LaTeX-mode-map "\C-cw" 'hzl/texcount))) | |||
#+END_SRC | |||
** AucTeX =latexmk= | |||
I use =latexmk= to build my LaTeX work because I use external files for my | |||
bibliographies. | |||
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el | |||
(package! auctex-latexmk) | |||
#+END_SRC | |||
We tell it to run the auto-setup function and use PDFs: | |||
#+BEGIN_SRC emacs-lisp | |||
(use-package! auctex-latexmk | |||
:config | |||
(auctex-latexmk-setup) | |||
(setq auctex-latexmk-inherit-TeX-pdf-mode t)) | |||
#+END_SRC | |||
** Maxima | |||
I use this CAS in conjunction with Org to take notes in my physics class, as | |||
well as to take notes on personal projects. It also automatically renders to | |||
LaTeX, and has a really clean Emacs interface. This isn't installed via | |||
=use-package!=, it's the =maxima-emacs= package on Void, which is why it's a | |||
series of autoloads. | |||
#+BEGIN_SRC emacs-lisp | |||
(autoload 'maxima-mode "maxima" "Maxima mode" t) | |||
(autoload 'imaxima "imaxima" "Frontend for maxima with image support" t) | |||
(autoload 'maxima "maxima" "Maxima interaction" t) | |||
(autoload 'imath-mode "imath" "Imath mode for math formula input" t) | |||
#+END_SRC | |||
Bind =M-x imaxima= to use =maxima-mode=: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq imaxima-use-maxima-mode-flag t) | |||
#+END_SRC | |||
And bind the right file extensions: | |||
#+BEGIN_SRC emacs-lisp | |||
(add-to-list 'auto-mode-alist '("\\.ma[cx]" . maxima-mode)) | |||
#+END_SRC | |||
** =mu4e= | |||
Since ProtonMail offers an IMAP/SMTP bridge now on Linux, I can use this. | |||
Tell =mu4e= to use =mbsync=. =offlineimap= was the only piece of software on my | |||
machine binding me to Python 2. | |||
#+BEGIN_SRC emacs-lisp | |||
(setq +mu4e-backend 'mbsync) | |||
#+END_SRC | |||
Tell it where I save my mail: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq mu4e-maildir "/home/hazel/var/mail/protonmail" | |||
mu4e-attachment-dir "/home/hazel/var/mail/protonmail/attachments") | |||
#+END_SRC | |||
We tell it how I save my email, too: | |||
#+BEGIN_SRC emacs-lisp | |||
(set-email-account! "ProtonMail" | |||
'((mu4e-sent-folder . "/Sent") | |||
(mu4e-drafts-folder . "/Drafts") | |||
(mu4e-trash-folder . "/Trash") | |||
(mu4e-refile-folder . "/All Mail") | |||
(smtpmail-smtp-user . "me@qtp2t.club") | |||
(smtpmail-smtp-service . 1025) | |||
(user-mail-address . "me@qtp2t.club")) | |||
t) | |||
#+END_SRC | |||
This took me forever to figure out. In order to let Emacs send mail from the | |||
ProtonMail Bridge's SMTP server, you have to allow its self-signed certificate. | |||
To get this certificate, you can execute the command: | |||
#+BEGIN_SRC sh :tangle no | |||
openssl s_client -connect 127.0.0.1:1025 -starttls smtp 2>/dev/null </dev/null | \ | |||
sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > out.pem | |||
#+END_SRC | |||
Then copy the resulting file =out.pem= to wherever you want, and: | |||
#+BEGIN_SRC emacs-lisp | |||
(add-to-list 'gnutls-trustfiles "/home/hazel/var/certs/protonmail_bridge.pem") | |||
#+END_SRC | |||
** Org-mode | |||
Set the bullets to pretty stuff: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq org-bullets-bullet-list '("โฏ" "โฐ" "โฑ" "โฒ" "โณ" "โด" "โต" "โถ" "โท")) | |||
(setq org-ellipsis "โ") | |||
#+END_SRC | |||
Make sure that Org doesn't try to clutter my home directory, and put stuff where | |||
it's supposed to be: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq org-directory "~/usr/doc/org/") | |||
#+END_SRC | |||
Set DOOM's scratch buffer, available at any point with =SPC x=, to Org, which I | |||
find useful for taking quick notes: | |||
#+BEGIN_SRC emacs-lisp | |||
(setq doom-scratch-buffer-major-mode 'org-mode) | |||
#+END_SRC | |||
** PDF Tools | |||
Not sure if I'll stick to this over Zathura, but it's nice to have everything in | |||
Emacs. This is bundled with DOOM, so I just tell AucTeX to open stuff up in it. | |||
#+BEGIN_SRC emacs-lisp | |||
(setq TeX-view-program-selection '((output-pdf "PDF Tools"))) | |||
(add-hook 'TeX-after-compilation-finished-functions #'TeX-revert-document-buffer) | |||
#+END_SRC | |||
* Programming languages | |||
The cool stuff. Unless it's Java. | |||
** FRC Mode | |||
This is a =gradlew= wrapper I hacked together really fast while sitting in my | |||
physics class not paying attention. The officially sanctioned IDE for FIRST | |||
Robotics is Visual Studio Code, which I hate with a burning passion for numerous | |||
reasons. | |||
We grab it directly from my Git, since it's not in ELPA (and probably never will | |||
be): | |||
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el | |||
(package! frc-mode | |||
:recipe (:host nil :repo "https://git.qtp2t.club/hazel/frc-mode")) | |||
#+END_SRC | |||
And tell it to run with all Java files. I'd never willingly write Java outside | |||
of FRC, so it's fine. | |||
#+BEGIN_SRC emacs-lisp | |||
(use-package! frc-mode | |||
:hook (java-mode . frc-mode)) | |||
#+END_SRC | |||
** =rust-analyzer= | |||
Iunno, man. Sure. Why not. Racer and RLS keep giving me issues. | |||
#+BEGIN_SRC emacs-lisp | |||
(setq rustic-lsp-server 'rust-analyzer) | |||
#+END_SRC | |||
** vimrc mode | |||
Hey. HEY. CHILL. The irony is not lost on me. This is actually for editing my | |||
[[https://tridactyl.xyz][Tridactyl]] config. | |||
#+BEGIN_SRC emacs-lisp | |||
(define-generic-mode 'vimrc-generic-mode | |||
'() | |||
'() | |||
'(("^[\t ]*:?\\(!\\|ab\\|map\\|unmap\\)[^\r\n\"]*\"[^\r\n\"]*\\(\"[^\r\n\"]*\"[^\r\n\"]*\\)*$" | |||
(0 font-lock-warning-face)) | |||
("\\(^\\|[\t ]\\)\\(\".*\\)$") | |||
(2 font-lock-comment-face) | |||
("\"\\([^\n\r\"\\]\\|\\.\\)*\"" | |||
(0 font-lock-string-face))) | |||
'("/vimrc\\'" "\\.vim\\(rc\\)?\\'") | |||
'((lambda () | |||
(modify-syntax-entry ?\" "."))) | |||
"Generic mode for Vim configuration files.") | |||
#+END_SRC | |||
** Shen | |||
A really cool experimental, portable, and typed Lisp. I like it a lot, but I | |||
don't know much about it on the merit that it's locked behind a paywall of a | |||
book that I can't afford nor pirate. | |||
#+BEGIN_SRC emacs-lisp :tangle ~/.config/doom/packages.el | |||
(package! shen-mode) | |||
#+END_SRC |
@ -0,0 +1,93 @@ | |||
;;; init.el -*- lexical-binding: t; -*- | |||
;; ,;' `:. NOTE: THERE ARE TWO BFGS IN HELL | |||
;; :: _ ;; | |||
;; `:_|_;' | |||
;; ! | |||
(doom! :completion | |||
company | |||
(ivy +icons +prescient) | |||
:ui | |||
doom | |||
doom-dashboard | |||
doom-quit | |||
hl-todo | |||
modeline | |||
nav-flash | |||
ophints | |||
(popup +all +defaults) | |||
(pretty-code +fira) | |||
treemacs | |||
vc-gutter | |||
vi-tilde-fringe | |||
window-select | |||
workspaces | |||
zen | |||
:editor | |||
(evil +everywhere) | |||
file-templates | |||
fold | |||
multiple-cursors | |||
parinfer | |||
rotate-text | |||
snippets | |||
:emacs | |||
(dired +icons +ranger) | |||
electric | |||
ibuffer | |||
vc | |||
:term | |||
eshell | |||
vterm | |||
:checkers | |||
syntax | |||
spell ; ...is this annoying? yes! do i need it for editing latex papers? | |||
; also yes! | |||
:tools | |||
debugger | |||
direnv | |||
(eval +overlay) | |||
gist | |||
(lookup +docsets) | |||
lsp | |||
magit | |||
make | |||
rgb | |||
:lang | |||
cc | |||
common-lisp | |||
data | |||
emacs-lisp | |||
go | |||
(haskell +dante) | |||
(java +lsp) | |||
javascript | |||
latex | |||
markdown | |||
nix | |||
(org +dragndrop +pandoc +present) | |||
python | |||
racket | |||
(rust +lsp) | |||
(scala +lsp) | |||
scheme | |||
sh | |||
(mu4e +gmail) | |||
:app | |||
irc | |||
(rss +org) | |||
:config | |||
literate | |||
(default +bindings +smartparens)) |
@ -0,0 +1,948 @@ | |||
/* | |||
Quietfox for Firefox 76 | |||
https://github.com/coekuss/quietfox | |||
Thanks for using my file | |||
*/ | |||
* { | |||
/* -------------------- ๐จ Customization ๐จ -------------------- */ | |||
--tab-corner-rounding: 5px; | |||
--button-corner-rounding: 4px; | |||
--menu-corner-rounding: 5px; | |||
--menu-item-height: 30px; | |||
--animation-speed: 0.15s; | |||
} | |||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); | |||
/* Bottom corner rounding for tabs, Remove this section if your tab bottom corners look bad */ | |||
tab[visuallyselected] .tab-background::before, | |||
tab[visuallyselected] .tab-background::after { | |||
content: "" !important; | |||
display: inline !important; | |||
position: absolute !important; | |||
width: var(--tab-corner-rounding) !important; | |||
height: var(--tab-corner-rounding) !important; | |||
bottom: -1px !important; | |||
pointer-events: none !important; | |||
background-color: transparent !important; | |||
transition: var(--animation-speed) !important; | |||
} | |||
tab[visuallyselected] .tab-background::before { | |||
border-bottom-right-radius: var(--tab-corner-rounding) !important; | |||
transform: translateX(calc(-1 * var(--tab-corner-rounding))) !important; | |||
box-shadow: 4px 4px 0px 4px var(--toolbar-bgcolor) !important; | |||
transition: var(--animation-speed) !important; | |||
} | |||
tab[visuallyselected] .tab-background::after { | |||
border-bottom-left-radius: var(--tab-corner-rounding) !important; | |||
right: 0px !important; | |||
transform: translateX(calc(var(--tab-corner-rounding) + 1px)) !important; | |||
box-shadow: -4px 4px 0px 4px var(--toolbar-bgcolor) !important; | |||
transition: var(--animation-speed) !important; | |||
} | |||
/* --------------------------------------------------------------------------------------- */ | |||
/* This makes certain menus adapt to your toolbar text color. Can make menu text illegible in | |||
some transparent themes, remove if needed. */ | |||
panelview:not([extension]), | |||
.panel-arrowcontent { | |||
background-color: var(--toolbar-bgcolor) !important; | |||
color: var(--toolbar-color) !important; | |||
} | |||
/*-------------------------------------------------------------------*/ | |||
/* -------------------- Transitions for some things -------------------- */ | |||
toolbarbutton, | |||
.subviewbutton, | |||
stack, | |||
.toolbarbutton-icon, | |||
vbox | |||
{ | |||
transition: background-color var(--animation-speed) !important; | |||
} | |||
/* -------------------- Style tab bar -------------------- */ | |||
.titlebar-spacer[type="pre-tabs"] { | |||
border-inline-end: 0px !important; | |||
width: 0px !important; | |||
} | |||
scrollbox[part="scrollbox"][flex="1"][orient="horizontal"] { | |||
padding-left: 12px !important; | |||
} | |||
[sizemode="maximized"] [first-visible-tab] { | |||
margin-left: -12px !important; | |||
} | |||
[sizemode="maximized"] [first-visible-tab] stack { | |||
margin-left: 12px !important; | |||
} | |||
.tab-background, .tab-loading-burst { | |||
border-radius: var(--tab-corner-rounding) var(--tab-corner-rounding) 0px 0px !important; | |||
margin: -1px !important; | |||
} | |||
tab:not(:active) .tab-background { | |||
transition: background-color var(--animation-speed) !important; | |||
} | |||
:root[uidensity="compact"] { | |||
--tab-min-height: 31px !important; | |||
} | |||
tab:not([selected]):hover .tab-background { | |||
background-color: var(--toolbarbutton-hover-background) !important; | |||
} | |||
tab { | |||
min-width: 1px !important; | |||
clip-width: 1px !important; | |||
padding-bottom: 1px !important; | |||
} | |||
.tab-line { | |||
display: none !important; | |||
} | |||
tab:not([beforeselected-visible])::after { | |||
margin-top: 4px !important; | |||
margin-bottom: 4px !important; | |||
transition: border-color var(--animation-speed), margin-top var(--animation-speed) !important; | |||
} | |||
tab[beforehovered]::after, | |||
tab:hover::after { | |||
border-color: transparent !important; | |||
margin-top: 20px !important; | |||
} | |||
#navigator-toolbox { | |||
--tabs-border-color: transparent !important; | |||
} | |||
tab[visuallyselected] .tab-background { | |||
box-shadow: 0px 3px 12px -5px black !important; | |||
} | |||
[last-visible-tab] { | |||
margin-right: calc(var(--tab-corner-rounding) + 2px) !important; /* fix unexpected tab overflow */ | |||
} | |||
/* -------------------- Style toolbar -------------------- */ | |||
.toolbarbutton-icon, .toolbarbutton-badge-stack { | |||
background-color: transparent !important; | |||
} | |||
toolbarbutton:not(#back-button)[open], .toolbarbutton-1:not(#back-button)[open] { | |||
background-color: var(--toolbarbutton-active-background); | |||
} | |||
#nav-bar .toolbarbutton-1 { | |||
margin-right: 2px !important; | |||
} | |||
.close-icon:not(.tab-close-button):hover { | |||
fill-opacity: 0.0 !important; | |||
} | |||
toolbarbutton:not(#back-button):not([disabled]):not([open]):hover, | |||
.toolbarbutton-1:not(#back-button):not([disabled]):not([open]):hover { | |||
background-color: var(--toolbarbutton-hover-background) !important; | |||
} | |||
.tab-close-button { | |||
transition: fill-opacity var(--animation-speed) !important; | |||
} | |||
.findbar-textbox:focus { | |||
border: 1px solid grey !important; | |||
} | |||
#BMB_bookmarksShowAll { | |||
display: none !important; | |||
} | |||
/* touch and normal density buttons */ | |||
:root:not([uidensity="compact"]) #back-button > .toolbarbutton-icon { | |||
background-color: transparent !important; | |||
border-color: var(--toolbarbutton-hover-background) !important; | |||
transition: background-color var(--animation-speed) !important; | |||
} | |||
:root:not([uidensity="compact"]) #back-button:not([disabled]):not([open]):hover > .toolbarbutton-icon { | |||
background-color: var(--toolbarbutton-hover-background) !important; | |||
box-shadow: none !important; | |||
} | |||
:root:not([uidensity="compact"]) #back-button:not([disabled]):not([open]):active > .toolbarbutton-icon { | |||
background-color: var(--toolbarbutton-active-background) !important; | |||
box-shadow: none !important; | |||
} | |||
:root:not([uidensity="compact"]) toolbarbutton:not(#back-button):not([disabled]):not([open]):active, | |||
.toolbarbutton-1:not(#back-button):not([disabled]):not([open]):active { | |||
background-color: var(--toolbarbutton-active-background) !important; | |||
} | |||
:root:not([uidensity="compact"]) #back-button[open] > .toolbarbutton-icon { | |||
background-color: var(--toolbarbutton-active-background) !important; | |||
} | |||
:root:not([uidensity="compact"]) #PersonalToolbar { /* bookmark bar */ | |||
height: 30px !important; | |||
} | |||
/* normal density buttons */ | |||
:root:not([uidensity="touch"]):not([uidensity="compact"]) #nav-bar .toolbarbutton-1:not(#back-button) { | |||
margin-top: 4px !important; | |||
margin-bottom: 4px !important; | |||
margin-left: 0px !important; | |||
margin-right: 0px !important; | |||
padding-left: 3px !important; | |||