summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Vink <mike1994vink@gmail.com>2023-06-27 13:23:15 +0200
committerMike Vink <mike1994vink@gmail.com>2023-06-27 13:23:15 +0200
commitf4effc7665bf280c8587c6c65e4ee769d0244363 (patch)
tree24a0246a65b637ef7b37c922f26bb2790e393f0a
parentfe1eea2ebb749fc89a56a972d03a7797b9cc36e8 (diff)
stuff
-rw-r--r--email/gmail.nix60
-rw-r--r--email/neomutt.nix174
-rw-r--r--email/notmuch.nix26
-rw-r--r--home.nix7
m---------home/dwmblocks0
-rw-r--r--home/packages.nix1
-rwxr-xr-xshell-scripts/kakup25
-rw-r--r--shell-scripts/sb-battery37
-rw-r--r--shell-scripts/sb-clock29
-rw-r--r--shell-scripts/sb-internet26
-rw-r--r--shell-scripts/sb-nettraf29
-rw-r--r--shell-scripts/sb-news17
-rwxr-xr-xshell-scripts/sysact26
13 files changed, 449 insertions, 8 deletions
diff --git a/email/gmail.nix b/email/gmail.nix
new file mode 100644
index 0000000..a883b17
--- /dev/null
+++ b/email/gmail.nix
@@ -0,0 +1,60 @@
+{
+ flake,
+ config,
+ pkgs,
+ home-manager,
+ ...
+}: {
+ accounts.email = {
+ maildirBasePath = "${config.xdg.dataHome}/mail";
+ accounts = {
+ gmail = {
+ primary = true;
+ realName = "Mike Vink";
+ userName = "mike1994vink@gmail.com";
+ address = "mike1994vink@gmail.com";
+ passwordCommand = ["${pkgs.pass}/bin/pass" "personal/neomutt"];
+ imap = { host = "imap.gmail.com"; port = 993; tls = { enable = true; }; };
+ smtp = { host = "smtp.gmail.com"; port = 587; tls = { enable = true; useStartTls = true; }; };
+ neomutt = {
+ enable = true;
+ sendMailCommand = "echo 'hi'";
+ mailboxName = "=== gmail ===";
+ extraConfig = ''
+ set spoolfile='Inbox'
+ '';
+ };
+ mbsync = {
+ enable = true;
+ create = "both"; remove = "both"; expunge = "both";
+ groups = {
+ mailboxes = {
+ channels = {
+ Inbox = { farPattern = "INBOX"; nearPattern = "INBOX"; extraConfig = { Create = "Near"; }; };
+ Archive = { farPattern = "[Gmail]/All Mail"; nearPattern = "Archive"; extraConfig = { Create = "Near"; }; };
+ Spam = { farPattern = "[Gmail]/Spam"; nearPattern = "Spam"; extraConfig = { Create = "Near"; }; };
+ Trash = { farPattern = "[Gmail]/Bin"; nearPattern = "Trash"; extraConfig = { Create = "Near"; }; };
+ Important = { farPattern = "[Gmail]/Important"; nearPattern = "Important"; extraConfig = { Create = "Near"; }; };
+ Sent = { farPattern = "[Gmail]/Sent Mail"; nearPattern = "Sent"; extraConfig = { Create = "Near"; }; };
+ FarDrafts = { farPattern = "[Gmail]/Drafts"; nearPattern = "FarDrafts"; extraConfig = { Create = "Near"; }; };
+ };
+ };
+ };
+ };
+ notmuch = {
+ enable = true;
+ neomutt = {
+ enable = true;
+ virtualMailboxes = [
+ { name = "Inbox"; query = "tag:inbox"; }
+ { name = "Archive"; query = "tag:archive"; }
+ { name = "Sent"; query = "tag:sent"; }
+ { name = "Spam"; query = "tag:spam"; }
+ { name = "Trash"; query = "tag:trash"; }
+ ];
+ };
+ };
+ };
+ };
+ };
+}
diff --git a/email/neomutt.nix b/email/neomutt.nix
new file mode 100644
index 0000000..b8bc848
--- /dev/null
+++ b/email/neomutt.nix
@@ -0,0 +1,174 @@
+{
+ flake,
+ config,
+ pkgs,
+ home-manager,
+ ...
+}: {
+ programs.neomutt = {
+ enable = true;
+ sort = "reverse-date";
+ extraConfig = ''
+ set use_threads=yes
+ set send_charset="us-ascii:utf-8"
+ set mailcap_path = $HOME/.config/mutt/mailcap
+ set mime_type_query_command = "file --mime-type -b %s"
+ set date_format="%y/%m/%d %I:%M%p"
+ set index_format="%2C %Z %?X?A& ? %D %-15.15F %s (%-4.4c)"
+ set smtp_authenticators = 'gssapi:login'
+ set query_command = "abook --mutt-query '%s'"
+ set rfc2047_parameters = yes
+ set sleep_time = 0 # Pause 0 seconds for informational messages
+ set markers = no # Disables the `+` displayed at line wraps
+ set mark_old = no # Unread mail stay unread until read
+ set mime_forward = no # mail body is forwarded as text
+ set forward_attachments = yes # attachments are forwarded with mail
+ set wait_key = no # mutt won't ask "press key to continue"
+ set fast_reply # skip to compose when replying
+ set fcc_attach # save attachments with the body
+ set forward_format = "Fwd: %s" # format of subject when forwarding
+ set forward_quote # include message in forwards
+ set reverse_name # reply as whomever it was to
+ set include # include message in replies
+ set mail_check=0 # to avoid lags using IMAP with some email providers (yahoo for example)
+ auto_view text/html # automatically show html (mailcap uses lynx)
+ auto_view application/pgp-encrypted
+ #set display_filter = "tac | sed '/\\\[-- Autoview/,+1d' | tac" # Suppress autoview messages.
+ alternative_order text/plain text/enriched text/html
+
+ set sidebar_visible = yes
+ set sidebar_width = 20
+ set sidebar_short_path = yes
+ set sidebar_next_new_wrap = yes
+ set mail_check_stats
+ set sidebar_format = '%D%?F? [%F]?%* %?N?%N/? %?S?%S?'
+ bind index,pager \Ck sidebar-prev
+ bind index,pager \Cj sidebar-next
+ bind index,pager \Co sidebar-open
+ bind index,pager \Cp sidebar-prev-new
+ bind index,pager \Cn sidebar-next-new
+ bind index,pager B sidebar-toggle-visible
+
+ # Default index colors:
+ color index yellow default '.*'
+ color index_author red default '.*'
+ color index_number blue default
+ color index_subject cyan default '.*'
+
+ # New mail is boldened:
+ color index brightyellow black "~N"
+ color index_author brightred black "~N"
+ color index_subject brightcyan black "~N"
+
+ # Tagged mail is highlighted:
+ color index brightyellow blue "~T"
+ color index_author brightred blue "~T"
+ color index_subject brightcyan blue "~T"
+
+ # Flagged mail is highlighted:
+ color index brightgreen default "~F"
+ color index_subject brightgreen default "~F"
+ color index_author brightgreen default "~F"
+
+ # Other colors and aesthetic settings:
+ mono bold bold
+ mono underline underline
+ mono indicator reverse
+ mono error bold
+ color normal default default
+ color indicator brightblack white
+ color sidebar_highlight red default
+ color sidebar_divider brightblack black
+ color sidebar_flagged red black
+ color sidebar_new green black
+ color error red default
+ color tilde black default
+ color message cyan default
+ color markers red white
+ color attachment white default
+ color search brightmagenta default
+ color status brightyellow black
+ color hdrdefault brightgreen default
+ color quoted green default
+ color quoted1 blue default
+ color quoted2 cyan default
+ color quoted3 yellow default
+ color quoted4 red default
+ color quoted5 brightred default
+ color signature brightgreen default
+ color bold black default
+ color underline black default
+
+ # Regex highlighting:
+ color header brightmagenta default "^From"
+ color header brightcyan default "^Subject"
+ color header brightwhite default "^(CC|BCC)"
+ color header blue default ".*"
+ color body brightred default "[\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+" # Email addresses
+ color body brightblue default "(https?|ftp)://[\-\.,/%~_:?&=\#a-zA-Z0-9]+" # URL
+ color body green default "\`[^\`]*\`" # Green text between ` and `
+ color body brightblue default "^# \.*" # Headings as bold blue
+ color body brightcyan default "^## \.*" # Subheadings as bold cyan
+ color body brightgreen default "^### \.*" # Subsubheadings as bold green
+ color body yellow default "^(\t| )*(-|\\*) \.*" # List items as yellow
+ color body brightcyan default "[;:][-o][)/(|]" # emoticons
+ color body brightcyan default "[;:][)(|]" # emoticons
+ color body brightcyan default "[ ][*][^*]*[*][ ]?" # more emoticon?
+ color body brightcyan default "[ ]?[*][^*]*[*][ ]" # more emoticon?
+ color body red default "(BAD signature)"
+ color body cyan default "(Good signature)"
+ color body brightblack default "^gpg: Good signature .*"
+ color body brightyellow default "^gpg: "
+ color body brightyellow red "^gpg: BAD signature from.*"
+ mono body bold "^gpg: Good signature"
+ mono body bold "^gpg: BAD signature from.*"
+ color body red default "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]"
+ '';
+ sidebar = {
+ enable = true;
+ };
+ binds = [
+ { map = ["index" "pager"]; key = "i"; action = "noop"; }
+ { map = ["index" "pager"]; key = "g"; action = "noop"; }
+ { map = ["index"]; key = "\\Cf"; action = "noop"; }
+ { map = ["index" "pager"]; key = "M"; action = "noop"; }
+ { map = ["index" "pager"]; key = "C"; action = "noop"; }
+ { map = ["index"]; key = "gg"; action = "first-entry"; }
+ { map = ["index"]; key = "j"; action = "next-entry"; }
+ { map = ["index"]; key = "k"; action = "previous-entry"; }
+ { map = ["attach"]; key = "<return>"; action = "view-mailcap"; }
+ { map = ["attach"]; key = "l"; action = "view-mailcap"; }
+ { map = ["editor"]; key = "<space>"; action = "noop"; }
+ { map = ["index"]; key = "G"; action = "last-entry"; }
+ { map = ["pager" "attach"]; key = "h"; action = "exit"; }
+ { map = ["pager"]; key = "j"; action = "next-line"; }
+ { map = ["pager"]; key = "k"; action = "previous-line"; }
+ { map = ["pager"]; key = "l"; action = "view-attachments"; }
+ { map = ["index"]; key = "D"; action = "delete-message"; }
+ { map = ["index"]; key = "U"; action = "undelete-message"; }
+ { map = ["index"]; key = "L"; action = "limit"; }
+ { map = ["index"]; key = "h"; action = "noop"; }
+ { map = ["index"]; key = "l"; action = "display-message"; }
+ { map = ["index" "query"]; key = "<space>"; action = "tag-entry"; }
+ { map = ["index" "pager"]; key = "H"; action = "view-raw-message"; }
+ { map = ["browser"]; key = "l"; action = "select-entry"; }
+ { map = ["browser"]; key = "gg"; action = "top-page"; }
+ { map = ["browser"]; key = "G"; action = "bottom-page"; }
+ { map = ["pager"]; key = "gg"; action = "top"; }
+ { map = ["pager"]; key = "G"; action = "bottom"; }
+ { map = ["index" "pager" "browser"]; key = "d"; action = "half-down"; }
+ { map = ["index" "pager" "browser"]; key = "u"; action = "half-up"; }
+ { map = ["index" "pager"]; key = "S"; action = "sync-mailbox"; }
+ { map = ["index" "pager"]; key = "R"; action = "group-reply"; }
+ { map = ["index"]; key = "\031"; action = "previous-undeleted"; }
+ { map = ["index"]; key = "\005"; action = "next-undeleted"; }
+ { map = ["pager"]; key = "\031"; action = "previous-line"; }
+ { map = ["pager"]; key = "\005"; action = "next-line"; }
+ { map = ["editor"]; key = "<Tab>"; action = "complete-query"; }
+ ];
+ macros = [
+ { map = ["browser"]; key = "h"; action = "<change-dir><kill-line>..<enter>"; }
+ { map = ["index"]; key = "<right>"; action = "<enter-command>toggle sidebar_visible<enter><refresh>"; }
+ ];
+ };
+}
diff --git a/email/notmuch.nix b/email/notmuch.nix
new file mode 100644
index 0000000..15bfcd9
--- /dev/null
+++ b/email/notmuch.nix
@@ -0,0 +1,26 @@
+{
+ flake,
+ config,
+ pkgs,
+ home-manager,
+ ...
+}: {
+ programs.mbsync = {
+ enable = true;
+ };
+ programs.notmuch = {
+ enable = true;
+ new = {
+ tags = ["unread" "inbox"];
+ ignore = [".mbsyncstate" ".uidvalidity"];
+ };
+ search.excludeTags = ["deleted" "spam"];
+ maildir.synchronizeFlags = true;
+ extraConfig = {
+ database.path = "${config.xdg.dataHome}/mail";
+ user.name = "Mike Vink";
+ user.primary_email = "mike1994vink@gmail.com";
+ crypto.gpg_path="gpg";
+ };
+ };
+}
diff --git a/home.nix b/home.nix
index 6dd63e6..45339ac 100644
--- a/home.nix
+++ b/home.nix
@@ -9,12 +9,16 @@
home.homeDirectory = "/home/${username}";
home.username = username;
home.stateVersion = "23.05";
+ xdg = {
+ enable = true;
+ };
fonts.fontconfig.enable = true;
programs.home-manager.enable = true;
home.sessionVariables = {
- EDITOR = "nvim";
+ EDITOR = "kak";
+ TERMINAL = "st";
};
home.sessionPath = [
@@ -136,5 +140,4 @@
programs.password-store = {
enable = true;
};
-
}
diff --git a/home/dwmblocks b/home/dwmblocks
-Subproject 50f519c335008abe52bab6a21db09df14076e36
+Subproject a933ce0d6109524b393feb3e7156cbf0de88b42
diff --git a/home/packages.nix b/home/packages.nix
index 9a2c0bb..075736a 100644
--- a/home/packages.nix
+++ b/home/packages.nix
@@ -20,6 +20,7 @@
nnn
python311Packages.editorconfig
# shell tools
+ pstree
pywal
bashInteractive
k9s
diff --git a/shell-scripts/kakup b/shell-scripts/kakup
index be12494..f2f894b 100755
--- a/shell-scripts/kakup
+++ b/shell-scripts/kakup
@@ -1,3 +1,4 @@
+#!@bash@/bin/bash
session-or-client() {
{
project="$(cat -)"
@@ -65,14 +66,26 @@ search() {
popd >/dev/null 2>&1
}
-case "${@}" in
- "") client="$(pwd | session-or-client)" ;;
- .) client="$(search | session-or-client)" ;;
- *) client="$(echo "${@}" | session-or-client)" ;;
-esac
+NEW_TERM=0
+while getopts "tf" opt; do
+ case "$opt" in
+ h|\?) exit 0 ;;
+ t) NEW_TERM=1 ;;
+ f) client="$(search | session-or-client)" ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+[ -z "$client" ] && {
+ case "${@}" in
+ "") client="$(pwd | session-or-client)" ;;
+ *) client="$(echo "${@}" | session-or-client)" ;;
+ esac
+}
[[ ! -z "$client" ]] && {
- if tty -s
+ if [ $NEW_TERM -eq 0 ]
then
tmux attach -t "$client"
else
diff --git a/shell-scripts/sb-battery b/shell-scripts/sb-battery
new file mode 100644
index 0000000..aeb7413
--- /dev/null
+++ b/shell-scripts/sb-battery
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+# Prints all batteries, their percentage remaining and an emoji corresponding
+# to charge status (🔌 for plugged up, 🔋 for discharging on battery, etc.).
+
+case $BUTTON in
+ 3) notify-send "🔋 Battery module" "🔋: discharging
+🛑: not charging
+♻: stagnant charge
+🔌: charging
+⚡: charged
+❗: battery very low!
+- Scroll to change adjust xbacklight." ;;
+ 4) xbacklight -inc 10 ;;
+ 5) xbacklight -dec 10 ;;
+ 6) "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+# Loop through all attached batteries and format the info
+for battery in /sys/class/power_supply/BAT?*; do
+ # If non-first battery, print a space separator.
+ [ -n "${capacity+x}" ] && printf " "
+ # Sets up the status and capacity
+ case "$(cat "$battery/status" 2>&1)" in
+ "Full") status="⚡" ;;
+ "Discharging") status="🔋" ;;
+ "Charging") status="🔌" ;;
+ "Not charging") status="🛑" ;;
+ "Unknown") status="♻️" ;;
+ *) exit 1 ;;
+ esac
+ capacity="$(cat "$battery/capacity" 2>&1)"
+ # Will make a warn variable if discharging and low
+ [ "$status" = "🔋" ] && [ "$capacity" -le 25 ] && warn="❗"
+ # Prints the info
+ printf "%s%s%d%%" "$status" "$warn" "$capacity"; unset warn
+done && printf "\\n"
diff --git a/shell-scripts/sb-clock b/shell-scripts/sb-clock
new file mode 100644
index 0000000..d32f787
--- /dev/null
+++ b/shell-scripts/sb-clock
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+clock=$(date '+%I')
+
+case "$clock" in
+ "00") icon="🕛" ;;
+ "01") icon="🕐" ;;
+ "02") icon="🕑" ;;
+ "03") icon="🕒" ;;
+ "04") icon="🕓" ;;
+ "05") icon="🕔" ;;
+ "06") icon="🕕" ;;
+ "07") icon="🕖" ;;
+ "08") icon="🕗" ;;
+ "09") icon="🕘" ;;
+ "10") icon="🕙" ;;
+ "11") icon="🕚" ;;
+ "12") icon="🕛" ;;
+esac
+
+case $BUTTON in
+ 1) notify-send "This Month" "$(cal --color=always | sed "s/..7m/<b><span color=\"red\">/;s|..27m|</span></b>|")" && notify-send "Appointments" "$(calcurse -d3)" ;;
+ 2) setsid -f "$TERMINAL" -e calcurse ;;
+ 3) notify-send "📅 Time/date module" "\- Left click to show upcoming appointments for the next three days via \`calcurse -d3\` and show the month via \`cal\`
+- Middle click opens calcurse if installed" ;;
+ 6) "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+date "+%Y %b %d (%a) $icon%I:%M%p"
diff --git a/shell-scripts/sb-internet b/shell-scripts/sb-internet
new file mode 100644
index 0000000..225d376
--- /dev/null
+++ b/shell-scripts/sb-internet
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# Show wifi 📶 and percent strength or 📡 if none.
+# Show 🌐 if connected to ethernet or ❎ if none.
+# Show 🔒 if a vpn connection is active
+
+case $BUTTON in
+ 1) "$TERMINAL" -e nmtui; pkill -RTMIN+4 dwmblocks ;;
+ 3) notify-send "🌐 Internet module" "\- Click to connect
+❌: wifi disabled
+📡: no wifi connection
+📶: wifi connection with quality
+❎: no ethernet
+🌐: ethernet working
+🔒: vpn is active
+" ;;
+ 6) "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+if grep -xq 'up' /sys/class/net/w*/operstate 2>/dev/null ; then
+ wifiicon="$(awk '/^\s*w/ { print "📶", int($3 * 100 / 70) "% " }' /proc/net/wireless)"
+elif grep -xq 'down' /sys/class/net/w*/operstate 2>/dev/null ; then
+ grep -xq '0x1003' /sys/class/net/w*/flags && wifiicon="📡 " || wifiicon="❌ "
+fi
+
+printf "%s%s%s\n" "$wifiicon" "$(sed "s/down/❎/;s/up/🌐/" /sys/class/net/e*/operstate 2>/dev/null)" "$(sed "s/.*/🔒/" /sys/class/net/tun*/operstate 2>/dev/null)"
diff --git a/shell-scripts/sb-nettraf b/shell-scripts/sb-nettraf
new file mode 100644
index 0000000..06b3c49
--- /dev/null
+++ b/shell-scripts/sb-nettraf
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+# Module showing network traffic. Shows how much data has been received (RX) or
+# transmitted (TX) since the previous time this script ran. So if run every
+# second, gives network traffic per second.
+
+case $BUTTON in
+ 1) setsid -f "$TERMINAL" -e bmon ;;
+ 3) notify-send "🌐 Network traffic module" "🔻: Traffic received
+🔺: Traffic transmitted" ;;
+ 6) "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+update() {
+ sum=0
+ for arg; do
+ read -r i < "$arg"
+ sum=$(( sum + i ))
+ done
+ cache=/tmp/${1##*/}
+ [ -f "$cache" ] && read -r old < "$cache" || old=0
+ printf %d\\n "$sum" > "$cache"
+ printf %d\\n $(( sum - old ))
+}
+
+rx=$(update /sys/class/net/[ew]*/statistics/rx_bytes)
+tx=$(update /sys/class/net/[ew]*/statistics/tx_bytes)
+
+printf "🔻%4sB 🔺%4sB\\n" $(numfmt --to=iec $rx $tx)
diff --git a/shell-scripts/sb-news b/shell-scripts/sb-news
new file mode 100644
index 0000000..a155c0a
--- /dev/null
+++ b/shell-scripts/sb-news
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# Displays number of unread news items and an loading icon if updating.
+# When clicked, brings up `newsboat`.
+
+case $BUTTON in
+ 1) setsid "$TERMINAL" -e newsboat ;;
+ 2) setsid -f newsup >/dev/null exit ;;
+ 3) notify-send "📰 News module" "\- Shows unread news items
+- Shows 🔃 if updating with \`newsup\`
+- Left click opens newsboat
+- Middle click syncs RSS feeds
+<b>Note:</b> Only one instance of newsboat (including updates) may be running at a time." ;;
+ 6) "$TERMINAL" -e "$EDITOR" "$0" ;;
+esac
+
+ cat /tmp/newsupdate 2>/dev/null || echo "$(newsboat -x print-unread | awk '{ if($1>0) print "📰" $1}')$(cat "${XDG_CONFIG_HOME:-$HOME/.config}"/newsboat/.update 2>/dev/null)"
diff --git a/shell-scripts/sysact b/shell-scripts/sysact
new file mode 100755
index 0000000..64c2f32
--- /dev/null
+++ b/shell-scripts/sysact
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+# A dmenu wrapper script for system functions.
+export WM="dwm"
+case "$(readlink -f /sbin/init)" in
+ *systemd*) ctl='systemctl' ;;
+ *) ctl='loginctl' ;;
+esac
+
+wmpid(){ # This function is needed if there are multiple instances of the window manager.
+ tree="$(pstree -ps $$)"
+ tree="${tree#*$WM(}"
+ echo "${tree%%)*}"
+}
+
+case "$(printf "🔒 lock\n🚪 leave $WM\n♻️ renew $WM\n🐻 hibernate\n🔃 reboot\n🖥️shutdown\n💤 sleep\n📺 display off" | dmenu -i -p 'Action: ')" in
+ '🔒 lock') slock ;;
+ "🚪 leave $WM") kill -TERM "$(wmpid)" ;;
+ "♻️ renew $WM") kill -HUP "$(wmpid)" ;;
+ '🐻 hibernate') slock $ctl hibernate -i ;;
+ '💤 sleep') slock $ctl suspend -i ;;
+ '🔃 reboot') $ctl reboot -i ;;
+ '🖥️shutdown') $ctl poweroff -i ;;
+ '📺 display off') xset dpms force off ;;
+ *) exit 1 ;;
+esac