Fix mobile data static IP bearer, call recording, earpiece audio, and ringtone/vibration
This commit is contained in:
@@ -30,5 +30,12 @@ start() {
|
|||||||
if [ -n "$QMICLI_MODEM" ]; then
|
if [ -n "$QMICLI_MODEM" ]; then
|
||||||
$QMICLI_MODEM --dpm-open-port=hw-data-ep-type=bam-dmux,hw-data-ep-iface-number=1,hw-data-rx-id=1,hw-data-tx-id=1 >/dev/null 2>&1 || true
|
$QMICLI_MODEM --dpm-open-port=hw-data-ep-type=bam-dmux,hw-data-ep-iface-number=1,hw-data-rx-id=1,hw-data-tx-id=1 >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
for f in /etc/NetworkManager/system-connections/*.nmconnection; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
if grep -q "^type=gsm" "$f" 2>/dev/null; then
|
||||||
|
sed -i '/^\[ipv6\]/,/^\[/ s/^method=auto$/method=ignore/' "$f" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
eend 0
|
eend 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
SXMO_RINGTIME=20
|
||||||
|
SXMO_RINGNUMBER=10
|
||||||
|
SXMO_VIBRATE_STRENGTH=2
|
||||||
|
SXMO_RINGVOL=100
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/usr/share/sxmo/ringtones/Banana_Chime.wav
|
||||||
@@ -2,6 +2,8 @@
|
|||||||
# SPDX-License-Identifier: AGPL-3.0-only
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
# Copyright 2022 Sxmo Contributors
|
# Copyright 2022 Sxmo Contributors
|
||||||
|
|
||||||
|
export PATH="${XDG_CONFIG_HOME:-${HOME:-/home/user}/.config}/sxmo/hooks:/usr/share/sxmo/default_hooks:$PATH"
|
||||||
|
|
||||||
# include common definitions
|
# include common definitions
|
||||||
# shellcheck source=configs/default_hooks/sxmo_hook_icons.sh
|
# shellcheck source=configs/default_hooks/sxmo_hook_icons.sh
|
||||||
. sxmo_hook_icons.sh
|
. sxmo_hook_icons.sh
|
||||||
@@ -107,9 +109,10 @@ checkforfinishedcalls() {
|
|||||||
# If it was the last call
|
# If it was the last call
|
||||||
if ! sxmo_modemcall.sh list_active_calls | grep -q .; then
|
if ! sxmo_modemcall.sh list_active_calls | grep -q .; then
|
||||||
# Cleanup
|
# Cleanup
|
||||||
|
sxmo_jobs.sh stop ringing
|
||||||
rm -f "$XDG_RUNTIME_DIR/sxmo_calls/"* 2>/dev/null || true
|
rm -f "$XDG_RUNTIME_DIR/sxmo_calls/"* 2>/dev/null || true
|
||||||
if [ "$STATE" != muted ]; then
|
if [ "$STATE" != muted ] && [ ! -f "$XDG_CONFIG_HOME/sxmo/.novibrate" ]; then
|
||||||
sxmo_vibrate 1000 "${SXMO_VIBRATE_STRENGTH:-1}" &
|
sxmo_vibrate 600 &
|
||||||
fi
|
fi
|
||||||
sxmo_jobs.sh stop incall_menu
|
sxmo_jobs.sh stop incall_menu
|
||||||
sxmo_jobs.sh stop proximity_lock
|
sxmo_jobs.sh stop proximity_lock
|
||||||
|
|||||||
@@ -6,19 +6,26 @@ export PATH="${XDG_CONFIG_HOME:-$HOME/.config}/sxmo/hooks:/usr/share/sxmo/defaul
|
|||||||
. sxmo_common.sh
|
. sxmo_common.sh
|
||||||
|
|
||||||
enable_call_audio_mode() {
|
enable_call_audio_mode() {
|
||||||
|
pactl set-card-profile alsa_card.comprC0 off >/dev/null 2>&1 || true
|
||||||
|
pactl set-card-profile alsa_card.platform-7702000.sound "HiFi (Earpiece, Mic1)" >/dev/null 2>&1 || true
|
||||||
|
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1 || true
|
||||||
|
wpctl set-volume @DEFAULT_AUDIO_SINK@ 100% >/dev/null 2>&1 || true
|
||||||
if command -v amixer >/dev/null 2>&1; then
|
if command -v amixer >/dev/null 2>&1; then
|
||||||
amixer -c 0 sset 'SPK DAC Switch' off >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='SPK DAC Switch'" 0 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RX3 MIX1 INP1' 'ZERO' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX3 MIX1 INP1'" 'ZERO' >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'PRI_MI2S_RX Voice Mixer VoiceMMode1' on >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX3 Digital Volume'" 0 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'VoiceMMode1 Capture Mixer TERT_MI2S_TX' on >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='PRI_MI2S_RX Voice Mixer VoiceMMode1'" 1 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'MultiMedia2 Mixer TERT_MI2S_TX' on >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='VoiceMMode1 Capture Mixer TERT_MI2S_TX'" 1 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RX1 MIX1 INP1' 'RX1' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='MultiMedia2 Mixer TERT_MI2S_TX'" 0 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RDAC2 MUX' 'RX1' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX1 MIX1 INP1'" 'RX1' >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'EAR_S' 'Switch' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RDAC2 MUX'" 'RX1' >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'CIC1 MUX' 'AMIC' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='EAR_S'" 'Switch' >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'DEC1 MUX' 'ADC1' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX1 Mute Switch'" 0 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'ADC1 Volume' 6 >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX1 Digital Volume'" 88 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RX1 Digital Volume' 84 >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='CIC1 MUX'" 'AMIC' >/dev/null 2>&1 || true
|
||||||
|
amixer -c 0 cset "name='DEC1 MUX'" 'ADC1' >/dev/null 2>&1 || true
|
||||||
|
amixer -c 0 cset "name='ADC1 Volume'" 8 >/dev/null 2>&1 || true
|
||||||
|
amixer -c 0 cset "name='TX1 Digital Volume'" 88 >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
sxmo_log "Enabled ALSA voice call audio mode (Earpiece)."
|
sxmo_log "Enabled ALSA voice call audio mode (Earpiece)."
|
||||||
sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true
|
sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true
|
||||||
@@ -38,18 +45,21 @@ disable_call_audio_mode() {
|
|||||||
pactl set-card-profile alsa_card.platform-7702000.sound "HiFi (Mic1, Speaker)" >/dev/null 2>&1 || true
|
pactl set-card-profile alsa_card.platform-7702000.sound "HiFi (Mic1, Speaker)" >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
if command -v amixer >/dev/null 2>&1; then
|
if command -v amixer >/dev/null 2>&1; then
|
||||||
amixer -c 0 sset 'PRI_MI2S_RX Voice Mixer VoiceMMode1' off >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='PRI_MI2S_RX Voice Mixer VoiceMMode1'" 0 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'VoiceMMode1 Capture Mixer TERT_MI2S_TX' off >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='VoiceMMode1 Capture Mixer TERT_MI2S_TX'" 0 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'EAR_S' 'ZERO' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='MultiMedia2 Mixer TERT_MI2S_TX'" 1 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RX1 MIX1 INP1' 'ZERO' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='EAR_S'" 'ZERO' >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RX3 MIX1 INP1' 'RX1' >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX1 MIX1 INP1'" 'ZERO' >/dev/null 2>&1 || true
|
||||||
|
amixer -c 0 cset "name='RX1 Digital Volume'" 84 >/dev/null 2>&1 || true
|
||||||
|
amixer -c 0 cset "name='TX1 Digital Volume'" 84 >/dev/null 2>&1 || true
|
||||||
|
amixer -c 0 cset "name='RX3 MIX1 INP1'" 'RX1' >/dev/null 2>&1 || true
|
||||||
if [ "$spk_vol" -gt 0 ]; then
|
if [ "$spk_vol" -gt 0 ]; then
|
||||||
rx3_hw=$(( 60 + spk_vol * 32 / 100 ))
|
rx3_hw=$(( 60 + spk_vol * 32 / 100 ))
|
||||||
amixer -c 0 sset 'SPK DAC Switch' on >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='SPK DAC Switch'" 1 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RX3 Digital Volume' "$rx3_hw" >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX3 Digital Volume'" "$rx3_hw" >/dev/null 2>&1 || true
|
||||||
else
|
else
|
||||||
amixer -c 0 sset 'SPK DAC Switch' off >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='SPK DAC Switch'" 0 >/dev/null 2>&1 || true
|
||||||
amixer -c 0 sset 'RX3 Digital Volume' 0 >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX3 Digital Volume'" 0 >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
wpctl set-mute @DEFAULT_AUDIO_SINK@ "$([ "$spk_vol" -le 0 ] && echo 1 || echo 0)" >/dev/null 2>&1 || true
|
wpctl set-mute @DEFAULT_AUDIO_SINK@ "$([ "$spk_vol" -le 0 ] && echo 1 || echo 0)" >/dev/null 2>&1 || true
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,27 @@
|
|||||||
|
SectionDevice."Earpiece" {
|
||||||
|
Comment "Earpiece playback"
|
||||||
|
|
||||||
|
ConflictingDevice [
|
||||||
|
"Headphones"
|
||||||
|
]
|
||||||
|
|
||||||
|
EnableSequence [
|
||||||
|
cset "name='RX1 MIX1 INP1' RX1"
|
||||||
|
cset "name='RDAC2 MUX' RX1"
|
||||||
|
cset "name='EAR_S' 1"
|
||||||
|
## gain to +6dB
|
||||||
|
cset "name='RX1 Digital Volume' 90"
|
||||||
|
]
|
||||||
|
|
||||||
|
DisableSequence [
|
||||||
|
cset "name='RX1 Digital Volume' 0"
|
||||||
|
cset "name='EAR_S' 0"
|
||||||
|
cset "name='RX1 MIX1 INP1' ZERO"
|
||||||
|
]
|
||||||
|
|
||||||
|
Value {
|
||||||
|
PlaybackPCM "${var:WcdPlaybackPCM}"
|
||||||
|
PlaybackChannels 2
|
||||||
|
PlaybackPriority 100
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,13 +4,14 @@
|
|||||||
# Copyright 2022 Sxmo Contributors
|
# Copyright 2022 Sxmo Contributors
|
||||||
|
|
||||||
# This script is executed (asynchronously) when you get an incoming call
|
# This script is executed (asynchronously) when you get an incoming call
|
||||||
# You can use it to play a ring tone
|
|
||||||
|
|
||||||
# $1 = Contact Name or Number (if not in contacts)
|
# $1 = Contact Name or Number (if not in contacts)
|
||||||
|
|
||||||
# shellcheck source=scripts/core/sxmo_common.sh
|
# shellcheck source=scripts/core/sxmo_common.sh
|
||||||
. sxmo_common.sh
|
. sxmo_common.sh
|
||||||
|
|
||||||
|
export XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-${HOME:-/home/user}/.config}"
|
||||||
|
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"
|
||||||
|
|
||||||
if [ -z "${SXMO_VIBRATE_DEV:-}" ] && [ ! -e /dev/input/by-path/platform-vibrator-event ]; then
|
if [ -z "${SXMO_VIBRATE_DEV:-}" ] && [ ! -e /dev/input/by-path/platform-vibrator-event ]; then
|
||||||
for vdev in /dev/input/by-path/*vibrator*-event; do
|
for vdev in /dev/input/by-path/*vibrator*-event; do
|
||||||
if [ -e "$vdev" ]; then
|
if [ -e "$vdev" ]; then
|
||||||
@@ -25,33 +26,61 @@ if [ -f "$XDG_CONFIG_HOME/sxmo/ringsettings" ]; then
|
|||||||
. "$XDG_CONFIG_HOME/sxmo/ringsettings"
|
. "$XDG_CONFIG_HOME/sxmo/ringsettings"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# pm8xxx-vib shifts FF_RUMBLE strong_magnitude right by 8 bits (valid 256..65535)
|
case "${SXMO_VIBRATE_STRENGTH:-2}" in
|
||||||
case "${SXMO_VIBRATE_STRENGTH:-}" in
|
1) SXMO_VIBRATE_STRENGTH=1 ;;
|
||||||
1) SXMO_VIBRATE_STRENGTH=8000 ;;
|
3) SXMO_VIBRATE_STRENGTH=3 ;;
|
||||||
2|"") SXMO_VIBRATE_STRENGTH=32000 ;;
|
*)
|
||||||
3) SXMO_VIBRATE_STRENGTH=65535 ;;
|
if [ "${SXMO_VIBRATE_STRENGTH:-2}" -gt 45000 ] 2>/dev/null; then
|
||||||
|
SXMO_VIBRATE_STRENGTH=3
|
||||||
|
elif [ "${SXMO_VIBRATE_STRENGTH:-2}" -gt 3 ] && [ "${SXMO_VIBRATE_STRENGTH:-2}" -le 16000 ] 2>/dev/null; then
|
||||||
|
SXMO_VIBRATE_STRENGTH=1
|
||||||
|
else
|
||||||
|
SXMO_VIBRATE_STRENGTH=2
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
export SXMO_VIBRATE_STRENGTH
|
||||||
|
|
||||||
if [ -f "$XDG_CONFIG_HOME/sxmo/ringtone" ]; then
|
if [ -f "$XDG_CONFIG_HOME/sxmo/ringtone" ]; then
|
||||||
custom_ring="$(head -n 1 "$XDG_CONFIG_HOME/sxmo/ringtone" 2>/dev/null)"
|
custom_ring="$(head -n 1 "$XDG_CONFIG_HOME/sxmo/ringtone" 2>/dev/null | tr -d '\r')"
|
||||||
if [ -n "$custom_ring" ] && [ -f "$custom_ring" ]; then
|
if [ -n "$custom_ring" ] && [ -f "$custom_ring" ]; then
|
||||||
SXMO_RINGTONE="$custom_ring"
|
SXMO_RINGTONE="$custom_ring"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Only vibrate if you already got an active call
|
if [ -z "${SXMO_RINGTONE:-}" ] || [ ! -f "${SXMO_RINGTONE:-}" ]; then
|
||||||
if sxmo_modemcall.sh list_active_calls \
|
if [ -f /usr/share/sxmo/ringtones/Banana_Chime.wav ]; then
|
||||||
| grep -v ringing-in \
|
SXMO_RINGTONE="/usr/share/sxmo/ringtones/Banana_Chime.wav"
|
||||||
| grep -q .; then
|
elif [ -f /usr/share/sxmo/ring.ogg ]; then
|
||||||
sxmo_vibrate 1500 "${SXMO_VIBRATE_STRENGTH:-1}"
|
SXMO_RINGTONE="/usr/share/sxmo/ring.ogg"
|
||||||
exit
|
else
|
||||||
|
for f in /usr/share/sxmo/ringtones/*; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
SXMO_RINGTONE="$f"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
export SXMO_RINGTONE
|
||||||
|
|
||||||
|
# DND / BUSY MODE
|
||||||
|
if [ -f "$XDG_RUNTIME_DIR/sxmo.busy" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ -f "$XDG_CONFIG_HOME/sxmo/.busy" ]; then
|
||||||
|
end="$(cat "$XDG_CONFIG_HOME/sxmo/.busy" 2>/dev/null)"
|
||||||
|
if [ -z "$end" ] || [ "$(date +%s)" -lt "$end" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Shallow if you have more than one ringing call
|
# Only vibrate once if a call is already active
|
||||||
if ! sxmo_modemcall.sh list_active_calls \
|
if [ "$(cat /tmp/sxmo_active_call 2>/dev/null)" = "2" ]; then
|
||||||
| grep -c ringing-in \
|
if [ ! -f "$XDG_CONFIG_HOME/sxmo/.novibrate" ]; then
|
||||||
| grep -q 1; then
|
sxmo_vibrate 1500 "$SXMO_VIBRATE_STRENGTH"
|
||||||
exit
|
fi
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "${SXMO_RINGVOL:-100}" in
|
case "${SXMO_RINGVOL:-100}" in
|
||||||
@@ -59,90 +88,167 @@ case "${SXMO_RINGVOL:-100}" in
|
|||||||
esac
|
esac
|
||||||
[ "$SXMO_RINGVOL" -gt 100 ] && SXMO_RINGVOL=100
|
[ "$SXMO_RINGVOL" -gt 100 ] && SXMO_RINGVOL=100
|
||||||
|
|
||||||
SAVED_RX3="$(amixer -c 0 cget "name='RX3 Digital Volume'" 2>/dev/null | awk -F= '/: values=/ {print $2; exit}')"
|
SAVED_SPK_VOL=85
|
||||||
SAVED_SPK_VOL=100
|
|
||||||
if [ -r "$XDG_CONFIG_HOME/sxmo/speaker_vol" ]; then
|
if [ -r "$XDG_CONFIG_HOME/sxmo/speaker_vol" ]; then
|
||||||
read -r SAVED_SPK_VOL < "$XDG_CONFIG_HOME/sxmo/speaker_vol" 2>/dev/null || SAVED_SPK_VOL=100
|
read -r SAVED_SPK_VOL < "$XDG_CONFIG_HOME/sxmo/speaker_vol" 2>/dev/null || SAVED_SPK_VOL=85
|
||||||
fi
|
fi
|
||||||
case "$SAVED_SPK_VOL" in ''|*[!0-9]*) SAVED_SPK_VOL=100 ;; esac
|
case "$SAVED_SPK_VOL" in ''|*[!0-9]*) SAVED_SPK_VOL=85 ;; esac
|
||||||
|
SAVED_RX3=$(( 60 + SAVED_SPK_VOL * 32 / 100 ))
|
||||||
|
|
||||||
|
RING_PID=""
|
||||||
|
VIB_PID=""
|
||||||
|
|
||||||
|
is_call_picked_up() {
|
||||||
|
[ "$(cat /tmp/sxmo_active_call 2>/dev/null)" = "2" ] && return 0
|
||||||
|
for f in "$XDG_RUNTIME_DIR"/sxmo_calls/*.pickedupcall "$XDG_RUNTIME_DIR"/sxmo_calls/*.initiatedcall; do
|
||||||
|
[ -e "$f" ] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
finish() {
|
finish() {
|
||||||
trap - INT TERM EXIT
|
trap - INT TERM EXIT
|
||||||
jobs -p | xargs -r kill 2>/dev/null || true
|
[ -n "$RING_PID" ] && pkill -TERM -P "$RING_PID" 2>/dev/null || true
|
||||||
wait 2>/dev/null || true
|
[ -n "$VIB_PID" ] && pkill -TERM -P "$VIB_PID" 2>/dev/null || true
|
||||||
if [ -n "${SAVED_RX3:-}" ]; then
|
[ -n "$RING_PID" ] && kill -TERM "$RING_PID" 2>/dev/null || true
|
||||||
|
[ -n "$VIB_PID" ] && kill -TERM "$VIB_PID" 2>/dev/null || true
|
||||||
|
pkill -TERM -P "$$" 2>/dev/null || true
|
||||||
|
if ! is_call_picked_up; then
|
||||||
amixer -c 0 cset "name='RX3 Digital Volume'" "$SAVED_RX3" >/dev/null 2>&1 || true
|
amixer -c 0 cset "name='RX3 Digital Volume'" "$SAVED_RX3" >/dev/null 2>&1 || true
|
||||||
|
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${SAVED_SPK_VOL}%" >/dev/null 2>&1 || true
|
||||||
fi
|
fi
|
||||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${SAVED_SPK_VOL}%" >/dev/null 2>&1 || true
|
exit 0
|
||||||
exit
|
}
|
||||||
|
|
||||||
|
pause_media_async() {
|
||||||
|
if [ -p /tmp/dwm_plugin_music.fifo ]; then
|
||||||
|
echo "PAUSE" > /tmp/dwm_plugin_music.fifo 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
mpc -q pause >/dev/null 2>&1 &
|
||||||
|
sxmo_playerctl.sh pause_all >/dev/null 2>&1 &
|
||||||
}
|
}
|
||||||
|
|
||||||
ring() {
|
ring() {
|
||||||
if [ "${SXMO_RINGVOL:-100}" -le 0 ]; then
|
if [ "${SXMO_RINGVOL:-100}" -le 0 ] || [ -z "${SXMO_RINGTONE:-}" ] || [ ! -f "$SXMO_RINGTONE" ]; then
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
ring_rx3=$(( 60 + SXMO_RINGVOL * 34 / 100 ))
|
ring_rx3=$(( 60 + SXMO_RINGVOL * 34 / 100 ))
|
||||||
mpv_vol=$(( SXMO_RINGVOL * 130 / 100 ))
|
pw_vol="$(awk -v v="$SXMO_RINGVOL" 'BEGIN { printf "%.2f", v / 100.0 }')"
|
||||||
amixer -c 0 cset "name='SPK DAC Switch'" 1 >/dev/null 2>&1 || true
|
|
||||||
amixer -c 0 cset "name='RX3 MIX1 INP1'" 3 >/dev/null 2>&1 || true
|
(
|
||||||
amixer -c 0 cset "name='RX3 Digital Volume'" "$ring_rx3" >/dev/null 2>&1 || true
|
rchild=""
|
||||||
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1 || true
|
vhelper=""
|
||||||
wpctl set-volume @DEFAULT_AUDIO_SINK@ 100% >/dev/null 2>&1 || true
|
trap '[ -n "$rchild" ] && kill -TERM "$rchild" 2>/dev/null; [ -n "$vhelper" ] && kill -TERM "$vhelper" 2>/dev/null; exit 0' INT TERM EXIT
|
||||||
mpv --no-resume-playback --quiet --no-video \
|
|
||||||
--ao=pipewire,pulse,alsa --audio-channels=stereo --audio-samplerate=48000 \
|
amixer -c 0 -s >/dev/null 2>&1 <<EOF || true
|
||||||
--volume-max=150 --volume="$mpv_vol" \
|
cset "name='EAR_S'" 0
|
||||||
--loop="${SXMO_RINGNUMBER:-10}" "$SXMO_RINGTONE" >/dev/null &
|
cset "name='RX1 MIX1 INP1'" 0
|
||||||
|
cset "name='PRI_MI2S_RX Audio Mixer MultiMedia1'" 1
|
||||||
|
cset "name='SPK DAC Switch'" 1
|
||||||
|
cset "name='RX3 MIX1 INP1'" 3
|
||||||
|
cset "name='RX3 Digital Volume'" $ring_rx3
|
||||||
|
EOF
|
||||||
|
wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1 || true
|
||||||
|
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${SXMO_RINGVOL}%" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
(
|
||||||
|
for d in 0.15 0.40; do
|
||||||
|
sleep "$d" || exit 0
|
||||||
|
is_call_picked_up && exit 0
|
||||||
|
amixer -c 0 -s >/dev/null 2>&1 <<EOF || true
|
||||||
|
cset "name='SPK DAC Switch'" 1
|
||||||
|
cset "name='RX3 MIX1 INP1'" 3
|
||||||
|
cset "name='RX3 Digital Volume'" $ring_rx3
|
||||||
|
EOF
|
||||||
|
wpctl set-volume @DEFAULT_AUDIO_SINK@ "${SXMO_RINGVOL}%" >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
vhelper="$!"
|
||||||
|
|
||||||
|
ext="$(printf '%s' "${SXMO_RINGTONE##*.}" | tr '[:upper:]' '[:lower:]')"
|
||||||
|
use_pw=0
|
||||||
|
case "$ext" in
|
||||||
|
wav|ogg|oga|mp3|flac|aiff|aif)
|
||||||
|
command -v pw-play >/dev/null 2>&1 && use_pw=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
loops="${SXMO_RINGNUMBER:-10}"
|
||||||
|
if [ "$use_pw" -eq 1 ]; then
|
||||||
|
i=0
|
||||||
|
while [ "$i" -lt "$loops" ]; do
|
||||||
|
is_call_picked_up && break
|
||||||
|
pw-play --volume="$pw_vol" "$SXMO_RINGTONE" >/dev/null 2>&1 &
|
||||||
|
rchild="$!"
|
||||||
|
wait "$rchild" 2>/dev/null
|
||||||
|
rc="$?"
|
||||||
|
rchild=""
|
||||||
|
if [ "$rc" -ne 0 ]; then
|
||||||
|
use_pw=0
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
i=$(( i + 1 ))
|
||||||
|
sleep 0.2 &
|
||||||
|
rchild="$!"
|
||||||
|
wait "$rchild" 2>/dev/null || break
|
||||||
|
rchild=""
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$use_pw" -eq 0 ] && ! is_call_picked_up; then
|
||||||
|
mpv --no-config --no-terminal --no-resume-playback --quiet --vo=null --no-video \
|
||||||
|
--ao=pipewire,pulse,alsa --audio-channels=stereo --audio-samplerate=48000 \
|
||||||
|
--volume-max=150 --volume="$SXMO_RINGVOL" \
|
||||||
|
--loop="$loops" "$SXMO_RINGTONE" </dev/null >/dev/null 2>&1 &
|
||||||
|
rchild="$!"
|
||||||
|
wait "$rchild" 2>/dev/null || true
|
||||||
|
rchild=""
|
||||||
|
fi
|
||||||
|
) &
|
||||||
|
RING_PID="$!"
|
||||||
}
|
}
|
||||||
|
|
||||||
vibrate() {
|
vibrate() {
|
||||||
while : ; do
|
(
|
||||||
trap 'finish' INT TERM EXIT
|
vchild=""
|
||||||
sxmo_vibrate 1500 "${SXMO_VIBRATE_STRENGTH:-1}" &
|
trap '[ -n "$vchild" ] && kill -TERM "$vchild" 2>/dev/null; exit 0' INT TERM EXIT
|
||||||
wait "$!"
|
while : ; do
|
||||||
|
is_call_picked_up && exit 0
|
||||||
|
sxmo_vibrate 1500 "$SXMO_VIBRATE_STRENGTH" &
|
||||||
|
vchild="$!"
|
||||||
|
wait "$vchild" 2>/dev/null || exit 0
|
||||||
|
vchild=""
|
||||||
|
|
||||||
sleep 0.5 &
|
sleep 0.5 &
|
||||||
wait "$!"
|
vchild="$!"
|
||||||
done &
|
wait "$vchild" 2>/dev/null || exit 0
|
||||||
|
vchild=""
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
VIB_PID="$!"
|
||||||
}
|
}
|
||||||
|
|
||||||
# SILENT MODE
|
trap 'finish' INT TERM EXIT
|
||||||
if [ -f "$XDG_CONFIG_HOME"/sxmo/.busy ]; then
|
|
||||||
end="$(cat "$XDG_CONFIG_HOME"/sxmo/.busy)"
|
|
||||||
if [ -z "$end" ] || [ "$(date +%s)" -lt "$end" ]; then
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# RING & VIBRATE MODE (DEFAULT)
|
# RING & VIBRATE MODE (DEFAULT)
|
||||||
if [ ! -f "$XDG_CONFIG_HOME"/sxmo/.noring ] && [ ! -f "$XDG_CONFIG_HOME"/sxmo/.novibrate ]; then
|
if [ ! -f "$XDG_CONFIG_HOME/sxmo/.noring" ] && [ ! -f "$XDG_CONFIG_HOME/sxmo/.novibrate" ]; then
|
||||||
sxmo_log "RING AND VIBRATE"
|
sxmo_log "RING AND VIBRATE ($SXMO_RINGTONE, vol=${SXMO_RINGVOL}%, vib=${SXMO_VIBRATE_STRENGTH})"
|
||||||
|
pause_media_async
|
||||||
sxmo_playerctl.sh pause_all
|
|
||||||
if [ -p /tmp/dwm_plugin_music.fifo ]; then
|
|
||||||
echo "PAUSE" > /tmp/dwm_plugin_music.fifo 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
ring
|
|
||||||
vibrate
|
vibrate
|
||||||
|
ring
|
||||||
|
|
||||||
# RING-ONLY MODE
|
# RING-ONLY MODE
|
||||||
elif [ ! -f "$XDG_CONFIG_HOME"/sxmo/.noring ] && [ -f "$XDG_CONFIG_HOME"/sxmo/.novibrate ]; then
|
elif [ ! -f "$XDG_CONFIG_HOME/sxmo/.noring" ] && [ -f "$XDG_CONFIG_HOME/sxmo/.novibrate" ]; then
|
||||||
sxmo_log "RING ONLY"
|
sxmo_log "RING ONLY ($SXMO_RINGTONE, vol=${SXMO_RINGVOL}%)"
|
||||||
|
pause_media_async
|
||||||
sxmo_playerctl.sh pause_all
|
|
||||||
if [ -p /tmp/dwm_plugin_music.fifo ]; then
|
|
||||||
echo "PAUSE" > /tmp/dwm_plugin_music.fifo 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
ring
|
ring
|
||||||
|
|
||||||
# VIBRATE-ONLY MODE
|
# VIBRATE-ONLY MODE
|
||||||
elif [ ! -f "$XDG_CONFIG_HOME"/sxmo/.novibrate ] && [ -f "$XDG_CONFIG_HOME"/sxmo/.noring ]; then
|
elif [ ! -f "$XDG_CONFIG_HOME/sxmo/.novibrate" ] && [ -f "$XDG_CONFIG_HOME/sxmo/.noring" ]; then
|
||||||
sxmo_log "VIBRATE ONLY"
|
sxmo_log "VIBRATE ONLY (vib=${SXMO_VIBRATE_STRENGTH})"
|
||||||
|
|
||||||
vibrate
|
vibrate
|
||||||
fi
|
fi
|
||||||
|
|
||||||
trap 'finish' INT TERM EXIT
|
|
||||||
sleep "${SXMO_RINGTIME:-20}" &
|
sleep "${SXMO_RINGTIME:-20}" &
|
||||||
wait "$!"
|
wait "$!"
|
||||||
|
finish
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -192,10 +192,12 @@ std::string sanitizeForFilename(const std::string& raw) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out.empty()) {
|
if (out.empty()) {
|
||||||
return "unknown";
|
return "voice";
|
||||||
}
|
}
|
||||||
if (out.size() > 20) {
|
if (out.size() > 10 && out[0] == '+') {
|
||||||
out = out.substr(0, 20);
|
out = out.substr(out.size() - 10);
|
||||||
|
} else if (out.size() > 10) {
|
||||||
|
out = out.substr(0, 10);
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
@@ -217,7 +219,7 @@ void finalizeWavHeader(const std::string& path) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t hdr[64] = {0};
|
uint8_t hdr[64] = {0};
|
||||||
ssize_t n = :: pread(fd, hdr, sizeof(hdr), 0);
|
ssize_t n = ::pread(fd, hdr, sizeof(hdr), 0);
|
||||||
if (n < 44 || std::memcmp(hdr, "RIFF", 4) != 0 ||
|
if (n < 44 || std::memcmp(hdr, "RIFF", 4) != 0 ||
|
||||||
std::memcmp(hdr + 8, "WAVE", 4) != 0) {
|
std::memcmp(hdr + 8, "WAVE", 4) != 0) {
|
||||||
::close(fd);
|
::close(fd);
|
||||||
@@ -237,6 +239,12 @@ void finalizeWavHeader(const std::string& path) {
|
|||||||
};
|
};
|
||||||
(void)::pwrite(fd, b, 4, 4);
|
(void)::pwrite(fd, b, 4, 4);
|
||||||
}
|
}
|
||||||
|
ssize_t dataOff = -1;
|
||||||
|
uint32_t dataBytes = 0;
|
||||||
|
uint16_t audioFmt = static_cast<uint16_t>(hdr[20]) |
|
||||||
|
(static_cast<uint16_t>(hdr[21]) << 8);
|
||||||
|
uint16_t bitsPerSample = static_cast<uint16_t>(hdr[34]) |
|
||||||
|
(static_cast<uint16_t>(hdr[35]) << 8);
|
||||||
for (ssize_t off = 12; off + 8 <= n;) {
|
for (ssize_t off = 12; off + 8 <= n;) {
|
||||||
uint32_t chunkLen = static_cast<uint32_t>(hdr[off + 4]) |
|
uint32_t chunkLen = static_cast<uint32_t>(hdr[off + 4]) |
|
||||||
(static_cast<uint32_t>(hdr[off + 5]) << 8) |
|
(static_cast<uint32_t>(hdr[off + 5]) << 8) |
|
||||||
@@ -244,6 +252,8 @@ void finalizeWavHeader(const std::string& path) {
|
|||||||
(static_cast<uint32_t>(hdr[off + 7]) << 24);
|
(static_cast<uint32_t>(hdr[off + 7]) << 24);
|
||||||
if (std::memcmp(hdr + off, "data", 4) == 0) {
|
if (std::memcmp(hdr + off, "data", 4) == 0) {
|
||||||
uint32_t expectedData = static_cast<uint32_t>(st.st_size - (off + 8));
|
uint32_t expectedData = static_cast<uint32_t>(st.st_size - (off + 8));
|
||||||
|
dataOff = off + 8;
|
||||||
|
dataBytes = expectedData;
|
||||||
if (chunkLen == 0 || chunkLen != expectedData) {
|
if (chunkLen == 0 || chunkLen != expectedData) {
|
||||||
uint8_t b[4] = {
|
uint8_t b[4] = {
|
||||||
static_cast<uint8_t>(expectedData & 0xff),
|
static_cast<uint8_t>(expectedData & 0xff),
|
||||||
@@ -258,6 +268,33 @@ void finalizeWavHeader(const std::string& path) {
|
|||||||
if (chunkLen == 0) break;
|
if (chunkLen == 0) break;
|
||||||
off += 8 + static_cast<ssize_t>((chunkLen + 1) & ~1u);
|
off += 8 + static_cast<ssize_t>((chunkLen + 1) & ~1u);
|
||||||
}
|
}
|
||||||
|
// Peak-normalize quiet 16-bit signed PCM recordings so playback is clearly audible
|
||||||
|
if (audioFmt == 1 && bitsPerSample == 16 && dataOff >= 44 &&
|
||||||
|
dataBytes >= 2 && dataBytes <= 32 * 1024 * 1024) {
|
||||||
|
size_t numSamples = dataBytes / 2;
|
||||||
|
std::vector<int16_t> pcm(numSamples);
|
||||||
|
ssize_t rd = ::pread(fd, pcm.data(), numSamples * sizeof(int16_t), dataOff);
|
||||||
|
if (rd == static_cast<ssize_t>(numSamples * sizeof(int16_t))) {
|
||||||
|
int maxAbs = 0;
|
||||||
|
size_t skip = std::min<size_t>(numSamples / 4, 400);
|
||||||
|
for (size_t i = skip; i < numSamples; ++i) {
|
||||||
|
int v = std::abs(static_cast<int>(pcm[i]));
|
||||||
|
if (v > maxAbs) {
|
||||||
|
maxAbs = v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (maxAbs >= 32 && maxAbs < 18000) {
|
||||||
|
double gain = std::min(24.0, 24000.0 / static_cast<double>(maxAbs));
|
||||||
|
if (gain > 1.15) {
|
||||||
|
for (size_t i = 0; i < numSamples; ++i) {
|
||||||
|
int scaled = static_cast<int>(std::lround(pcm[i] * gain));
|
||||||
|
pcm[i] = static_cast<int16_t>(std::clamp(scaled, -32767, 32767));
|
||||||
|
}
|
||||||
|
(void)::pwrite(fd, pcm.data(), numSamples * sizeof(int16_t), dataOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
::fsync(fd);
|
::fsync(fd);
|
||||||
::close(fd);
|
::close(fd);
|
||||||
}
|
}
|
||||||
@@ -434,7 +471,7 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
bool ringMuted = false;
|
bool ringMuted = false;
|
||||||
bool micMuted = false;
|
bool micMuted = false;
|
||||||
bool speakerOn = false;
|
bool speakerOn = false;
|
||||||
int callVolumePct = 80;
|
int callVolumePct = 85;
|
||||||
int selectedRow = 0;
|
int selectedRow = 0;
|
||||||
|
|
||||||
int64_t callStartMs = 0;
|
int64_t callStartMs = 0;
|
||||||
@@ -446,6 +483,8 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
int64_t recordStartMs = 0;
|
int64_t recordStartMs = 0;
|
||||||
std::string recordFilePath;
|
std::string recordFilePath;
|
||||||
std::string recordFileName;
|
std::string recordFileName;
|
||||||
|
int lastRecordedSec = 0;
|
||||||
|
int recordedClipCount = 0;
|
||||||
|
|
||||||
bool visible = false;
|
bool visible = false;
|
||||||
int64_t openStartMs = 0;
|
int64_t openStartMs = 0;
|
||||||
@@ -454,6 +493,22 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
int softkeyPending = 0;
|
int softkeyPending = 0;
|
||||||
int64_t softkeyDeadlineMs = 0;
|
int64_t softkeyDeadlineMs = 0;
|
||||||
|
|
||||||
|
auto pctToHwVol = [](int pct, bool spk) -> long {
|
||||||
|
int c = std::clamp(pct, 5, 100);
|
||||||
|
// RX1/RX3 Digital Volume: 84 = 0 dB, 88 = +4 dB, 92 = +8 dB, 94 = +10 dB
|
||||||
|
return spk ? (68 + (c * 26) / 100) : (68 + (c * 24) / 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto hwVolToPct = [](long hwVol, bool spk) -> int {
|
||||||
|
if (hwVol < 68) {
|
||||||
|
return 85;
|
||||||
|
}
|
||||||
|
int span = spk ? 26 : 24;
|
||||||
|
int pct = static_cast<int>(std::lround(((hwVol - 68) * 100.0) / span));
|
||||||
|
pct = std::clamp(pct, 5, 100);
|
||||||
|
return std::max(5, ((pct + 2) / 5) * 5);
|
||||||
|
};
|
||||||
|
|
||||||
auto refreshHardwareAudioState = [&]() {
|
auto refreshHardwareAudioState = [&]() {
|
||||||
unsigned int dec1 = alsa.getEnum("DEC1 MUX", 1);
|
unsigned int dec1 = alsa.getEnum("DEC1 MUX", 1);
|
||||||
if (dec1 > 0) {
|
if (dec1 > 0) {
|
||||||
@@ -464,27 +519,18 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
savedMicMux = static_cast<unsigned int>(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 1)));
|
savedMicMux = static_cast<unsigned int>(std::max(1, readIntFile("/tmp/xdrawer_mic_mux", 1)));
|
||||||
micMuted = true;
|
micMuted = true;
|
||||||
}
|
}
|
||||||
bool spkSwitch = alsa.getBoolean("SPK DAC Switch", false);
|
int savedVol = readIntFile("/tmp/sxmo_call_vol", -1);
|
||||||
bool earSwitch = (alsa.getEnum("EAR_S", 1) != 0);
|
if (savedVol >= 5 && savedVol <= 100) {
|
||||||
speakerOn = (spkSwitch && !earSwitch);
|
callVolumePct = savedVol;
|
||||||
|
|
||||||
long rawVol = speakerOn ? alsa.getInteger("RX3 Digital Volume", 68)
|
|
||||||
: alsa.getInteger("RX1 Digital Volume", 68);
|
|
||||||
if (rawVol <= 0) {
|
|
||||||
rawVol = alsa.getInteger("RX1 Digital Volume", 68);
|
|
||||||
}
|
|
||||||
callVolumePct = std::clamp(static_cast<int>(std::lround((rawVol * 100.0) / 84.0)), 0, 100);
|
|
||||||
callVolumePct = (callVolumePct / 5) * 5;
|
|
||||||
if (callVolumePct == 0) {
|
|
||||||
callVolumePct = 75;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto setMicMuted = [&](bool mute) {
|
auto setMicMuted = [&](bool mute) {
|
||||||
micMuted = mute;
|
micMuted = mute;
|
||||||
alsa.setEnum("DEC1 MUX", mute ? 0 : (savedMicMux > 0 ? savedMicMux : 1));
|
alsa.setEnum("DEC1 MUX", mute ? 0 : (savedMicMux > 0 ? savedMicMux : 1));
|
||||||
alsa.setInteger("ADC1 Volume", mute ? 0 : 6);
|
alsa.setInteger("ADC1 Volume", mute ? 0 : 8);
|
||||||
alsa.setInteger("ADC3 Volume", mute ? 0 : 6);
|
alsa.setInteger("ADC3 Volume", mute ? 0 : 8);
|
||||||
|
alsa.setInteger("TX1 Digital Volume", mute ? 0 : 88);
|
||||||
alsa.setBoolean("VoiceMMode1 Capture Mixer TERT_MI2S_TX", !mute);
|
alsa.setBoolean("VoiceMMode1 Capture Mixer TERT_MI2S_TX", !mute);
|
||||||
runAsyncCmd(mute ? "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 1 >/dev/null 2>&1"
|
runAsyncCmd(mute ? "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 1 >/dev/null 2>&1"
|
||||||
: "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 0 >/dev/null 2>&1");
|
: "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ 0 >/dev/null 2>&1");
|
||||||
@@ -494,7 +540,15 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
|
|
||||||
auto setSpeakerRoute = [&](bool toSpeaker) {
|
auto setSpeakerRoute = [&](bool toSpeaker) {
|
||||||
speakerOn = toSpeaker;
|
speakerOn = toSpeaker;
|
||||||
long hwVol = std::lround((callVolumePct * 84.0) / 100.0);
|
long hwVol = pctToHwVol(callVolumePct, toSpeaker);
|
||||||
|
alsa.setBoolean("PRI_MI2S_RX Voice Mixer VoiceMMode1", true);
|
||||||
|
alsa.setBoolean("VoiceMMode1 Capture Mixer TERT_MI2S_TX", !micMuted);
|
||||||
|
alsa.setEnum("CIC1 MUX", 0);
|
||||||
|
if (!micMuted) {
|
||||||
|
alsa.setEnum("DEC1 MUX", savedMicMux > 0 ? savedMicMux : 1);
|
||||||
|
alsa.setInteger("ADC1 Volume", 8);
|
||||||
|
alsa.setInteger("TX1 Digital Volume", 88);
|
||||||
|
}
|
||||||
if (toSpeaker) {
|
if (toSpeaker) {
|
||||||
alsa.setEnum("EAR_S", 0);
|
alsa.setEnum("EAR_S", 0);
|
||||||
alsa.setEnum("RX1 MIX1 INP1", 0);
|
alsa.setEnum("RX1 MIX1 INP1", 0);
|
||||||
@@ -505,9 +559,11 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
} else {
|
} else {
|
||||||
alsa.setBoolean("SPK DAC Switch", false);
|
alsa.setBoolean("SPK DAC Switch", false);
|
||||||
alsa.setEnum("RX3 MIX1 INP1", 0);
|
alsa.setEnum("RX3 MIX1 INP1", 0);
|
||||||
|
alsa.setInteger("RX3 Digital Volume", 0);
|
||||||
alsa.setEnum("RX1 MIX1 INP1", 3);
|
alsa.setEnum("RX1 MIX1 INP1", 3);
|
||||||
alsa.setEnum("RDAC2 MUX", 0);
|
alsa.setEnum("RDAC2 MUX", 0);
|
||||||
alsa.setEnum("EAR_S", 1);
|
alsa.setEnum("EAR_S", 1);
|
||||||
|
alsa.setBoolean("RX1 Mute Switch", false);
|
||||||
alsa.setInteger("RX1 Digital Volume", hwVol);
|
alsa.setInteger("RX1 Digital Volume", hwVol);
|
||||||
statusNotice = "Audio routed to Earpiece";
|
statusNotice = "Audio routed to Earpiece";
|
||||||
}
|
}
|
||||||
@@ -516,10 +572,10 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
|
|
||||||
auto adjustVolume = [&](int deltaPct) {
|
auto adjustVolume = [&](int deltaPct) {
|
||||||
callVolumePct = std::clamp(callVolumePct + deltaPct, 5, 100);
|
callVolumePct = std::clamp(callVolumePct + deltaPct, 5, 100);
|
||||||
long hwVol = std::lround((callVolumePct * 84.0) / 100.0);
|
writeIntFile("/tmp/sxmo_call_vol", callVolumePct);
|
||||||
alsa.setInteger("RX1 Digital Volume", hwVol);
|
alsa.setInteger("RX1 Digital Volume", pctToHwVol(callVolumePct, false));
|
||||||
if (speakerOn) {
|
if (speakerOn) {
|
||||||
alsa.setInteger("RX3 Digital Volume", hwVol);
|
alsa.setInteger("RX3 Digital Volume", pctToHwVol(callVolumePct, true));
|
||||||
}
|
}
|
||||||
char cmd[128];
|
char cmd[128];
|
||||||
std::snprintf(cmd, sizeof(cmd),
|
std::snprintf(cmd, sizeof(cmd),
|
||||||
@@ -528,6 +584,24 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
runAsyncCmd(cmd);
|
runAsyncCmd(cmd);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto finishRecordingFile = [&]() {
|
||||||
|
alsa.setBoolean("MultiMedia2 Mixer TERT_MI2S_TX", false);
|
||||||
|
finalizeWavHeader(recordFilePath);
|
||||||
|
struct stat st {};
|
||||||
|
if (!recordFilePath.empty() && ::stat(recordFilePath.c_str(), &st) == 0 &&
|
||||||
|
st.st_size > 44) {
|
||||||
|
int durFromBytes = static_cast<int>((st.st_size - 44 + 8000) / 16000);
|
||||||
|
int durFromWall = static_cast<int>((nowMs() - recordStartMs + 500) / 1000);
|
||||||
|
lastRecordedSec = std::max(1, std::max(durFromBytes, durFromWall));
|
||||||
|
++recordedClipCount;
|
||||||
|
statusNotice = "SAVED (" + formatTimerSec(lastRecordedSec) + ") " + recordFileName;
|
||||||
|
statusNoticeUntilMs = nowMs() + 8000;
|
||||||
|
} else if (!recordFileName.empty()) {
|
||||||
|
statusNotice = "Recording failed";
|
||||||
|
statusNoticeUntilMs = nowMs() + 4000;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
auto checkRecordingAlive = [&]() -> bool {
|
auto checkRecordingAlive = [&]() -> bool {
|
||||||
if (recordPid <= 0) {
|
if (recordPid <= 0) {
|
||||||
return false;
|
return false;
|
||||||
@@ -536,7 +610,7 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
pid_t r = ::waitpid(recordPid, &status, WNOHANG);
|
pid_t r = ::waitpid(recordPid, &status, WNOHANG);
|
||||||
if (r == recordPid || (r < 0 && ::kill(recordPid, 0) != 0)) {
|
if (r == recordPid || (r < 0 && ::kill(recordPid, 0) != 0)) {
|
||||||
recordPid = -1;
|
recordPid = -1;
|
||||||
finalizeWavHeader(recordFilePath);
|
finishRecordingFile();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -565,11 +639,7 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
recordPid = -1;
|
recordPid = -1;
|
||||||
}
|
}
|
||||||
finalizeWavHeader(recordFilePath);
|
finishRecordingFile();
|
||||||
if (!recordFileName.empty()) {
|
|
||||||
statusNotice = "Saved: " + recordFileName;
|
|
||||||
statusNoticeUntilMs = nowMs() + 3500;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
auto startRecording = [&]() -> bool {
|
auto startRecording = [&]() -> bool {
|
||||||
@@ -581,18 +651,19 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
struct tm tmNow {};
|
struct tm tmNow {};
|
||||||
localtime_r(&nowSec, &tmNow);
|
localtime_r(&nowSec, &tmNow);
|
||||||
char tsBuf[32];
|
char tsBuf[32];
|
||||||
std::strftime(tsBuf, sizeof(tsBuf), "%Y%m%d_%H%M%S", &tmNow);
|
std::strftime(tsBuf, sizeof(tsBuf), "%H%M%S", &tmNow);
|
||||||
|
|
||||||
std::string tag = sanitizeForFilename(
|
std::string tag = sanitizeForFilename(
|
||||||
!contactName.empty() ? contactName : (!callNumber.empty() ? callNumber : "voice"));
|
!contactName.empty() ? contactName : (!callNumber.empty() ? callNumber : "voice"));
|
||||||
recordFileName = "call_" + tag + "_" + tsBuf + ".wav";
|
recordFileName = std::string("rec_") + tsBuf + "_" + tag + ".wav";
|
||||||
recordFilePath = dir + "/" + recordFileName;
|
recordFilePath = dir + "/" + recordFileName;
|
||||||
|
|
||||||
// Ensure MultiMedia2 capture mixer and primary mic path are active for PipeWire recording
|
// Enable MultiMedia2 capture mixer at 8 kHz mono to match active VoiceMMode1 AFE port
|
||||||
alsa.setEnum("CIC1 MUX", 0);
|
alsa.setEnum("CIC1 MUX", 0);
|
||||||
if (!micMuted) {
|
if (!micMuted) {
|
||||||
alsa.setEnum("DEC1 MUX", savedMicMux > 0 ? savedMicMux : 1);
|
alsa.setEnum("DEC1 MUX", savedMicMux > 0 ? savedMicMux : 1);
|
||||||
alsa.setInteger("ADC1 Volume", 6);
|
alsa.setInteger("ADC1 Volume", 8);
|
||||||
|
alsa.setInteger("TX1 Digital Volume", 88);
|
||||||
}
|
}
|
||||||
alsa.setBoolean("MultiMedia2 Mixer TERT_MI2S_TX", true);
|
alsa.setBoolean("MultiMedia2 Mixer TERT_MI2S_TX", true);
|
||||||
|
|
||||||
@@ -602,12 +673,6 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
::setsid();
|
::setsid();
|
||||||
std::string runtimeDir = "/run/user/" + std::to_string(::getuid());
|
|
||||||
const char* xdg = std::getenv("XDG_RUNTIME_DIR");
|
|
||||||
struct stat stRuntime {};
|
|
||||||
if (!xdg || !*xdg || ::stat(xdg, &stRuntime) != 0) {
|
|
||||||
::setenv("XDG_RUNTIME_DIR", runtimeDir.c_str(), 1);
|
|
||||||
}
|
|
||||||
int nullfd = ::open("/dev/null", O_RDWR);
|
int nullfd = ::open("/dev/null", O_RDWR);
|
||||||
if (nullfd >= 0) {
|
if (nullfd >= 0) {
|
||||||
::dup2(nullfd, STDIN_FILENO);
|
::dup2(nullfd, STDIN_FILENO);
|
||||||
@@ -617,19 +682,17 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
::close(nullfd);
|
::close(nullfd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
execlp("pw-record", "pw-record", "--format", "s16", "--rate", "16000",
|
execlp("arecord", "arecord", "-q", "-D", "hw:0,1", "-f", "S16_LE",
|
||||||
"--channels", "1", recordFilePath.c_str(),
|
"-r", "8000", "-c", "1", recordFilePath.c_str(),
|
||||||
static_cast<char*>(nullptr));
|
static_cast<char*>(nullptr));
|
||||||
execlp("parecord", "parecord", "--rate=16000", "--channels=1",
|
execlp("arecord", "arecord", "-q", "-f", "S16_LE", "-r", "8000", "-c", "1",
|
||||||
"--file-format=wav", recordFilePath.c_str(), static_cast<char*>(nullptr));
|
|
||||||
execlp("arecord", "arecord", "-f", "S16_LE", "-r", "16000", "-c", "1",
|
|
||||||
recordFilePath.c_str(), static_cast<char*>(nullptr));
|
recordFilePath.c_str(), static_cast<char*>(nullptr));
|
||||||
_exit(1);
|
_exit(1);
|
||||||
}
|
}
|
||||||
recordPid = pid;
|
recordPid = pid;
|
||||||
recordStartMs = nowMs();
|
recordStartMs = nowMs();
|
||||||
statusNotice = "Recording to " + recordFileName;
|
statusNotice = "REC -> " + recordFileName;
|
||||||
statusNoticeUntilMs = nowMs() + 2500;
|
statusNoticeUntilMs = nowMs() + 3000;
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -804,9 +867,17 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
drawPango(layoutSmall, &colLight, 8, 88, 26,
|
drawPango(layoutSmall, &colLight, 8, 88, 26,
|
||||||
"[Call] Answer [*] Mute [End] Reject", 224);
|
"[Call] Answer [*] Mute [End] Reject", 224);
|
||||||
} else if (callState == CallState::Ended) {
|
} else if (callState == CallState::Ended) {
|
||||||
drawPango(layoutSmall, &colMuted, 8, 88, 26,
|
if (recordedClipCount > 0 && !recordFileName.empty()) {
|
||||||
recordFileName.empty() ? "Call finished" : ("Saved: " + recordFileName),
|
std::string savedStr = "SAVED (" + formatTimerSec(lastRecordedSec) + ") " +
|
||||||
224);
|
recordFileName;
|
||||||
|
drawPango(layoutSmall, &colGreen, 8, 88, 26, savedStr, 224);
|
||||||
|
} else {
|
||||||
|
drawPango(layoutSmall, &colMuted, 8, 88, 26, "Call finished", 224);
|
||||||
|
}
|
||||||
|
} else if (recordedClipCount > 0 && !recordFileName.empty()) {
|
||||||
|
std::string savedStr = "SAVED (" + formatTimerSec(lastRecordedSec) + ") " +
|
||||||
|
recordFileName;
|
||||||
|
drawPango(layoutSmall, &colGreen, 8, 88, 26, savedStr, 224);
|
||||||
} else {
|
} else {
|
||||||
drawPango(layoutSmall, &colMuted, 8, 88, 26,
|
drawPango(layoutSmall, &colMuted, 8, 88, 26,
|
||||||
"[1] Mic [2] Rec [3] Route [0] DTMF", 224);
|
"[1] Mic [2] Rec [3] Route [0] DTMF", 224);
|
||||||
@@ -883,16 +954,21 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
"[1] Microphone", std::max(80, kWinWidth - vw - 48));
|
"[1] Microphone", std::max(80, kWinWidth - vw - 48));
|
||||||
drawPango(layoutMain, icCol, kWinWidth - vw - 8, ry, rowH, valStr);
|
drawPango(layoutMain, icCol, kWinWidth - vw - 8, ry, rowH, valStr);
|
||||||
} else if (i == 1) {
|
} else if (i == 1) {
|
||||||
XftColor* icCol = isRec ? &colRed : &colMuted;
|
XftColor* icCol = isRec ? &colRed
|
||||||
std::string valStr = isRec
|
: (recordedClipCount > 0 ? &colGreen : &colMuted);
|
||||||
? ("REC " + formatTimerSec(static_cast<int>((now - recordStartMs) / 1000)))
|
std::string valStr;
|
||||||
: "OFF";
|
if (isRec) {
|
||||||
|
valStr = "REC " + formatTimerSec(static_cast<int>((now - recordStartMs) / 1000));
|
||||||
|
} else if (recordedClipCount > 0) {
|
||||||
|
valStr = "SAVED " + formatTimerSec(lastRecordedSec);
|
||||||
|
} else {
|
||||||
|
valStr = "OFF";
|
||||||
|
}
|
||||||
int vw = pangoWidth(layoutMain, valStr);
|
int vw = pangoWidth(layoutMain, valStr);
|
||||||
drawPango(layoutIcon, icCol, 10, ry, rowH, kIconRecord);
|
drawPango(layoutIcon, icCol, 10, ry, rowH, kIconRecord);
|
||||||
drawPango(layoutMain, isSel ? &colWhite : &colLight, 34, ry, rowH,
|
drawPango(layoutMain, isSel ? &colWhite : &colLight, 34, ry, rowH,
|
||||||
"[2] Record", std::max(80, kWinWidth - vw - 44));
|
"[2] Record", std::max(72, kWinWidth - vw - 44));
|
||||||
drawPango(layoutMain, isRec ? &colRed : &colMuted,
|
drawPango(layoutMain, icCol, kWinWidth - vw - 8, ry, rowH, valStr);
|
||||||
kWinWidth - vw - 8, ry, rowH, valStr);
|
|
||||||
} else if (i == 2) {
|
} else if (i == 2) {
|
||||||
const char* ic = speakerOn ? kIconSpeaker : kIconEarpiece;
|
const char* ic = speakerOn ? kIconSpeaker : kIconEarpiece;
|
||||||
XftColor* icCol = speakerOn ? &colYellow : &colBlue;
|
XftColor* icCol = speakerOn ? &colYellow : &colBlue;
|
||||||
@@ -1242,9 +1318,13 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (foundState == "active" && callState != CallState::Active) {
|
if (foundState == "active" && callState != CallState::Active) {
|
||||||
|
if (callState != CallState::Outgoing) {
|
||||||
|
speakerOn = false;
|
||||||
|
}
|
||||||
callState = CallState::Active;
|
callState = CallState::Active;
|
||||||
callStartMs = nowMs();
|
callStartMs = nowMs();
|
||||||
ringMuted = false;
|
ringMuted = false;
|
||||||
|
setSpeakerRoute(speakerOn);
|
||||||
writeActiveStateFiles();
|
writeActiveStateFiles();
|
||||||
render();
|
render();
|
||||||
} else if (foundState == "ringing-in" && callState == CallState::Idle) {
|
} else if (foundState == "ringing-in" && callState == CallState::Idle) {
|
||||||
@@ -1359,10 +1439,15 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
callId.clear();
|
callId.clear();
|
||||||
callNumber.clear();
|
callNumber.clear();
|
||||||
contactName.clear();
|
contactName.clear();
|
||||||
|
recordFileName.clear();
|
||||||
|
recordFilePath.clear();
|
||||||
|
lastRecordedSec = 0;
|
||||||
|
recordedClipCount = 0;
|
||||||
updateCallerDetails(arg1, arg2);
|
updateCallerDetails(arg1, arg2);
|
||||||
callState = CallState::Incoming;
|
callState = CallState::Incoming;
|
||||||
callStartMs = nowMs();
|
callStartMs = nowMs();
|
||||||
ringMuted = false;
|
ringMuted = false;
|
||||||
|
speakerOn = false;
|
||||||
dtmfMode = false;
|
dtmfMode = false;
|
||||||
dtmfSent.clear();
|
dtmfSent.clear();
|
||||||
selectedRow = 0;
|
selectedRow = 0;
|
||||||
@@ -1372,24 +1457,39 @@ int callscreen_run_main(int argc, char** argv) {
|
|||||||
callId.clear();
|
callId.clear();
|
||||||
callNumber.clear();
|
callNumber.clear();
|
||||||
contactName.clear();
|
contactName.clear();
|
||||||
|
recordFileName.clear();
|
||||||
|
recordFilePath.clear();
|
||||||
|
lastRecordedSec = 0;
|
||||||
|
recordedClipCount = 0;
|
||||||
updateCallerDetails(arg1, arg2);
|
updateCallerDetails(arg1, arg2);
|
||||||
callState = CallState::Outgoing;
|
callState = CallState::Outgoing;
|
||||||
callStartMs = nowMs();
|
callStartMs = nowMs();
|
||||||
ringMuted = false;
|
ringMuted = false;
|
||||||
|
speakerOn = false;
|
||||||
dtmfMode = false;
|
dtmfMode = false;
|
||||||
dtmfSent.clear();
|
dtmfSent.clear();
|
||||||
selectedRow = 0;
|
selectedRow = 0;
|
||||||
|
setSpeakerRoute(false);
|
||||||
|
statusNotice.clear();
|
||||||
|
statusNoticeUntilMs = 0;
|
||||||
writeActiveStateFiles();
|
writeActiveStateFiles();
|
||||||
showWindow();
|
showWindow();
|
||||||
} else if (verb == "ACTIVE") {
|
} else if (verb == "ACTIVE") {
|
||||||
bool wasActive = (callState == CallState::Active);
|
bool wasActive = (callState == CallState::Active);
|
||||||
|
bool wasOutgoing = (callState == CallState::Outgoing);
|
||||||
updateCallerDetails(arg1, arg2);
|
updateCallerDetails(arg1, arg2);
|
||||||
callState = CallState::Active;
|
callState = CallState::Active;
|
||||||
if (!wasActive) {
|
if (!wasActive) {
|
||||||
callStartMs = nowMs();
|
callStartMs = nowMs();
|
||||||
|
if (!wasOutgoing) {
|
||||||
|
speakerOn = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ringMuted = false;
|
ringMuted = false;
|
||||||
selectedRow = 0;
|
selectedRow = 0;
|
||||||
|
setSpeakerRoute(speakerOn);
|
||||||
|
statusNotice.clear();
|
||||||
|
statusNoticeUntilMs = 0;
|
||||||
writeActiveStateFiles();
|
writeActiveStateFiles();
|
||||||
showWindow();
|
showWindow();
|
||||||
} else if (verb == "OPEN") {
|
} else if (verb == "OPEN") {
|
||||||
|
|||||||
@@ -1003,7 +1003,9 @@ int runNetworksApp(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
std::string cmd = "nmcli connection add type gsm con-name " +
|
std::string cmd = "nmcli connection add type gsm con-name " +
|
||||||
shellQuote(cleanApn) + " ifname '*' apn " +
|
shellQuote(cleanApn) + " ifname '*' apn " +
|
||||||
shellQuote(cleanApn) + " connection.autoconnect yes";
|
shellQuote(cleanApn) +
|
||||||
|
" ipv4.method auto ipv6.method ignore"
|
||||||
|
" connection.autoconnect yes";
|
||||||
if (!trimStr(apnUser).empty()) {
|
if (!trimStr(apnUser).empty()) {
|
||||||
cmd += " gsm.username " + shellQuote(trimStr(apnUser));
|
cmd += " gsm.username " + shellQuote(trimStr(apnUser));
|
||||||
}
|
}
|
||||||
@@ -1148,9 +1150,15 @@ int runNetworksApp(int argc, char** argv) {
|
|||||||
bool turnOn = !cellInfo.gsmActive;
|
bool turnOn = !cellInfo.gsmActive;
|
||||||
setBanner(turnOn ? "Enabling Mobile Data..." : "Disabling Mobile Data...");
|
setBanner(turnOn ? "Enabling Mobile Data..." : "Disabling Mobile Data...");
|
||||||
render();
|
render();
|
||||||
|
if (turnOn) {
|
||||||
|
runCaptureCmd("nmcli connection modify " +
|
||||||
|
shellQuote(cellInfo.gsmConName) +
|
||||||
|
" ipv4.method auto ipv6.method ignore >/dev/null 2>&1");
|
||||||
|
}
|
||||||
runCaptureCmd("nmcli connection " + std::string(turnOn ? "up " : "down ") +
|
runCaptureCmd("nmcli connection " + std::string(turnOn ? "up " : "down ") +
|
||||||
shellQuote(cellInfo.gsmConName) + " >/dev/null 2>&1");
|
shellQuote(cellInfo.gsmConName) + " >/dev/null 2>&1");
|
||||||
refreshCurrentTab(false);
|
refreshCurrentTab(false);
|
||||||
|
runAsyncCmd("sxmo_hook_statusbar.sh modem >/dev/null 2>&1");
|
||||||
}
|
}
|
||||||
} else if (cellRowSel == 1) {
|
} else if (cellRowSel == 1) {
|
||||||
apnName = cellInfo.apn;
|
apnName = cellInfo.apn;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ struct RingSettings {
|
|||||||
int ringVol = 100;
|
int ringVol = 100;
|
||||||
int ringTimeSec = 20;
|
int ringTimeSec = 20;
|
||||||
int ringLoops = 10;
|
int ringLoops = 10;
|
||||||
int vibrateStrength = 1;
|
int vibrateStrength = 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int kRingTimeSteps[] = {10, 15, 20, 25, 30, 45, 60};
|
constexpr int kRingTimeSteps[] = {10, 15, 20, 25, 30, 45, 60};
|
||||||
@@ -164,6 +164,10 @@ std::string loadActiveRingtonePath() {
|
|||||||
}
|
}
|
||||||
const char* envRing = std::getenv("SXMO_RINGTONE");
|
const char* envRing = std::getenv("SXMO_RINGTONE");
|
||||||
if (envRing && *envRing) return envRing;
|
if (envRing && *envRing) return envRing;
|
||||||
|
struct stat stDef {};
|
||||||
|
if (::stat("/usr/share/sxmo/ringtones/Banana_Chime.wav", &stDef) == 0) {
|
||||||
|
return "/usr/share/sxmo/ringtones/Banana_Chime.wav";
|
||||||
|
}
|
||||||
return "/usr/share/sxmo/ring.ogg";
|
return "/usr/share/sxmo/ring.ogg";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -386,15 +390,22 @@ int runRingtonesApp(int argc, char** argv) {
|
|||||||
settings = loadRingSettings();
|
settings = loadRingSettings();
|
||||||
int rvol = std::clamp(settings.ringVol, 0, 100);
|
int rvol = std::clamp(settings.ringVol, 0, 100);
|
||||||
int rx3 = (rvol <= 0) ? 0 : (60 + rvol * 34 / 100);
|
int rx3 = (rvol <= 0) ? 0 : (60 + rvol * 34 / 100);
|
||||||
int mpvVol = rvol * 130 / 100;
|
int mpvVol = rvol;
|
||||||
std::string volArg = "--volume=" + std::to_string(mpvVol);
|
std::string volArg = "--volume=" + std::to_string(mpvVol);
|
||||||
runAsyncCmd("amixer -c 0 cset \"name='EAR_S'\" 0 >/dev/null 2>&1; "
|
runAsyncCmd("amixer -c 0 cset \"name='EAR_S'\" 0 >/dev/null 2>&1; "
|
||||||
|
"amixer -c 0 cset \"name='RX1 MIX1 INP1'\" 0 >/dev/null 2>&1; "
|
||||||
|
"amixer -c 0 cset \"name='PRI_MI2S_RX Audio Mixer MultiMedia1'\" 1 >/dev/null 2>&1; "
|
||||||
"amixer -c 0 cset \"name='SPK DAC Switch'\" 1 >/dev/null 2>&1; "
|
"amixer -c 0 cset \"name='SPK DAC Switch'\" 1 >/dev/null 2>&1; "
|
||||||
"amixer -c 0 cset \"name='RX3 MIX1 INP1'\" 3 >/dev/null 2>&1; "
|
"amixer -c 0 cset \"name='RX3 MIX1 INP1'\" 3 >/dev/null 2>&1; "
|
||||||
"amixer -c 0 cset \"name='RX3 Digital Volume'\" " +
|
"amixer -c 0 cset \"name='RX3 Digital Volume'\" " +
|
||||||
std::to_string(rx3) + " >/dev/null 2>&1; "
|
std::to_string(rx3) + " >/dev/null 2>&1; "
|
||||||
"wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1; "
|
"wpctl set-mute @DEFAULT_AUDIO_SINK@ 0 >/dev/null 2>&1; "
|
||||||
"wpctl set-volume @DEFAULT_AUDIO_SINK@ 100% >/dev/null 2>&1");
|
"wpctl set-volume @DEFAULT_AUDIO_SINK@ " +
|
||||||
|
std::to_string(rvol) + "% >/dev/null 2>&1; "
|
||||||
|
"(sleep 0.25; amixer -c 0 cset \"name='RX3 Digital Volume'\" " +
|
||||||
|
std::to_string(rx3) + " >/dev/null 2>&1; "
|
||||||
|
"wpctl set-volume @DEFAULT_AUDIO_SINK@ " +
|
||||||
|
std::to_string(rvol) + "% >/dev/null 2>&1) &");
|
||||||
pid_t pid = ::fork();
|
pid_t pid = ::fork();
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
std::string runtimeDir = "/run/user/" + std::to_string(::getuid());
|
std::string runtimeDir = "/run/user/" + std::to_string(::getuid());
|
||||||
@@ -410,7 +421,8 @@ int runRingtonesApp(int argc, char** argv) {
|
|||||||
::dup2(devNull, STDERR_FILENO);
|
::dup2(devNull, STDERR_FILENO);
|
||||||
if (devNull > 2) ::close(devNull);
|
if (devNull > 2) ::close(devNull);
|
||||||
}
|
}
|
||||||
::execlp("mpv", "mpv", "--no-resume-playback", "--quiet",
|
::execlp("mpv", "mpv", "--no-config", "--no-terminal",
|
||||||
|
"--no-resume-playback", "--quiet", "--vo=null",
|
||||||
"--no-video", "--ao=pipewire,pulse,alsa",
|
"--no-video", "--ao=pipewire,pulse,alsa",
|
||||||
"--audio-channels=stereo", "--audio-samplerate=48000",
|
"--audio-channels=stereo", "--audio-samplerate=48000",
|
||||||
"--volume-max=150", volArg.c_str(),
|
"--volume-max=150", volArg.c_str(),
|
||||||
@@ -564,6 +576,8 @@ int runRingtonesApp(int argc, char** argv) {
|
|||||||
"Vibrate Only",
|
"Vibrate Only",
|
||||||
"Silent",
|
"Silent",
|
||||||
};
|
};
|
||||||
|
const char* vibLabels[3] = {"1 (Light)", "2 (Medium)", "3 (Strong)"};
|
||||||
|
int vIdx = std::clamp(settings.vibrateStrength, 1, 3) - 1;
|
||||||
std::string rows[6][2] = {
|
std::string rows[6][2] = {
|
||||||
{"Ringer Mode",
|
{"Ringer Mode",
|
||||||
"< " + std::string(modeLabels[settings.ringerMode % 4]) + " >"},
|
"< " + std::string(modeLabels[settings.ringerMode % 4]) + " >"},
|
||||||
@@ -574,7 +588,7 @@ int runRingtonesApp(int argc, char** argv) {
|
|||||||
{"Ring Loop Count",
|
{"Ring Loop Count",
|
||||||
"< " + std::to_string(settings.ringLoops) + " loops >"},
|
"< " + std::to_string(settings.ringLoops) + " loops >"},
|
||||||
{"Vibrate Strength",
|
{"Vibrate Strength",
|
||||||
"< Level " + std::to_string(settings.vibrateStrength) + " >"},
|
"< Level " + std::string(vibLabels[vIdx]) + " >"},
|
||||||
{"Test Vibration", "Press OK to vibrate"},
|
{"Test Vibration", "Press OK to vibrate"},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -665,6 +679,13 @@ int runRingtonesApp(int argc, char** argv) {
|
|||||||
} else if (settingsRow == 1) {
|
} else if (settingsRow == 1) {
|
||||||
settings.ringVol = std::clamp(settings.ringVol + delta * 5, 0, 100);
|
settings.ringVol = std::clamp(settings.ringVol + delta * 5, 0, 100);
|
||||||
saveRingSettings(settings);
|
saveRingSettings(settings);
|
||||||
|
if (previewPid > 0) {
|
||||||
|
int rx3 = (settings.ringVol <= 0) ? 0 : (60 + settings.ringVol * 34 / 100);
|
||||||
|
runAsyncCmd("amixer -c 0 cset \"name='RX3 Digital Volume'\" " +
|
||||||
|
std::to_string(rx3) + " >/dev/null 2>&1; "
|
||||||
|
"wpctl set-volume @DEFAULT_AUDIO_SINK@ " +
|
||||||
|
std::to_string(settings.ringVol) + "% >/dev/null 2>&1");
|
||||||
|
}
|
||||||
setBanner("Ring volume: " + std::to_string(settings.ringVol) + "%");
|
setBanner("Ring volume: " + std::to_string(settings.ringVol) + "%");
|
||||||
} else if (settingsRow == 2) {
|
} else if (settingsRow == 2) {
|
||||||
int curIdx = 2;
|
int curIdx = 2;
|
||||||
@@ -692,17 +713,14 @@ int runRingtonesApp(int argc, char** argv) {
|
|||||||
settings.vibrateStrength =
|
settings.vibrateStrength =
|
||||||
((settings.vibrateStrength - 1 + 3 + delta) % 3) + 1;
|
((settings.vibrateStrength - 1 + 3 + delta) % 3) + 1;
|
||||||
saveRingSettings(settings);
|
saveRingSettings(settings);
|
||||||
|
runAsyncCmd("SXMO_VIBRATE_FORCE=1 sxmo_vibrate 450 " +
|
||||||
|
std::to_string(settings.vibrateStrength) + " >/dev/null 2>&1");
|
||||||
|
setBanner("Vibration level: " + std::to_string(settings.vibrateStrength));
|
||||||
} else if (settingsRow == 5) {
|
} else if (settingsRow == 5) {
|
||||||
int mag = (settings.vibrateStrength == 1)
|
runAsyncCmd("SXMO_VIBRATE_FORCE=1 sxmo_vibrate 800 " +
|
||||||
? 8000
|
std::to_string(settings.vibrateStrength) + " >/dev/null 2>&1");
|
||||||
: (settings.vibrateStrength == 3 ? 65535 : 32000);
|
setBanner("Testing vibration (Level " +
|
||||||
runAsyncCmd(
|
std::to_string(settings.vibrateStrength) + ")...");
|
||||||
"for d in /dev/input/by-path/platform-vibrator-event "
|
|
||||||
"/dev/input/by-path/*vibrator*-event; do "
|
|
||||||
"[ -e \"$d\" ] && export SXMO_VIBRATE_DEV=\"$d\" && break; done; "
|
|
||||||
"sxmo_vibrate 600 " +
|
|
||||||
std::to_string(mag) + " >/dev/null 2>&1");
|
|
||||||
setBanner("Testing vibration...");
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+107
-26
@@ -2,6 +2,7 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <glob.h>
|
#include <glob.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
|
#include <signal.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -9,17 +10,42 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static int read_configured_level(void) {
|
static volatile sig_atomic_t g_stop = 0;
|
||||||
char path[512];
|
|
||||||
|
static void handle_signal(int sig) {
|
||||||
|
(void)sig;
|
||||||
|
g_stop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_sxmo_config_dir(char* out, size_t out_sz) {
|
||||||
const char* cfg = getenv("XDG_CONFIG_HOME");
|
const char* cfg = getenv("XDG_CONFIG_HOME");
|
||||||
const char* home = getenv("HOME");
|
const char* home = getenv("HOME");
|
||||||
if (cfg && *cfg) {
|
if (cfg && *cfg) {
|
||||||
snprintf(path, sizeof(path), "%s/sxmo/ringsettings", cfg);
|
snprintf(out, out_sz, "%s/sxmo", cfg);
|
||||||
} else if (home && *home) {
|
} else if (home && *home) {
|
||||||
snprintf(path, sizeof(path), "%s/.config/sxmo/ringsettings", home);
|
snprintf(out, out_sz, "%s/.config/sxmo", home);
|
||||||
} else {
|
} else {
|
||||||
snprintf(path, sizeof(path), "/home/user/.config/sxmo/ringsettings");
|
snprintf(out, out_sz, "/home/user/.config/sxmo");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int is_vibrate_muted(void) {
|
||||||
|
const char* force = getenv("SXMO_VIBRATE_FORCE");
|
||||||
|
if (force && *force == '1') {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
char dir[448];
|
||||||
|
char path[512];
|
||||||
|
get_sxmo_config_dir(dir, sizeof(dir));
|
||||||
|
snprintf(path, sizeof(path), "%s/.novibrate", dir);
|
||||||
|
return access(path, F_OK) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_configured_level(void) {
|
||||||
|
char dir[448];
|
||||||
|
char path[512];
|
||||||
|
get_sxmo_config_dir(dir, sizeof(dir));
|
||||||
|
snprintf(path, sizeof(path), "%s/ringsettings", dir);
|
||||||
|
|
||||||
FILE* fp = fopen(path, "r");
|
FILE* fp = fopen(path, "r");
|
||||||
if (!fp) return 0;
|
if (!fp) return 0;
|
||||||
@@ -32,24 +58,45 @@ static int read_configured_level(void) {
|
|||||||
if (strncmp(p, "SXMO_VIBRATE_STRENGTH=", 22) == 0) {
|
if (strncmp(p, "SXMO_VIBRATE_STRENGTH=", 22) == 0) {
|
||||||
int v = atoi(p + 22);
|
int v = atoi(p + 22);
|
||||||
if (v >= 1 && v <= 3) level = v;
|
if (v >= 1 && v <= 3) level = v;
|
||||||
|
else if (v > 3 && v <= 16000) level = 1;
|
||||||
|
else if (v > 16000 && v <= 45000) level = 2;
|
||||||
|
else if (v > 45000) level = 3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int resolve_magnitude(int raw_arg, int has_arg) {
|
static int resolve_level_and_magnitude(int raw_arg, int has_arg, int* level_out) {
|
||||||
int val = raw_arg;
|
int val = raw_arg;
|
||||||
if (!has_arg || (val == 1 && !getenv("SXMO_VIBRATE_STRENGTH"))) {
|
const char* env_str = getenv("SXMO_VIBRATE_STRENGTH");
|
||||||
|
if (!has_arg || (val == 1 && (!env_str || !*env_str))) {
|
||||||
int cfg = read_configured_level();
|
int cfg = read_configured_level();
|
||||||
if (cfg >= 1 && cfg <= 3) val = cfg;
|
if (cfg >= 1 && cfg <= 3) {
|
||||||
|
val = cfg;
|
||||||
|
} else if (env_str && *env_str) {
|
||||||
|
int ev = atoi(env_str);
|
||||||
|
if (ev >= 1) val = ev;
|
||||||
|
} else if (!has_arg) {
|
||||||
|
val = 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (val <= 1) return 8000;
|
|
||||||
if (val == 2) return 32000;
|
int level = 2;
|
||||||
if (val == 3) return 65535;
|
int mag = 32000;
|
||||||
if (val < 256) return val * 257;
|
if (val <= 1 || (val >= 256 && val <= 16000)) {
|
||||||
if (val > 65535) return 65535;
|
level = 1;
|
||||||
return val;
|
mag = 14000;
|
||||||
|
} else if (val == 2 || (val > 16000 && val <= 45000)) {
|
||||||
|
level = 2;
|
||||||
|
mag = 32000;
|
||||||
|
} else {
|
||||||
|
level = 3;
|
||||||
|
mag = 65535;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level_out) *level_out = level;
|
||||||
|
return mag;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int open_vibrator_device(void) {
|
static int open_vibrator_device(void) {
|
||||||
@@ -77,6 +124,15 @@ static int open_vibrator_device(void) {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sleep_ms_interruptible(long ms) {
|
||||||
|
if (ms <= 0 || g_stop) return;
|
||||||
|
struct timespec req;
|
||||||
|
req.tv_sec = ms / 1000;
|
||||||
|
req.tv_nsec = (ms % 1000) * 1000000L;
|
||||||
|
while (!g_stop && nanosleep(&req, &req) == -1 && errno == EINTR) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
if (argc < 2 || argc > 3) {
|
if (argc < 2 || argc > 3) {
|
||||||
fprintf(stderr, "Usage: sxmo_vibrate duration_ms [strength_number]\n");
|
fprintf(stderr, "Usage: sxmo_vibrate duration_ms [strength_number]\n");
|
||||||
@@ -91,6 +147,10 @@ int main(int argc, char** argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_vibrate_muted()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int raw_strength = 2;
|
int raw_strength = 2;
|
||||||
int has_strength = 0;
|
int has_strength = 0;
|
||||||
if (argc == 3) {
|
if (argc == 3) {
|
||||||
@@ -103,7 +163,16 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int magnitude = resolve_magnitude(raw_strength, has_strength);
|
int level = 2;
|
||||||
|
int magnitude = resolve_level_and_magnitude(raw_strength, has_strength, &level);
|
||||||
|
|
||||||
|
struct sigaction sa;
|
||||||
|
memset(&sa, 0, sizeof(sa));
|
||||||
|
sa.sa_handler = handle_signal;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
sigaction(SIGINT, &sa, NULL);
|
||||||
|
sigaction(SIGTERM, &sa, NULL);
|
||||||
|
sigaction(SIGHUP, &sa, NULL);
|
||||||
|
|
||||||
int fd = open_vibrator_device();
|
int fd = open_vibrator_device();
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
@@ -128,18 +197,30 @@ int main(int argc, char** argv) {
|
|||||||
memset(&play, 0, sizeof(play));
|
memset(&play, 0, sizeof(play));
|
||||||
play.type = EV_FF;
|
play.type = EV_FF;
|
||||||
play.code = (unsigned short)effect.id;
|
play.code = (unsigned short)effect.id;
|
||||||
play.value = 3;
|
|
||||||
|
|
||||||
if (write(fd, &play, sizeof(play)) < 0) {
|
if (level >= 3 || duration_ms <= 90) {
|
||||||
ioctl(fd, EVIOCRMFF, effect.id);
|
long actual_ms = (level == 1 && duration_ms > 55) ? 55 : duration_ms;
|
||||||
close(fd);
|
play.value = 1;
|
||||||
return 1;
|
if (write(fd, &play, sizeof(play)) >= 0) {
|
||||||
}
|
sleep_ms_interruptible(actual_ms);
|
||||||
|
}
|
||||||
struct timespec req;
|
} else {
|
||||||
req.tv_sec = duration_ms / 1000;
|
long on_ms = (level == 1) ? 75 : 160;
|
||||||
req.tv_nsec = (duration_ms % 1000) * 1000000L;
|
long off_ms = (level == 1) ? 125 : 80;
|
||||||
while (nanosleep(&req, &req) == -1 && errno == EINTR) {
|
long rem = duration_ms;
|
||||||
|
while (rem > 0 && !g_stop) {
|
||||||
|
long cur_on = (rem < on_ms) ? rem : on_ms;
|
||||||
|
play.value = 1;
|
||||||
|
if (write(fd, &play, sizeof(play)) < 0) break;
|
||||||
|
sleep_ms_interruptible(cur_on);
|
||||||
|
rem -= cur_on;
|
||||||
|
play.value = 0;
|
||||||
|
(void)write(fd, &play, sizeof(play));
|
||||||
|
if (rem <= 0 || g_stop) break;
|
||||||
|
long cur_off = (rem < off_ms) ? rem : off_ms;
|
||||||
|
sleep_ms_interruptible(cur_off);
|
||||||
|
rem -= cur_off;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
play.value = 0;
|
play.value = 0;
|
||||||
|
|||||||
+63
-16
@@ -43,7 +43,7 @@ constexpr int kWinX = 0;
|
|||||||
constexpr int kWinY = kTopBarHeight;
|
constexpr int kWinY = kTopBarHeight;
|
||||||
constexpr int kWinWidth = kScreenWidth;
|
constexpr int kWinWidth = kScreenWidth;
|
||||||
constexpr int kWinHeight = kScreenHeight - kTopBarHeight; // 296px flush below 24px dwm bar
|
constexpr int kWinHeight = kScreenHeight - kTopBarHeight; // 296px flush below 24px dwm bar
|
||||||
constexpr int kRowCount = 9;
|
constexpr int kRowCount = 10;
|
||||||
|
|
||||||
// Nerd Font PUA icons matching sxmo_hook_icons.sh & dwm ("Fira Code, DejaVuSansM Nerd Font")
|
// Nerd Font PUA icons matching sxmo_hook_icons.sh & dwm ("Fira Code, DejaVuSansM Nerd Font")
|
||||||
constexpr const char* kIconSpeaker = "\xef\x80\xa8"; // U+F028 speaker
|
constexpr const char* kIconSpeaker = "\xef\x80\xa8"; // U+F028 speaker
|
||||||
@@ -242,11 +242,12 @@ enum class ControlId {
|
|||||||
EarpieceVol = 1,
|
EarpieceVol = 1,
|
||||||
RingVol = 2,
|
RingVol = 2,
|
||||||
RingerMode = 3,
|
RingerMode = 3,
|
||||||
Flashlight = 4,
|
VibrateStrength = 4,
|
||||||
SoundMute = 5,
|
Flashlight = 5,
|
||||||
MicMute = 6,
|
SoundMute = 6,
|
||||||
ScreenBright = 7,
|
MicMute = 7,
|
||||||
KbdBright = 8,
|
ScreenBright = 8,
|
||||||
|
KbdBright = 9,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ControlItem {
|
struct ControlItem {
|
||||||
@@ -300,15 +301,15 @@ class PhoneController {
|
|||||||
items_.push_back({ControlId::SpeakerVol, kIconSpeaker, kIconSoundOff,
|
items_.push_back({ControlId::SpeakerVol, kIconSpeaker, kIconSoundOff,
|
||||||
"Speaker", true, 0, 100, 5, spkPct});
|
"Speaker", true, 0, 100, 5, spkPct});
|
||||||
|
|
||||||
// 2. Earpiece volume (0..100%, mapped to ALSA RX1 Digital Volume 58..90)
|
// 2. Earpiece volume (0..100%, mapped to ALSA RX1 Digital Volume 68..92)
|
||||||
long rx1 = alsa_.getInteger("RX1 Digital Volume", 84);
|
long rx1 = alsa_.getInteger("RX1 Digital Volume", 88);
|
||||||
int earPct = 100;
|
int earPct = 85;
|
||||||
if (rx1 <= 0) {
|
if (rx1 <= 0) {
|
||||||
earPct = 0;
|
earPct = 0;
|
||||||
} else if (rx1 < 84) {
|
} else if (rx1 >= 68) {
|
||||||
earPct = std::clamp(static_cast<int>(std::lround((rx1 * 100.0) / 84.0)), 5, 100);
|
earPct = std::clamp(static_cast<int>(std::lround(((rx1 - 68) * 100.0) / 24.0)), 5, 100);
|
||||||
}
|
}
|
||||||
earPct = (earPct / 5) * 5;
|
earPct = ((earPct + 2) / 5) * 5;
|
||||||
items_.push_back({ControlId::EarpieceVol, kIconEarpiece, kIconEarpiece,
|
items_.push_back({ControlId::EarpieceVol, kIconEarpiece, kIconEarpiece,
|
||||||
"Earpiece", true, 0, 100, 5, earPct});
|
"Earpiece", true, 0, 100, 5, earPct});
|
||||||
|
|
||||||
@@ -322,6 +323,11 @@ class PhoneController {
|
|||||||
items_.push_back({ControlId::RingerMode, kIconBell, kIconBellSlash,
|
items_.push_back({ControlId::RingerMode, kIconBell, kIconBellSlash,
|
||||||
"Ringer", false, 0, 3, 1, ringerVal});
|
"Ringer", false, 0, 3, 1, ringerVal});
|
||||||
|
|
||||||
|
// 5. Vibration Strength (1=Light, 2=Medium, 3=Strong)
|
||||||
|
int vibVal = readVibrateStrength();
|
||||||
|
items_.push_back({ControlId::VibrateStrength, kIconBell, kIconBell,
|
||||||
|
"Vibration", false, 1, 3, 1, vibVal});
|
||||||
|
|
||||||
// 5. Flashlight on/off (toggle)
|
// 5. Flashlight on/off (toggle)
|
||||||
int torchCur = readIntFile(torchDir_ + "/brightness", 0);
|
int torchCur = readIntFile(torchDir_ + "/brightness", 0);
|
||||||
items_.push_back({ControlId::Flashlight, kIconFlashlight, kIconFlashlight,
|
items_.push_back({ControlId::Flashlight, kIconFlashlight, kIconFlashlight,
|
||||||
@@ -373,6 +379,8 @@ class PhoneController {
|
|||||||
it.value = std::clamp(it.value + dir * it.stepVal, it.minVal, it.maxVal);
|
it.value = std::clamp(it.value + dir * it.stepVal, it.minVal, it.maxVal);
|
||||||
} else if (it.id == ControlId::RingerMode) {
|
} else if (it.id == ControlId::RingerMode) {
|
||||||
it.value = (it.value + dir + 4) % 4;
|
it.value = (it.value + dir + 4) % 4;
|
||||||
|
} else if (it.id == ControlId::VibrateStrength) {
|
||||||
|
it.value = ((it.value - 1 + dir + 3) % 3) + 1;
|
||||||
} else {
|
} else {
|
||||||
it.value = it.value ? 0 : 1;
|
it.value = it.value ? 0 : 1;
|
||||||
}
|
}
|
||||||
@@ -387,6 +395,9 @@ class PhoneController {
|
|||||||
if (it.id == ControlId::RingerMode) {
|
if (it.id == ControlId::RingerMode) {
|
||||||
it.value = (it.value + 1) % 4;
|
it.value = (it.value + 1) % 4;
|
||||||
apply(it);
|
apply(it);
|
||||||
|
} else if (it.id == ControlId::VibrateStrength) {
|
||||||
|
it.value = (it.value % 3) + 1;
|
||||||
|
apply(it);
|
||||||
} else if (!it.isSlider) {
|
} else if (!it.isSlider) {
|
||||||
it.value = it.value ? 0 : 1;
|
it.value = it.value ? 0 : 1;
|
||||||
apply(it);
|
apply(it);
|
||||||
@@ -432,13 +443,30 @@ class PhoneController {
|
|||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeRingVolume(int ringVol) {
|
static int readVibrateStrength() {
|
||||||
|
std::ifstream ifs(getSxmoCfgDir() + "/ringsettings");
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ifs, line)) {
|
||||||
|
auto eq = line.find('=');
|
||||||
|
if (eq == std::string::npos) continue;
|
||||||
|
std::string k = line.substr(0, eq);
|
||||||
|
std::string v = line.substr(eq + 1);
|
||||||
|
if (k == "SXMO_VIBRATE_STRENGTH") {
|
||||||
|
int val = std::atoi(v.c_str());
|
||||||
|
if (val >= 1 && val <= 3) return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void writeRingAndVibSettings(int newRingVol, int newVibStrength) {
|
||||||
std::string cfg = getSxmoCfgDir();
|
std::string cfg = getSxmoCfgDir();
|
||||||
::mkdir(cfg.c_str(), 0755);
|
::mkdir(cfg.c_str(), 0755);
|
||||||
std::string path = cfg + "/ringsettings";
|
std::string path = cfg + "/ringsettings";
|
||||||
int ringTime = 20;
|
int ringTime = 20;
|
||||||
int ringLoops = 10;
|
int ringLoops = 10;
|
||||||
int vibStrength = 2;
|
int vibStrength = 2;
|
||||||
|
int ringVol = 100;
|
||||||
{
|
{
|
||||||
std::ifstream ifs(path);
|
std::ifstream ifs(path);
|
||||||
std::string line;
|
std::string line;
|
||||||
@@ -450,14 +478,17 @@ class PhoneController {
|
|||||||
if (k == "SXMO_RINGTIME" && val >= 5 && val <= 120) ringTime = val;
|
if (k == "SXMO_RINGTIME" && val >= 5 && val <= 120) ringTime = val;
|
||||||
else if (k == "SXMO_RINGNUMBER" && val >= 1 && val <= 100) ringLoops = val;
|
else if (k == "SXMO_RINGNUMBER" && val >= 1 && val <= 100) ringLoops = val;
|
||||||
else if (k == "SXMO_VIBRATE_STRENGTH" && val >= 1 && val <= 3) vibStrength = val;
|
else if (k == "SXMO_VIBRATE_STRENGTH" && val >= 1 && val <= 3) vibStrength = val;
|
||||||
|
else if (k == "SXMO_RINGVOL" && val >= 0 && val <= 100) ringVol = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (newRingVol >= 0) ringVol = std::clamp(newRingVol, 0, 100);
|
||||||
|
if (newVibStrength >= 1 && newVibStrength <= 3) vibStrength = newVibStrength;
|
||||||
std::ofstream ofs(path, std::ios::trunc);
|
std::ofstream ofs(path, std::ios::trunc);
|
||||||
if (ofs) {
|
if (ofs) {
|
||||||
ofs << "SXMO_RINGTIME=" << ringTime << "\n";
|
ofs << "SXMO_RINGTIME=" << ringTime << "\n";
|
||||||
ofs << "SXMO_RINGNUMBER=" << ringLoops << "\n";
|
ofs << "SXMO_RINGNUMBER=" << ringLoops << "\n";
|
||||||
ofs << "SXMO_VIBRATE_STRENGTH=" << vibStrength << "\n";
|
ofs << "SXMO_VIBRATE_STRENGTH=" << vibStrength << "\n";
|
||||||
ofs << "SXMO_RINGVOL=" << std::clamp(ringVol, 0, 100) << "\n";
|
ofs << "SXMO_RINGVOL=" << ringVol << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,12 +515,21 @@ class PhoneController {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ControlId::EarpieceVol: {
|
case ControlId::EarpieceVol: {
|
||||||
long hw = (it.value <= 0) ? 0 : (58 + std::lround((it.value * 32.0) / 100.0));
|
long hw = (it.value <= 0) ? 0 : (68 + std::lround((it.value * 24.0) / 100.0));
|
||||||
alsa_.setInteger("RX1 Digital Volume", hw);
|
alsa_.setInteger("RX1 Digital Volume", hw);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ControlId::RingVol: {
|
case ControlId::RingVol: {
|
||||||
writeRingVolume(it.value);
|
writeRingAndVibSettings(it.value, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ControlId::VibrateStrength: {
|
||||||
|
writeRingAndVibSettings(-1, it.value);
|
||||||
|
char cmd[96];
|
||||||
|
std::snprintf(cmd, sizeof(cmd),
|
||||||
|
"SXMO_VIBRATE_FORCE=1 sxmo_vibrate 400 %d >/dev/null 2>&1",
|
||||||
|
it.value);
|
||||||
|
runAsyncCmd(cmd);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ControlId::Flashlight: {
|
case ControlId::Flashlight: {
|
||||||
@@ -792,6 +832,13 @@ static int xdrawer_run_main(int argc, char** argv) {
|
|||||||
XftColor* rCol = (it.value == 0) ? (isSel ? &colYellow : &colGreen)
|
XftColor* rCol = (it.value == 0) ? (isSel ? &colYellow : &colGreen)
|
||||||
: ((it.value == 1) ? &colYellow : &colMuted);
|
: ((it.value == 1) ? &colYellow : &colMuted);
|
||||||
drawPango(layoutSmall, rCol, kWinWidth - stW - 6, ry, rowH, stStr);
|
drawPango(layoutSmall, rCol, kWinWidth - stW - 6, ry, rowH, stStr);
|
||||||
|
} else if (it.id == ControlId::VibrateStrength) {
|
||||||
|
const char* vModes[3] = {"Light", "Medium", "Strong"};
|
||||||
|
int vIdx = std::clamp(it.value, 1, 3) - 1;
|
||||||
|
std::string stStr = std::string("< ") + vModes[vIdx] + " >";
|
||||||
|
int stW = pangoWidth(layoutSmall, stStr);
|
||||||
|
XftColor* vCol = isSel ? &colYellow : &colGreen;
|
||||||
|
drawPango(layoutSmall, vCol, kWinWidth - stW - 6, ry, rowH, stStr);
|
||||||
} else {
|
} else {
|
||||||
bool on = (it.value != 0);
|
bool on = (it.value != 0);
|
||||||
std::string stStr = on ? "ON" : "OFF";
|
std::string stStr = on ? "ON" : "OFF";
|
||||||
|
|||||||
Reference in New Issue
Block a user