Screen lock and power management now native I38 scripts. Removed customizations and added a personal mode template option instead. This means things added by users should not be removed on updates.

This commit is contained in:
Storm Dragon
2025-12-23 18:47:21 -05:00
parent b4578e0035
commit f1a882b8b1
6 changed files with 584 additions and 268 deletions

65
scripts/i38-help-personal.sh Executable file
View File

@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# This file is part of I38.
# I38 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.
# I38 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 I38. If not, see <https://www.gnu.org/licenses/>.
configPath="$(readlink -f "$0")"
configPath="${configPath%/*/*}"
customizationsPath="${configPath}/customizations"
if [[ -f "${configPath}/config" ]]; then
mod="$(grep "set \$mod " "${configPath}/config" | cut -d ' ' -f3)"
mod="${mod//Mod1/Alt}"
mod="${mod//Mod4/Super}"
else
exit 1
fi
if [[ ! -f "$customizationsPath" ]] || ! grep -q '^mode "personal"' "$customizationsPath"; then
message="Personal mode bindings were not found. Check ${customizationsPath}."
echo -e "$message" | yad --text-info --show-cursor --title "I38 - Personal Mode Help" --button "Close:0" --listen
exit 0
fi
personalModeKey="$(grep -m1 -E '^bindsym[[:space:]]+.*mode "personal"' "$customizationsPath" | \
sed -e 's/^bindsym[[:space:]]*//' -e 's/[[:space:]]*mode "personal".*$//')"
if [[ -n "$personalModeKey" ]]; then
personalModeKey="${personalModeKey//\$mod/$mod}"
personalModeKey="${personalModeKey//Mod1/Alt}"
personalModeKey="${personalModeKey//Mod4/Super}"
fi
mapfile helpText < <(sed -n '/^mode "personal"/,/^}$/p' "$customizationsPath" | \
sed -e '/^mode "personal"/d' \
-e '/^}$/d' \
-e 's/bindsym/Key:/g' \
-e 's/Mod1/Alt/g' \
-e 's/, mode "default"//g' \
-e 's/--no-startup-id //g' \
-e "s/\$mod/$mod/g")
for i in "${!helpText[@]}" ; do
helpText[i]="${helpText[i]//${configPath}\/scripts\//}"
helpText[i]="${helpText[i]/.sh/}"
helpText[i]="${helpText[i]/.py/}"
done
header="Personal Mode Keybindings\n\n"
if [[ -n "$personalModeKey" ]]; then
header+="Press ${personalModeKey} to enter personal mode, then use these shortcuts:\n\n"
else
header+="Use these shortcuts while in personal mode:\n\n"
fi
helpText=("$header" "${helpText[@]}" "End of personal mode help. Press Control+Home to jump to the beginning.")
echo -e "${helpText[@]}" | yad --text-info --show-cursor --title "I38 - Personal Mode Help" --button "Close:0" --listen
exit 0

160
scripts/power.sh Executable file
View File

@@ -0,0 +1,160 @@
#!/usr/bin/env bash
# This file is part of I38.
# I38 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.
# I38 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 I38. If not, see <https://www.gnu.org/licenses/>.
if ! command -v yad &> /dev/null; then
echo "yad is required for power options."
exit 1
fi
action="$(yad --title "I38" --text "Power options" --list \
--column "Action" --column "Description" \
"Power off" "Shut down the system" \
"Reboot" "Restart the system" \
"Log out" "Exit the current session" \
--print-column=1 --separator="" --button "Select:0" --button "Cancel:1")"
yadResult=$?
if [[ $yadResult -ne 0 ]]; then
exit 0
fi
case "$action" in
"Power off")
powerAction="poweroff"
login1Method="PowerOff"
consolekitMethod="Stop"
;;
"Reboot")
powerAction="reboot"
login1Method="Reboot"
consolekitMethod="Restart"
;;
"Log out")
powerAction="logout"
;;
*)
exit 0
;;
esac
confirmAction() {
local promptText="$1"
yad --title "I38" --text "$promptText" --button "Yes:0" --button "No:1"
return $?
}
case "$powerAction" in
"poweroff")
confirmAction "Power off the system now?" || exit 0
;;
"reboot")
confirmAction "Reboot the system now?" || exit 0
;;
"logout")
confirmAction "Log out of the current session now?" || exit 0
;;
esac
try_logout() {
if [[ -n "${SWAYSOCK:-}" ]] && command -v swaymsg &> /dev/null; then
swaymsg -t command exit &> /dev/null
return $?
fi
if [[ -n "${I3SOCK:-}" ]] && command -v i3-msg &> /dev/null; then
i3-msg -t command exit &> /dev/null
return $?
fi
if command -v swaymsg &> /dev/null; then
swaymsg -t command exit &> /dev/null
return $?
fi
if command -v i3-msg &> /dev/null; then
i3-msg -t command exit &> /dev/null
return $?
fi
return 1
}
try_loginctl() {
command -v loginctl &> /dev/null || return 1
loginctl "$powerAction" --no-ask-password &> /dev/null
}
try_systemctl() {
command -v systemctl &> /dev/null || return 1
systemctl "$powerAction" --no-ask-password &> /dev/null
}
try_gdbus_login1() {
command -v gdbus &> /dev/null || return 1
gdbus call --system \
--dest org.freedesktop.login1 \
--object-path /org/freedesktop/login1 \
--method "org.freedesktop.login1.Manager.${login1Method}" false \
&> /dev/null
}
try_gdbus_consolekit() {
command -v gdbus &> /dev/null || return 1
gdbus call --system \
--dest org.freedesktop.ConsoleKit \
--object-path /org/freedesktop/ConsoleKit/Manager \
--method "org.freedesktop.ConsoleKit.Manager.${consolekitMethod}" \
&> /dev/null
}
try_dbus_send_login1() {
command -v dbus-send &> /dev/null || return 1
dbus-send --system --print-reply \
--dest=org.freedesktop.login1 \
/org/freedesktop/login1 \
"org.freedesktop.login1.Manager.${login1Method}" \
boolean:false \
&> /dev/null
}
try_dbus_send_consolekit() {
command -v dbus-send &> /dev/null || return 1
dbus-send --system --print-reply \
--dest=org.freedesktop.ConsoleKit \
/org/freedesktop/ConsoleKit/Manager \
"org.freedesktop.ConsoleKit.Manager.${consolekitMethod}" \
&> /dev/null
}
try_shutdown() {
command -v shutdown &> /dev/null || return 1
if [[ "$powerAction" == "poweroff" ]]; then
shutdown -h now &> /dev/null
else
shutdown -r now &> /dev/null
fi
}
try_direct() {
command -v "$powerAction" &> /dev/null || return 1
"$powerAction" &> /dev/null
}
if [[ "$powerAction" == "logout" ]]; then
if try_logout; then
exit 0
fi
else
if try_loginctl || try_systemctl || try_gdbus_login1 || try_dbus_send_login1 || \
try_gdbus_consolekit || try_dbus_send_consolekit || try_shutdown || try_direct; then
exit 0
fi
fi
yad --title "I38" --text "Power action failed. You may need permission or a polkit agent to continue." --button "Close:0"
exit 1

100
scripts/screenlock.sh Executable file
View File

@@ -0,0 +1,100 @@
#!/usr/bin/env bash
# This file is part of I38.
# I38 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.
# I38 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 I38. If not, see <https://www.gnu.org/licenses/>.
screenlockPinHash=""
scriptPath="$(readlink -f "$0")"
scriptDir="${scriptPath%/*}"
i3Path="${scriptDir%/scripts}"
pinFile="${i3Path}/.screenpin"
if [[ -f "$pinFile" ]]; then
read -r screenlockPinHash < "$pinFile"
fi
if [[ -z "$screenlockPinHash" ]]; then
yad --title "I38" --text "Screen lock is not configured. Run i38.sh and set a 4-digit PIN to enable it." --button "Close:0"
exit 1
fi
if ! command -v yad &> /dev/null; then
exit 1
fi
if ! command -v jq &> /dev/null; then
yad --title "I38" --text "Screen lock requires jq to determine the current workspace." --button "Close:0"
exit 1
fi
if ! command -v sha512sum &> /dev/null; then
yad --title "I38" --text "Screen lock requires sha512sum to validate the PIN." --button "Close:0"
exit 1
fi
wmMsg="i3-msg"
if [[ -n "${SWAYSOCK:-}" ]] && command -v swaymsg &> /dev/null; then
wmMsg="swaymsg"
elif [[ -n "${I3SOCK:-}" ]] && command -v i3-msg &> /dev/null; then
wmMsg="i3-msg"
elif command -v swaymsg &> /dev/null; then
wmMsg="swaymsg"
elif command -v i3-msg &> /dev/null; then
wmMsg="i3-msg"
else
yad --title "I38" --text "No i3 or sway command interface was found for screen lock." --button "Close:0"
exit 1
fi
currentWorkspace="$($wmMsg -t get_workspaces | jq -r '.[] | select(.focused==true) | .name')"
lockWorkspace="i38-lock"
if $wmMsg -t get_workspaces | jq -e --arg name "$lockWorkspace" '.[] | select(.name==$name)' &> /dev/null; then
lockWorkspace="i38-lock-$$"
fi
$wmMsg -t command "workspace --no-auto-back-and-forth \"$lockWorkspace\"" &> /dev/null
$wmMsg -t command "mode screenlock" &> /dev/null
attemptCount=0
while : ; do
if [[ $attemptCount -eq 0 ]]; then
promptText="Screen lock is enabled. Enter your 4-digit PIN to unlock."
else
promptText="Incorrect PIN. Enter your 4-digit PIN to unlock."
fi
pinInput="$(yad --entry --hide-text --title "I38" --text "$promptText" --entry-label "Screen lock PIN" --button "Unlock:0" --on-top --sticky --skip-taskbar --fixed --center --undecorated --fullscreen --no-escape)"
yadResult=$?
if [[ $yadResult -ne 0 ]]; then
attemptCount=$((attemptCount + 1))
continue
fi
if [[ ! "$pinInput" =~ ^[0-9]{4}$ ]]; then
attemptCount=$((attemptCount + 1))
continue
fi
pinHash="$(printf "%s" "$pinInput" | sha512sum | awk '{print $1}')"
unset pinInput
if [[ "$pinHash" == "$screenlockPinHash" ]]; then
break
fi
attemptCount=$((attemptCount + 1))
done
$wmMsg -t command "mode default" &> /dev/null
if [[ -n "$currentWorkspace" ]]; then
$wmMsg -t command "workspace \"$currentWorkspace\"" &> /dev/null
fi
exit 0

View File

@@ -71,12 +71,16 @@ def on_mode(self,event):
mode = event.change
if mode == 'ratpoison':
play_sound_async('play -qV0 "|sox -np synth .07 sq 400" "|sox -np synth .5 sq 800" fade h 0 .5 .5 norm -20')
elif mode == 'personal':
play_sound_async('play -nqV0 synth pl E3 pl B3 remix - fade h 0 .25 .2 overdrive riaa norm -12')
elif mode == 'bypass':
play_sound_async('play -nqV0 synth .1 saw 700 saw 1200 delay 0 .04 remix - norm -6')
elif mode == 'default':
# Play different sounds based on which mode we're exiting
if currentMode == 'ratpoison':
play_sound_async('play -qV0 "|sox -np synth .07 sq 400" "|sox -np synth .5 sq 800" fade h 0 .5 .5 norm -20 reverse')
elif currentMode == 'personal':
play_sound_async('play -nqV0 synth pl E3 pl B3 remix - fade h 0 .25 .2 overdrive riaa norm -12 reverse')
elif currentMode == 'panel':
play_sound_async('play -nqV0 synth 0.05 pluck C5 norm -8 : synth 0.05 pluck F4 norm -8 : synth 0.05 pluck C4 norm -8 : synth 0.05 pluck F3 norm -8')
elif currentMode == 'bypass':