added wayshell framework
This commit is contained in:
Executable
+266
@@ -0,0 +1,266 @@
|
||||
#!/bin/bash
|
||||
#===============================================================================
|
||||
# Wayshell Daemon — Module Event Manager
|
||||
#===============================================================================
|
||||
# Description:
|
||||
# Reads module configuration, spawns zone/layout/focused event sources,
|
||||
# and dispatches ON/OFF actions with configurable trailing-edge debounce.
|
||||
#
|
||||
# Dependencies: jq, mmsg
|
||||
# Config: wayshell.conf (zone_buffer, on_delay, off_delay)
|
||||
# Modules: wayshell.modules (name,on_exec,off_exec,type,args...)
|
||||
#===============================================================================
|
||||
|
||||
CONFIG_DIR="${HOME}/.config/sdgos/wayshell"
|
||||
CONFIG_FILE="${CONFIG_DIR}/wayshell.conf"
|
||||
MODULES_FILE="${CONFIG_DIR}/wayshell.modules"
|
||||
MODULE_DIR="${CONFIG_DIR}/modules"
|
||||
|
||||
exec 1>>/tmp/wayshell_daemon.log 2>&1
|
||||
echo "=== Starting Wayshell Daemon ==="
|
||||
|
||||
CLEANED_UP=false
|
||||
cleanup() {
|
||||
$CLEANED_UP && return
|
||||
CLEANED_UP=true
|
||||
echo "Shutting down Wayshell Daemon..."
|
||||
kill -- -$$ 2>/dev/null
|
||||
exit 0
|
||||
}
|
||||
trap cleanup SIGTERM SIGINT EXIT
|
||||
|
||||
# --- Config ---
|
||||
ON_DELAY=$(grep -oP 'on_delay=\K[0-9]+' "$CONFIG_FILE" 2>/dev/null || echo "100")
|
||||
OFF_DELAY=$(grep -oP 'off_delay=\K[0-9]+' "$CONFIG_FILE" 2>/dev/null || echo "100")
|
||||
echo "Config: on_delay=$ON_DELAY, off_delay=$OFF_DELAY"
|
||||
|
||||
# --- Storage ---
|
||||
declare -A MODULES
|
||||
declare -A MODULE_STATES
|
||||
declare -A MODULE_ENTER_TS
|
||||
declare -A MODULE_EXIT_TS
|
||||
|
||||
# --- Module parsing ---
|
||||
parse_modules() {
|
||||
echo "Parsing modules from $MODULES_FILE"
|
||||
[[ -f "$MODULES_FILE" ]] || { echo "ERROR: Modules file not found"; exit 1; }
|
||||
local count=0
|
||||
while IFS=',' read -r name onexec offexec type args; do
|
||||
name="${name// /}"
|
||||
[[ "$name" =~ ^#.*$ || -z "$name" ]] && continue
|
||||
MODULES["$name"]="$onexec,$offexec,$type,$args"
|
||||
MODULE_STATES["$name"]="disabled"
|
||||
((count++))
|
||||
echo " [$count] $name ($type)"
|
||||
done < "$MODULES_FILE"
|
||||
echo "Total modules loaded: $count"
|
||||
}
|
||||
parse_modules
|
||||
|
||||
# --- Filter ---
|
||||
modules_by_type() {
|
||||
local t="$1"
|
||||
for n in "${!MODULES[@]}"; do
|
||||
IFS=',' read -r _ _ mt _ <<< "${MODULES[$n]}"
|
||||
[[ "$mt" == "$t" ]] && echo "$n"
|
||||
done
|
||||
}
|
||||
|
||||
# --- Monitor offset cache ---
|
||||
declare -A MONITOR_OFFSETS
|
||||
MONITOR_CACHE_TS=0
|
||||
get_monitor_offset() {
|
||||
local mon="$1"; local now; now=$(date +%s)
|
||||
if (( now - MONITOR_CACHE_TS > 5 )); then
|
||||
local json entry name ox oy
|
||||
json=$(mmsg get all-monitors 2>/dev/null)
|
||||
if [[ -n "$json" ]]; then
|
||||
while IFS= read -r entry; do
|
||||
name=$(jq -r '.name' <<< "$entry" 2>/dev/null)
|
||||
ox=$(jq -r '.x' <<< "$entry" 2>/dev/null); oy=$(jq -r '.y' <<< "$entry" 2>/dev/null)
|
||||
[[ -n "$name" && "$ox" != "null" ]] && MONITOR_OFFSETS["$name"]="$ox,$oy"
|
||||
done < <(jq -c '.monitors[]' <<< "$json" 2>/dev/null)
|
||||
fi
|
||||
MONITOR_CACHE_TS=$now
|
||||
fi
|
||||
echo "${MONITOR_OFFSETS[$mon]:-0,0}"
|
||||
}
|
||||
|
||||
# --- Check debounce timers and fire actions ---
|
||||
# Called every ~50ms from the main event loop.
|
||||
check_fires() {
|
||||
local now name onexec offexec
|
||||
now=$(date +%s%3N)
|
||||
|
||||
# Pending ON fires
|
||||
for name in "${!MODULE_ENTER_TS[@]}"; do
|
||||
local st=${MODULE_STATES[$name]}
|
||||
[[ "$st" != "pending_on" && "$st" != "pending_off" ]] && continue
|
||||
if (( now - MODULE_ENTER_TS[$name] >= ON_DELAY )); then
|
||||
if [[ "$st" == "pending_on" ]]; then
|
||||
MODULE_STATES["$name"]="enabled"
|
||||
IFS=',' read -r onexec _ _ _ <<< "${MODULES[$name]}"
|
||||
echo "ON $name"
|
||||
eval "$onexec" &
|
||||
fi
|
||||
unset "MODULE_ENTER_TS[$name]"
|
||||
fi
|
||||
done
|
||||
|
||||
# Pending OFF fires
|
||||
for name in "${!MODULE_EXIT_TS[@]}"; do
|
||||
local st=${MODULE_STATES[$name]}
|
||||
[[ "$st" != "pending_off" && "$st" != "enabled" ]] && continue
|
||||
if (( now - MODULE_EXIT_TS[$name] >= OFF_DELAY )); then
|
||||
MODULE_STATES["$name"]="disabled"
|
||||
IFS=',' read -r _ offexec _ _ <<< "${MODULES[$name]}"
|
||||
echo "OFF $name"
|
||||
eval "$offexec" &
|
||||
unset "MODULE_EXIT_TS[$name]"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# --- Process zone events ---
|
||||
process_zone_event() {
|
||||
local data="$1"
|
||||
|
||||
# Exit event — cursor left the edge zone entirely
|
||||
local state
|
||||
state=$(jq -r '.state // "enter"' <<< "$data" 2>/dev/null)
|
||||
if [[ "$state" == "exit" ]]; then
|
||||
local now; now=$(date +%s%3N)
|
||||
while IFS= read -r module_name; do
|
||||
[[ -z "$module_name" ]] && continue
|
||||
local st=${MODULE_STATES[$module_name]}
|
||||
if [[ "$st" == "enabled" || "$st" == "pending_on" ]]; then
|
||||
MODULE_EXIT_TS[$module_name]=$now
|
||||
MODULE_STATES["$module_name"]="pending_off"
|
||||
unset "MODULE_ENTER_TS[$module_name]"
|
||||
fi
|
||||
done < <(modules_by_type "zone")
|
||||
return
|
||||
fi
|
||||
|
||||
# Enter event — check bounding boxes
|
||||
local x y monitor
|
||||
x=$(jq -r '.x' <<< "$data" 2>/dev/null)
|
||||
y=$(jq -r '.y' <<< "$data" 2>/dev/null)
|
||||
monitor=$(jq -r '.monitor' <<< "$data" 2>/dev/null)
|
||||
[[ -z "$x" || -z "$y" || -z "$monitor" ]] && return
|
||||
|
||||
local x_int=${x%.*}
|
||||
local y_int=${y%.*}
|
||||
local offset mx my
|
||||
offset=$(get_monitor_offset "$monitor")
|
||||
mx="${offset%,*}"; my="${offset#*,}"
|
||||
|
||||
local now; now=$(date +%s%3N)
|
||||
|
||||
while IFS= read -r module_name; do
|
||||
[[ -z "$module_name" ]] && continue
|
||||
IFS=',' read -r onexec offexec _ args <<< "${MODULES[$module_name]}"
|
||||
IFS=',' read -r x1 y1 x2 y2 <<< "$args"
|
||||
local ax1=$(( x1 + mx )) ay1=$(( y1 + my ))
|
||||
local ax2=$(( x2 + mx )) ay2=$(( y2 + my ))
|
||||
local in=$(( x_int >= ax1 && x_int <= ax2 && y_int >= ay1 && y_int <= ay2 ? 1 : 0 ))
|
||||
local st=${MODULE_STATES[$module_name]}
|
||||
|
||||
if (( in )); then
|
||||
if [[ "$st" == "disabled" ]]; then
|
||||
MODULE_ENTER_TS[$module_name]=$now
|
||||
MODULE_STATES["$module_name"]="pending_on"
|
||||
elif [[ "$st" == "pending_off" ]]; then
|
||||
# Re-entered before off delay — cancel OFF, stay enabled
|
||||
unset "MODULE_EXIT_TS[$module_name]"
|
||||
MODULE_STATES["$module_name"]="enabled"
|
||||
fi
|
||||
else
|
||||
if [[ "$st" == "enabled" || "$st" == "pending_on" ]]; then
|
||||
MODULE_EXIT_TS[$module_name]=$now
|
||||
MODULE_STATES["$module_name"]="pending_off"
|
||||
unset "MODULE_ENTER_TS[$module_name]"
|
||||
fi
|
||||
fi
|
||||
done < <(modules_by_type "zone")
|
||||
}
|
||||
|
||||
# --- Layout event processing ---
|
||||
process_layout_event() {
|
||||
local event="$1"; local layout state event_monitor
|
||||
layout=$(jq -r '.layout' <<< "$event" 2>/dev/null)
|
||||
state=$(jq -r '.state' <<< "$event" 2>/dev/null)
|
||||
event_monitor=$(jq -r '.monitor // empty' <<< "$event" 2>/dev/null)
|
||||
[[ -z "$layout" || -z "$state" ]] && return
|
||||
local now; now=$(date +%s%3N)
|
||||
while IFS= read -r module_name; do
|
||||
[[ -z "$module_name" ]] && continue
|
||||
IFS=',' read -r onexec offexec _ args <<< "${MODULES[$module_name]}"
|
||||
# args = "layout_name" or "layout_name,monitor_name"
|
||||
local expected_layout="${args%%,*}"
|
||||
local expected_monitor=""
|
||||
[[ "$args" == *","* ]] && expected_monitor="${args#*,}"
|
||||
[[ "$layout" != "$expected_layout" ]] && continue
|
||||
[[ -n "$expected_monitor" && "$event_monitor" != "$expected_monitor" ]] && continue
|
||||
local st=${MODULE_STATES[$module_name]}
|
||||
if [[ "$state" == "active" && "$st" == "disabled" ]]; then
|
||||
MODULE_ENTER_TS[$module_name]=$now
|
||||
MODULE_STATES["$module_name"]="pending_on"
|
||||
elif [[ "$state" == "inactive" && ( "$st" == "enabled" || "$st" == "pending_on" ) ]]; then
|
||||
MODULE_EXIT_TS[$module_name]=$now
|
||||
MODULE_STATES["$module_name"]="pending_off"
|
||||
unset "MODULE_ENTER_TS[$module_name]"
|
||||
fi
|
||||
done < <(modules_by_type "layout")
|
||||
}
|
||||
|
||||
# --- Focused event processing ---
|
||||
process_focused_event() {
|
||||
local event="$1"; local app_id state
|
||||
app_id=$(jq -r '.app_id' <<< "$event" 2>/dev/null)
|
||||
state=$(jq -r '.state' <<< "$event" 2>/dev/null)
|
||||
[[ -z "$app_id" || -z "$state" ]] && return
|
||||
local now; now=$(date +%s%3N)
|
||||
while IFS= read -r module_name; do
|
||||
[[ -z "$module_name" ]] && continue
|
||||
IFS=',' read -r onexec offexec _ expected <<< "${MODULES[$module_name]}"
|
||||
[[ "$app_id" != "$expected" ]] && continue
|
||||
local st=${MODULE_STATES[$module_name]}
|
||||
if [[ "$state" == "focused" && "$st" == "disabled" ]]; then
|
||||
MODULE_ENTER_TS[$module_name]=$now
|
||||
MODULE_STATES["$module_name"]="pending_on"
|
||||
elif [[ "$state" == "unfocused" && ( "$st" == "enabled" || "$st" == "pending_on" ) ]]; then
|
||||
MODULE_EXIT_TS[$module_name]=$now
|
||||
MODULE_STATES["$module_name"]="pending_off"
|
||||
unset "MODULE_ENTER_TS[$module_name]"
|
||||
fi
|
||||
done < <(modules_by_type "focused")
|
||||
}
|
||||
|
||||
# --- Start module subprocesses (auto-restarting) ---
|
||||
echo "Starting modules..."
|
||||
|
||||
(
|
||||
start_src() {
|
||||
local script="$1" label="$2"
|
||||
[[ -x "$script" ]] || { echo "WARNING: $script not found — $label disabled" >&2; return; }
|
||||
(
|
||||
while true; do "$script"; sleep 0.5; done
|
||||
) | while IFS= read -r line; do echo "${label}:$line"; done &
|
||||
}
|
||||
start_src "$MODULE_DIR/zone.sh" "zone"
|
||||
start_src "$MODULE_DIR/layout.sh" "layout"
|
||||
start_src "$MODULE_DIR/focused.sh" "focused"
|
||||
wait
|
||||
) | while true; do
|
||||
if IFS= read -t 0.05 -r line; then
|
||||
source="${line%%:*}"
|
||||
data="${line#*:}"
|
||||
case "$source" in
|
||||
zone) process_zone_event "$data" ;;
|
||||
layout) process_layout_event "$data" ;;
|
||||
focused) process_focused_event "$data" ;;
|
||||
esac
|
||||
fi
|
||||
check_fires
|
||||
done
|
||||
Reference in New Issue
Block a user