diff --git a/README.md b/README.md index 4628076..402ba5f 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,13 @@ For spmenu: - ~~Unhiding a hidden window (using the show/hide function) and if it's the only program running, crashes dwm~~ - ~~Alt-tab crashes dwm altogther (idk man)~~ +## Future plans +- [ ] Rebase the dwm build to dwm-flexipatch (maybe under a new branch with a VM debug environment?) +- [ ] Integrate barmodules if the dwm-flexipatch rewrite did happen +- [ ] Version jump from 6.3 -> 6.4 +- [ ] Potentially making this project into a desktop environment, when I feel it's ready to do so +- [ ] Use `spmenu-desktop-launcher` if it's mature/usable, retaining `spmenu_run` for backwards compatibility with existing scripts + ## Patching even further Patching everything is as easy as downloading the diff file, use the `patch` command and apply changes. diff --git a/dwm/patches/dwm-resizepoint-6.2.diff b/dwm/patches/dwm-resizepoint-6.2.diff new file mode 100644 index 0000000..7287fb4 --- /dev/null +++ b/dwm/patches/dwm-resizepoint-6.2.diff @@ -0,0 +1,97 @@ +From 3a6c2b419b9e5787fa388bad5a184b70432a41d8 Mon Sep 17 00:00:00 2001 +From: bakkeby +Date: Wed, 24 Jun 2020 14:16:27 +0200 +Subject: [PATCH] resizepoint - Like resizecorners, but does not warp mouse + pointer + +--- + dwm.c | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +diff --git a/dwm.c b/dwm.c +index 4465af1..8c82bf3 100644 +--- a/dwm.c ++++ b/dwm.c +@@ -58,7 +58,7 @@ + #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + + /* enums */ +-enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */ ++enum { CurResizeBR, CurResizeBL, CurResizeTR, CurResizeTL, CurNormal, CurMove, CurLast }; /* cursor */ + enum { SchemeNorm, SchemeSel }; /* color schemes */ + enum { NetSupported, NetWMName, NetWMState, NetWMCheck, + NetWMFullscreen, NetActiveWindow, NetWMWindowType, +@@ -1290,10 +1290,13 @@ resizeclient(Client *c, int x, int y, int w, int h) + void + resizemouse(const Arg *arg) + { +- int ocx, ocy, nw, nh; ++ int opx, opy, ocx, ocy, och, ocw, nx, ny, nw, nh; + Client *c; + Monitor *m; + XEvent ev; ++ int horizcorner, vertcorner; ++ unsigned int dui; ++ Window dummy; + Time lasttime = 0; + + if (!(c = selmon->sel)) +@@ -1303,10 +1306,15 @@ resizemouse(const Arg *arg) + restack(selmon); + ocx = c->x; + ocy = c->y; ++ och = c->h; ++ ocw = c->w; ++ if (!XQueryPointer(dpy, c->win, &dummy, &dummy, &opx, &opy, &nx, &ny, &dui)) ++ return; ++ horizcorner = nx < c->w / 2; ++ vertcorner = ny < c->h / 2; + if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync, +- None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess) ++ None, cursor[horizcorner | (vertcorner << 1)]->cursor, CurrentTime) != GrabSuccess) + return; +- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + do { + XMaskEvent(dpy, MOUSEMASK|ExposureMask|SubstructureRedirectMask, &ev); + switch(ev.type) { +@@ -1320,8 +1328,10 @@ resizemouse(const Arg *arg) + continue; + lasttime = ev.xmotion.time; + +- nw = MAX(ev.xmotion.x - ocx - 2 * c->bw + 1, 1); +- nh = MAX(ev.xmotion.y - ocy - 2 * c->bw + 1, 1); ++ nx = horizcorner ? (ocx + ev.xmotion.x - opx) : c->x; ++ ny = vertcorner ? (ocy + ev.xmotion.y - opy) : c->y; ++ nw = MAX(horizcorner ? (ocx + ocw - nx) : (ocw + (ev.xmotion.x - opx)), 1); ++ nh = MAX(vertcorner ? (ocy + och - ny) : (och + (ev.xmotion.y - opy)), 1); + if (c->mon->wx + nw >= selmon->wx && c->mon->wx + nw <= selmon->wx + selmon->ww + && c->mon->wy + nh >= selmon->wy && c->mon->wy + nh <= selmon->wy + selmon->wh) + { +@@ -1330,11 +1340,10 @@ resizemouse(const Arg *arg) + togglefloating(NULL); + } + if (!selmon->lt[selmon->sellt]->arrange || c->isfloating) +- resize(c, c->x, c->y, nw, nh, 1); ++ resizeclient(c, nx, ny, nw, nh); + break; + } + } while (ev.type != ButtonRelease); +- XWarpPointer(dpy, None, c->win, 0, 0, 0, 0, c->w + c->bw - 1, c->h + c->bw - 1); + XUngrabPointer(dpy, CurrentTime); + while (XCheckMaskEvent(dpy, EnterWindowMask, &ev)); + if ((m = recttomon(c->x, c->y, c->w, c->h)) != selmon) { +@@ -1564,7 +1573,10 @@ setup(void) + netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False); + /* init cursors */ + cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr); +- cursor[CurResize] = drw_cur_create(drw, XC_sizing); ++ cursor[CurResizeBR] = drw_cur_create(drw, XC_bottom_right_corner); ++ cursor[CurResizeBL] = drw_cur_create(drw, XC_bottom_left_corner); ++ cursor[CurResizeTR] = drw_cur_create(drw, XC_top_right_corner); ++ cursor[CurResizeTL] = drw_cur_create(drw, XC_top_left_corner); + cursor[CurMove] = drw_cur_create(drw, XC_fleur); + /* init appearance */ + scheme = ecalloc(LENGTH(colors), sizeof(Clr *)); +-- +2.19.1 + diff --git a/scripts/dunst/Makefile b/scripts/dunst/Makefile new file mode 100644 index 0000000..89661fb --- /dev/null +++ b/scripts/dunst/Makefile @@ -0,0 +1,10 @@ +install: + mkdir -p ${PREFIX}${DESTDIR}/bin + cp -f volume-dunst ${PREFIX}${DESTDIR}/bin + chmod 755 ${PREFIX}${DESTDIR}/bin/volume-dunst + cp -f notify-vol-send ${PREFIX}${DESTDIR}/bin/notify-vol-send + chmod 755 ${PREFIX}${DESTDIR}/bin/notify-vol-send + +uninstall: + rm -f ${PREFIX}${DESTDIR}/bin/volume-dunst + rm -f ${PREFIX}${DESTDIR}/bin/notify-vol-send diff --git a/scripts/dunst/notify-vol-send b/scripts/dunst/notify-vol-send new file mode 100755 index 0000000..4149966 --- /dev/null +++ b/scripts/dunst/notify-vol-send @@ -0,0 +1,326 @@ +#!/usr/bin/env bash + +# notify-send.sh - drop-in replacement for notify-send with more features +# Copyright (C) 2015-2021 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 . + +# Desktop Notifications Specification +# https://developer.gnome.org/notification-spec/ + +VERSION=1.2 +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 < [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 + SLEEP_TIME="$( LC_NUMERIC=C printf %f "${EXPIRE_TIME}e-3" )" + ( 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"; } + if ! [[ "$EXPIRE_TIME" =~ ^-?[0-9]+$ ]]; then + echo "Invalid expire time: ${EXPIRE_TIME}" + exit 1; + fi + ;; + -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"; } + # always check that --close provides a numeric value + if [[ -z "$close_id" || ! "$close_id" =~ ^[0-9]+$ ]]; then + echo "Invalid close id: '$close_id'" + exit 1 + fi + notify_close "$close_id" + exit $? + ;; + --) + positional=yes + ;; + *) + process_posargs "$1" + ;; + esac + shift +done + +# always force --replace and --replace-file to provide a numeric value; 0 means no id provided +if [[ -z "$REPLACE_ID" || ! "$REPLACE_ID" =~ ^[0-9]+$ ]]; then + REPLACE_ID=0 +fi + +# urgency is always set +HINTS=("$(make_hint byte urgency "$URGENCY")" "${HINTS[@]}") + +if [[ "$SUMMARY_SET" = n ]] ; then + help + exit 1 +else + notify +fi diff --git a/scripts/dunst/volume-dunst b/scripts/dunst/volume-dunst new file mode 100644 index 0000000..6602b86 --- /dev/null +++ b/scripts/dunst/volume-dunst @@ -0,0 +1,70 @@ +#!/bin/bash + +# You can call this script like this: +# $./volume.sh up +# $./volume.sh down +# $./volume.sh mute + +function get_volume { + pamixer --get-volume | cut -d '%' -f 1 +} + +function is_mute { + pamixer --get-mute | grep true > /dev/null +} + +function send_notification { + DIR=`dirname "$0"` + volume=`get_volume` + # Make the bar with the special character ─ (it's not dash -) + # https://en.wikipedia.org/wiki/Box-drawing_character +#bar=$(seq -s "─" $(($volume/5)) | sed 's/[0-9]//g') +if [ "$volume" = "0" ]; then + icon_name="/usr/share/icons/breeze-dark/status/24@2x/audio-volume-muted.svg" +notify-vol-send -a "Volume" "$volume"" " -i "$icon_name" -t 2000 -h int:value:"$volume" -h string:synchronous:"─" --replace=555 + else + if [ "$volume" -lt "10" ]; then + icon_name="/usr/share/icons/breeze-dark/status/24@2x/audio-volume-low.svg" +notify-vol-send -a "Volume" "$volume"" " -i "$icon_name" --replace=555 -t 2000 + else + if [ "$volume" -lt "30" ]; then + icon_name="/usr/share/icons/breeze-dark/status/24@2x/audio-volume-low.svg" + else + if [ "$volume" -lt "70" ]; then + icon_name="/usr/share/icons/breeze-dark/status/24@2x/audio-volume-medium.svg" + else + icon_name="/usr/share/icons/breeze-dark/status/24@2x/audio-volume-high.svg" + fi + fi + fi +fi +bar=$(seq -s "─" $(($volume/5)) | sed 's/[0-9]//g') +# Send the notification +notify-vol-send -a "Volume" "$volume"" " -i "$icon_name" -t 2000 -h int:value:"$volume" -h string:synchronous:"$bar" --replace=555 + +} + +case $1 in + up) + # Set the volume on (if it was muted) + pamixer -u > /dev/null + # Up the volume (+ 5%) + pamixer -i 5 > /dev/null + send_notification + ;; + down) + pamixer -u > /dev/null + pamixer -d 5 > /dev/null + send_notification + ;; + mute) + # Toggle mute + pamixer -m > /dev/null + if is_mute ; then + DIR=`dirname "$0"` + notify-vol-send -a "Volume" -i "/usr/share/icons/breeze-dark/status/24@2x/audio-volume-muted.svg" --replace=555 -u normal "Mute" -t 2000 + else + send_notification + fi + ;; +esac