Skip to content
Open
8 changes: 7 additions & 1 deletion App/PyUI/main-ui/devices/trimui/trim_ui_brick.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,14 @@ def __init__(self, device_name, main_ui_mode):
self.ensure_wpa_supplicant_conf()
threading.Thread(target=self.monitor_wifi, daemon=True).start()
threading.Thread(target=self.startup_init, daemon=True).start()
# Poll briskly (50ms) so the volume bar tracks the hardware Volume
# keys closely: the firmware updates set_volume on every key event and
# the volume sync watchdog mirrors it into this file immediately, so a
# held key should see the bar chase the firmware OSD rather than trail
# it. A bare stat() per tick is cheap; the granularity-repeat window
# stays ~2s since it counts down in seconds.
self.config_watcher_thread, self.config_watcher_thread_stop_event = FileWatcher().start_file_watcher(
"/mnt/SDCARD/Saves/trim-ui-brick-system.json", self.on_system_config_changed, interval=0.2, repeat_trigger_for_mtime_granularity_issues=True)
"/mnt/SDCARD/Saves/trim-ui-brick-system.json", self.on_system_config_changed, interval=0.05, repeat_trigger_for_mtime_granularity_issues=True)
if(PyUiConfig.enable_button_watchers()):
from controller.controller import Controller
#/dev/miyooio if we want to get rid of miyoo_inputd
Expand Down
32 changes: 28 additions & 4 deletions App/fn_editor/com.trimui.quiet.sh
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
#!/bin/sh
# Quiet Mode: drop the volume to a low level through spruce's own set_volume so
# the in-UI volume reading matches, then restore the previous volume on exit.
#
# The stock script poked an ALSA mixer control directly (tinymix set 9 N), which
# spruce neither reads nor respects, so nothing audible changed and the settings
# slider stayed wrong. Going through set_volume updates SYSTEM_JSON .vol too.

. /mnt/SDCARD/spruce/scripts/helperFunctions.sh

QUIET_VOL=4 # spruce volume scale is 0-20; ~20% is low but audible
SAVED_VOL_FILE=/tmp/system/quiet_saved_vol

echo "============= scene quiet ============"
mkdir -p /tmp/system/

case "$1" in
1 )
1 )
echo "Enter quiet"
tinymix set 9 4
# Remember the current volume so we can restore it on exit
cur="$(get_volume_level)"
[ -z "$cur" ] && cur=0
echo "$cur" > "$SAVED_VOL_FILE"
# Lower spruce's volume (updates SYSTEM_JSON .vol -> UI reflects it).
# Pass "true false" so the config is saved but the stock volume OSD popup
# is not shown; the switch already shows its own toast.
set_volume "$QUIET_VOL" true false
;;
0 )
echo "Exit quiet"
tinymix set 9 1
;;
# Restore the volume we had before entering quiet (no OSD popup)
saved="$(cat "$SAVED_VOL_FILE" 2>/dev/null)"
[ -z "$saved" ] && saved=0
set_volume "$saved" true false
rm -f "$SAVED_VOL_FILE"
;;
*)
;;
esac
34 changes: 30 additions & 4 deletions App/fn_editor/com.trimui.silent.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,43 @@
#!/bin/sh
# Silent Mode: hard-mute the speaker and reflect it in spruce's volume value so
# the in-UI volume reading matches. On exit, restore the volume that was set
# before entering silent.
#
# Without the set_volume calls the speaker mutes but spruce's stored vol (and the
# settings slider) stays at its old value, so the UI looks wrong while muted.

. /mnt/SDCARD/spruce/scripts/helperFunctions.sh

SAVED_VOL_FILE=/tmp/system/silent_saved_vol

echo "============= scene silent ============"
mkdir -p /tmp/system/

case "$1" in
1 )
1 )
echo "Enter silent"
echo 1 > /sys/class/speaker/mute
# Remember the current volume so we can restore it on exit
cur="$(get_volume_level)"
[ -z "$cur" ] && cur=0
echo "$cur" > "$SAVED_VOL_FILE"
# Drop spruce's volume to 0 (updates SYSTEM_JSON .vol -> UI reflects it).
# Pass "true false" so the config is saved but the stock volume OSD popup
# is not shown; the switch already shows its own toast.
set_volume 0 true false
# Belt and suspenders: cut the speaker amp outright
echo 1 > /sys/class/speaker/mute
touch /tmp/system/muted
;;
0 )
echo "Exit silent"
echo 0 > /sys/class/speaker/mute
echo 0 > /sys/class/speaker/mute
rm -f /tmp/system/muted
;;
# Restore the volume we had before entering silent (no OSD popup)
saved="$(cat "$SAVED_VOL_FILE" 2>/dev/null)"
[ -z "$saved" ] && saved=0
set_volume "$saved" true false
rm -f "$SAVED_VOL_FILE"
;;
*)
;;
esac
30 changes: 18 additions & 12 deletions App/fn_editor/com.trimui.switch.backlight.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
#!/bin/sh
LCD_BL=`/usr/trimui/bin/systemval brightness`
LCD_BL_SET=$LCD_BL
# Cycle the LCD backlight through a few levels on each Fn key press.
# Uses spruce's own set_backlight so the hardware, the stored value, and the
# in-UI brightness slider all stay in sync (the stock /tmp/system/set_brightness
# path is not honored under spruce and desyncs the slider).

echo "get brightness:"$LCD_BL
. /mnt/SDCARD/spruce/scripts/helperFunctions.sh

if test $LCD_BL -lt 1; then
LCD_BL_SET=2
elif test $LCD_BL -lt 3; then
LCD_BL_SET=5
elif test $LCD_BL -lt 7; then
LCD_BL_SET=10
CURRENT=$(current_backlight)
echo "get brightness:$CURRENT"

# Cycle 1 -> 4 -> 7 -> 10 -> 1 (spruce clamps to 1..10)
if [ "$CURRENT" -lt 4 ]; then
NEXT=4
elif [ "$CURRENT" -lt 7 ]; then
NEXT=7
elif [ "$CURRENT" -lt 10 ]; then
NEXT=10
else
LCD_BL_SET=0
NEXT=1
fi

echo "set brightness:"$LCD_BL_SET
echo -n $LCD_BL_SET > /tmp/system/set_brightness
echo "set brightness:$NEXT"
set_backlight "$NEXT"
105 changes: 105 additions & 0 deletions spruce/brick/volume_sync_watchdog.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/bin/sh
# Mirror the firmware volume value into spruce's stored volume so the in-UI
# volume bar tracks the hardware Volume +/- keys, including while held.
#
# On the Brick the physical volume keys are handled by the stock firmware
# (trimui_inputd/hardwareservice), which owns /tmp/system/set_volume: it writes
# the new level there on every key event (autorepeat included) and applies it to
# ALSA and to its own config (/mnt/UDISK/system.json). spruce never read that
# file back, so its own .vol (SYSTEM_JSON) only changed when spruce itself set
# the volume. The UI volume bar reads .vol, so holding a volume key moved the
# audio but not the bar (it updated once at most, never ramped).
#
# This watchdog reacts to writes of /tmp/system/set_volume and mirrors the value
# into SYSTEM_JSON, the file PyUI's config watcher polls to redraw the bar. That
# is the watchdog's whole job: the durable copy and ALSA are the firmware's, so
# we deliberately do NOT call set_volume here (it would just re-poke the
# firmware's command file and add a redundant synchronous flash write). Keeping
# to a single in-place edit of SYSTEM_JSON is what lets the bar chase the OSD
# instead of crawling behind it while a key is held.
#
# Events are delivered by inotifywait so there is no fixed poll delay; if it is
# missing or exits we fall back to interval polling so syncing still works.
#
# It also lifts Silent Mode's hard mute on the first volume-up. Silent Mode mutes
# the speaker amp (/sys/class/speaker/mute) and that mute is otherwise only
# cleared by flipping the switch back. So raising the volume bumped ALSA and the
# OSD but produced no sound. When a non-zero volume arrives while the amp is
# muted we clear the mute (and its marker) so volume-up restores audio without
# touching the switch.

. /mnt/SDCARD/spruce/scripts/helperFunctions.sh

SET_VOLUME_DIR=/tmp/system
SET_VOLUME_FILE=/tmp/system/set_volume
SPEAKER_MUTE_FILE=/sys/class/speaker/mute
MUTED_MARKER=/tmp/system/muted
INOTIFYWAIT=/mnt/SDCARD/spruce/bin64/inotifywait
POLL_INTERVAL=0.15 # fallback polling cadence when inotifywait is unavailable

mkdir -p "$SET_VOLUME_DIR" 2>/dev/null

last_seen=""

# Lift Silent Mode's amp mute when the volume is raised above 0.
unmute_if_raised() {
vol="$1"
[ "$vol" -gt 0 ] 2>/dev/null || return 0
[ -w "$SPEAKER_MUTE_FILE" ] || return 0
[ "$(cat "$SPEAKER_MUTE_FILE" 2>/dev/null)" = "1" ] || return 0
echo 0 > "$SPEAKER_MUTE_FILE"
rm -f "$MUTED_MARKER"
log_message "volume_sync_watchdog.sh: volume raised to $vol, cleared Silent Mode mute."
}

read_current() {
new_vol=$(cat "$SET_VOLUME_FILE" 2>/dev/null)
case "$new_vol" in
''|*[!0-9]*) return 1 ;; # ignore empty or non-numeric writes
esac
echo "$new_vol"
}

# Mirror one observed value into the bar (SYSTEM_JSON), in place, and handle the
# Silent Mode unmute. Cheap enough to run on every key event, even held.
sync_value() {
new_vol="$1"
[ "$new_vol" = "$last_seen" ] && return 0
last_seen="$new_vol"
if [ "$new_vol" != "$(get_volume_level)" ]; then
sed -i "s/\"vol\":[[:space:]]*[0-9]*/\"vol\": $new_vol/" "$SYSTEM_JSON" 2>/dev/null
fi
unmute_if_raised "$new_vol"
}

run_event_driven() {
# -m: stream events; watch the dir (catches in-place echo and atomic mv of
# the file). Filter to our filename below. Each event triggers a read of the
# current value, so a burst of writes coalesces to the latest level.
"$INOTIFYWAIT" -m -q -e modify -e close_write -e moved_to \
--format '%f' "$SET_VOLUME_DIR" 2>/dev/null | \
while read -r fname; do
[ "$fname" = "set_volume" ] || continue
cur="$(read_current)" || continue
sync_value "$cur"
done
}

run_polling_fallback() {
log_message "volume_sync_watchdog.sh: inotifywait unavailable, using polling fallback."
while true; do
if [ -f "$SET_VOLUME_FILE" ]; then
cur="$(read_current)" && sync_value "$cur"
fi
sleep "$POLL_INTERVAL"
done
}

log_message "volume_sync_watchdog.sh: Started up."

if [ -x "$INOTIFYWAIT" ]; then
# If inotifywait ever exits, drop back to polling so syncing never stops.
run_event_driven
log_message "volume_sync_watchdog.sh: event loop ended, falling back to polling."
fi
run_polling_fallback
15 changes: 14 additions & 1 deletion spruce/scripts/platform/device_functions/trimui_a133p.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ get_current_volume() {
set_volume() {
new_vol="${1:-0}" # default to mute if no value supplied
SAVE_TO_CONFIG="${2:-true}" # Optional 2nd arg, defaults to true
SHOW_OSD="${3:-true}" # Optional 3rd arg, defaults to true
mkdir -p /tmp/system 2>/dev/null
echo "$new_vol" > /tmp/system/set_volume 2>/dev/null
if [ "$SAVE_TO_CONFIG" = true ]; then
Expand All @@ -80,7 +81,7 @@ set_volume() {
if [ "$current_volume" -ne "$new_vol" ]; then
save_volume_to_config_file "$new_vol"
sed "s/\"vol\":[[:space:]]*[0-9]\+/\"vol\": $new_vol/" /mnt/UDISK/system.json > /mnt/UDISK/system.json.tmp && mv /mnt/UDISK/system.json.tmp /mnt/UDISK/system.json
if ! pgrep MainUI >/dev/null; then
if ! pgrep MainUI >/dev/null && [ "$SHOW_OSD" = true ]; then
/usr/trimui/osd/show_volume_msg.sh "$new_vol" &
fi
fi
Expand Down Expand Up @@ -176,6 +177,18 @@ device_init_a133p() {
run_trimui_blobs "trimui_inputd trimui_scened trimui_btmanager hardwareservice musicserver"
}

launch_startup_watchdogs() {
launch_common_startup_watchdogs_v2

# Keep spruce's stored volume in sync with the firmware volume keys so the
# in-UI volume bar tracks the hardware Volume +/- keys (including on hold).
# Launched here (not in device_init) so it lives alongside the other durable
# watchdogs and survives the early-boot churn; pinned like its siblings.
/mnt/SDCARD/spruce/brick/volume_sync_watchdog.sh &
SYSTEM_CPU=${DEVICE_MAX_CORES_ONLINE%"${DEVICE_MAX_CORES_ONLINE#?}"}
pin_cpu "$SYSTEM_CPU" -n volume_sync_watchdog.sh &
}

set_event_arg_for_idlemon() {
log_message "nothing to do" -v
}
Expand Down
1 change: 1 addition & 0 deletions spruce/scripts/save_poweroff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ stop_problematic_scripts() {
killall -q -9 idlemon_mm.sh
killall -q -9 low_power_warning.sh
killall -q -9 theme_watchdog.sh
killall -q -9 volume_sync_watchdog.sh
killall -q -9 inotifywait
killall -q -9 inotifywatch
killall -q -9 getevent
Expand Down