diff --git a/App/PyUI/main-ui/devices/trimui/trim_ui_brick.py b/App/PyUI/main-ui/devices/trimui/trim_ui_brick.py index 649c8549a..d7b49bdfa 100644 --- a/App/PyUI/main-ui/devices/trimui/trim_ui_brick.py +++ b/App/PyUI/main-ui/devices/trimui/trim_ui_brick.py @@ -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 diff --git a/App/fn_editor/com.trimui.quiet.sh b/App/fn_editor/com.trimui.quiet.sh old mode 100644 new mode 100755 index 3265caf9a..efd8645ed --- a/App/fn_editor/com.trimui.quiet.sh +++ b/App/fn_editor/com.trimui.quiet.sh @@ -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 diff --git a/App/fn_editor/com.trimui.silent.sh b/App/fn_editor/com.trimui.silent.sh index 959d56634..2d51df743 100644 --- a/App/fn_editor/com.trimui.silent.sh +++ b/App/fn_editor/com.trimui.silent.sh @@ -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 diff --git a/App/fn_editor/com.trimui.switch.backlight.sh b/App/fn_editor/com.trimui.switch.backlight.sh index 1ccc07283..d5508f734 100644 --- a/App/fn_editor/com.trimui.switch.backlight.sh +++ b/App/fn_editor/com.trimui.switch.backlight.sh @@ -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" diff --git a/spruce/brick/volume_sync_watchdog.sh b/spruce/brick/volume_sync_watchdog.sh new file mode 100755 index 000000000..c0d694b7c --- /dev/null +++ b/spruce/brick/volume_sync_watchdog.sh @@ -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 diff --git a/spruce/scripts/platform/device_functions/trimui_a133p.sh b/spruce/scripts/platform/device_functions/trimui_a133p.sh index 02ea9f521..07b107891 100644 --- a/spruce/scripts/platform/device_functions/trimui_a133p.sh +++ b/spruce/scripts/platform/device_functions/trimui_a133p.sh @@ -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 @@ -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 @@ -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 } diff --git a/spruce/scripts/save_poweroff.sh b/spruce/scripts/save_poweroff.sh index b6f11d8c3..02eecffa4 100644 --- a/spruce/scripts/save_poweroff.sh +++ b/spruce/scripts/save_poweroff.sh @@ -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