Fix SIM2 QMI UIM provisioning, auto-enable ModemManager, PIN auto-prompt, call audio, and Dunst clearing

This commit is contained in:
Luxferre
2026-10-25 11:31:41 +02:00
parent 9b712dcbca
commit b56f310f21
41 changed files with 1193 additions and 352 deletions
@@ -1,7 +1,7 @@
# Maximum time to wait for SIM card to appear after starting the modem. # Maximum time to wait for SIM card to appear after starting the modem.
# Some SIM cards or modem firmwares might take longer to initialize. # Some SIM cards or modem firmwares might take longer to initialize.
# Set to 0 to disable waiting entirely. # Set to 0 to disable waiting entirely.
sim_wait_time=4 sim_wait_time=10
# Preferred physical SIM slot (1 or 2) for Primary GW provisioning on Dual-SIM # Preferred physical SIM slot (1 or 2) for Primary GW provisioning on Dual-SIM
# Qualcomm MSM8905/MSM8909 devices. Falls back automatically if the preferred # Qualcomm MSM8905/MSM8909 devices. Falls back automatically if the preferred
+21
View File
@@ -0,0 +1,21 @@
#!/sbin/openrc-run
# This script tries to automatically configure UIM application for Qualcomm QMI
# modems with more than one sim slot.
name="UIM Slot selection"
description="Select UIM slot and application on the embedded QMI modem."
depend() {
need rmtfs
after msm8909-modem-wwan-port
before ofono
before modemmanager
}
# Use the service start method instead of command= because we want to block
# other services while the modem isn't ready yet.
start() {
SIM_WAIT_TIME="${sim_wait_time:-10}" /usr/libexec/msm-modem-uim-selection boot
eend $?
}
@@ -8,7 +8,7 @@
horizontal_padding = 6 horizontal_padding = 6
frame_width = 2 frame_width = 2
frame_color = "#005577" frame_color = "#005577"
idle_threshold = 120 idle_threshold = 0
font = Fira Code 9.5 font = Fira Code 9.5
markup = full markup = full
max_icon_size = 24 max_icon_size = 24
@@ -22,16 +22,16 @@
[urgency_low] [urgency_low]
background = "#222222" background = "#222222"
foreground = "#cccccc" foreground = "#cccccc"
timeout = 8 timeout = 5
[urgency_normal] [urgency_normal]
background = "#1c1c1c" background = "#1c1c1c"
foreground = "#ffffff" foreground = "#ffffff"
frame_color = "#005577" frame_color = "#005577"
timeout = 12 timeout = 8
[urgency_critical] [urgency_critical]
background = "#900000" background = "#900000"
foreground = "#ffffff" foreground = "#ffffff"
frame_color = "#ff0000" frame_color = "#ff0000"
timeout = 0 timeout = 10
+24 -43
View File
@@ -3,61 +3,42 @@
# shellcheck source=scripts/core/sxmo_common.sh # shellcheck source=scripts/core/sxmo_common.sh
. sxmo_common.sh . sxmo_common.sh
ca_dbus_get_prop() {
dbus-send --session --print-reply --dest=org.mobian_project.CallAudio \
--reply-timeout=2500 /org/mobian_project/CallAudio org.freedesktop.DBus.Properties.Get \
string:org.mobian_project.CallAudio string:"$1"
}
ca_dbus_set_prop() {
dbus-send --session --print-reply --type=method_call \
--reply-timeout=2500 --dest=org.mobian_project.CallAudio \
/org/mobian_project/CallAudio org.mobian_project.CallAudio."$1" "$2" |\
grep -q "boolean true" && return 0 || return 1
}
enable_call_audio_mode() { enable_call_audio_mode() {
if pactl -f json info 2>/dev/null | jq -r .server_name | grep -q PipeWire; then if command -v amixer >/dev/null 2>&1; then
sxmo_audio.sh device set Earpiece amixer -c 0 sset 'PRI_MI2S_RX Voice Mixer VoiceMMode1' on >/dev/null 2>&1 || true
sxmo_log "Successfully enabled call audio mode (Earpiece)." amixer -c 0 sset 'VoiceMMode1 Capture Mixer TERT_MI2S_TX' on >/dev/null 2>&1 || true
sxmo_hook_statusbar.sh volume amixer -c 0 sset 'RX1 MIX1 INP1' 'RX1' >/dev/null 2>&1 || true
amixer -c 0 sset 'RDAC2 MUX' 'RX1' >/dev/null 2>&1 || true
amixer -c 0 sset 'EAR_S' 'Switch' >/dev/null 2>&1 || true
amixer -c 0 sset 'DEC1 MUX' 'ADC1' >/dev/null 2>&1 || true
amixer -c 0 sset 'ADC1 Volume' 100% >/dev/null 2>&1 || true
amixer -c 0 sset 'RX1 Digital Volume' 84 >/dev/null 2>&1 || true
fi
sxmo_audio.sh device set Earpiece >/dev/null 2>&1 || true
sxmo_log "Enabled ALSA voice call audio mode (Earpiece)."
sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true
return 0 return 0
fi
if ca_dbus_set_prop SelectMode uint32:1; then
sxmo_log "Successfully enabled call audio mode."
sxmo_hook_statusbar.sh volume
else
sxmo_notify_user.sh "Failed to enable call audio mode."
return 1
fi
} }
disable_call_audio_mode() { disable_call_audio_mode() {
if pactl -f json info 2>/dev/null | jq -r .server_name | grep -q PipeWire; then if command -v amixer >/dev/null 2>&1; then
sxmo_audio.sh device set Speaker amixer -c 0 sset 'PRI_MI2S_RX Voice Mixer VoiceMMode1' off >/dev/null 2>&1 || true
sxmo_log "Successfully disabled call audio mode (Speaker)." amixer -c 0 sset 'VoiceMMode1 Capture Mixer TERT_MI2S_TX' off >/dev/null 2>&1 || true
sxmo_hook_statusbar.sh volume fi
sxmo_audio.sh device set Speaker >/dev/null 2>&1 || true
sxmo_log "Disabled ALSA voice call audio mode (Speaker)."
sxmo_hook_statusbar.sh volume >/dev/null 2>&1 || true
return 0 return 0
fi
if ca_dbus_set_prop SelectMode uint32:0; then
sxmo_log "Successfully disabled call audio mode."
sxmo_hook_statusbar.sh volume
else
sxmo_notify_user.sh "Failed to disable call audio mode."
return 1
fi
} }
setup_audio() { setup_audio() {
if ! enable_call_audio_mode; then enable_call_audio_mode
return 1 return 0
fi
} }
reset_audio() { reset_audio() {
if ! disable_call_audio_mode; then disable_call_audio_mode
return 1 return 0
fi
} }
"$@" "$@"
+5 -3
View File
@@ -36,7 +36,7 @@ pickup() {
NUM="$(vid_to_number "$CALLID" 2>/dev/null || true)" NUM="$(vid_to_number "$CALLID" 2>/dev/null || true)"
DIRECTION="$( DIRECTION="$(
mmcli --voice-status -o "$CALLID" -K | (mmcli -m any -o "$CALLID" -K 2>/dev/null || mmcli -o "$CALLID" -K 2>/dev/null || mmcli --voice-status -o "$CALLID" -K 2>/dev/null) |
grep call.properties.direction | grep call.properties.direction |
cut -d: -f2 | cut -d: -f2 |
tr -d " " tr -d " "
@@ -53,11 +53,12 @@ pickup() {
return 1 return 1
fi fi
sxmo_notify_user.sh "Started call"
touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.initiatedcall" touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.initiatedcall"
log_event "call_start" "$CALLID" log_event "call_start" "$CALLID"
if command -v xcallscreen >/dev/null 2>&1; then if command -v xcallscreen >/dev/null 2>&1; then
xcallscreen outgoing "$CALLID" "$NUM" >/dev/null 2>&1 || true xcallscreen outgoing "$CALLID" "$NUM" >/dev/null 2>&1 || true
else
sxmo_notify_user.sh "Started call"
fi fi
;; ;;
incoming) incoming)
@@ -74,11 +75,12 @@ pickup() {
return 1 return 1
fi fi
sxmo_notify_user.sh "Picked up call"
touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.pickedupcall" touch "$XDG_RUNTIME_DIR/sxmo_calls/${CALLID}.pickedupcall"
log_event "call_pickup" "$CALLID" log_event "call_pickup" "$CALLID"
if command -v xcallscreen >/dev/null 2>&1; then if command -v xcallscreen >/dev/null 2>&1; then
xcallscreen active "$CALLID" "$NUM" >/dev/null 2>&1 || true xcallscreen active "$CALLID" "$NUM" >/dev/null 2>&1 || true
else
sxmo_notify_user.sh "Picked up call"
fi fi
;; ;;
*) *)
+23 -16
View File
@@ -16,15 +16,16 @@ dial_number() {
return 0 return 0
fi fi
NUMBER="$1" NUMBER="$(printf "%s" "$1" | tr -cd '+0-9*#')"
if [ -z "$NUMBER" ]; then
return 1
fi
CLEANEDNUMBER="$(pnc find ${DEFAULT_COUNTRY:+-c "$DEFAULT_COUNTRY"} "$1" 2>/dev/null || true)" mm_state="$(mmcli -m any -K 2>/dev/null | awk -F': ' '$1 == "modem.generic.state" {print $2; exit}')"
if [ -n "$CLEANEDNUMBER" ] && [ "$NUMBER" != "$CLEANEDNUMBER" ]; then if [ "$mm_state" = "disabled" ] || [ -z "$mm_state" ]; then
NUMBER="$(cat <<EOF | sxmo_dmenu.sh -p "Rewrite ?" mmcli -m any -e >/dev/null 2>&1 || true
$NUMBER mmcli -m any --3gpp-register-home >/dev/null 2>&1 || true
$CLEANEDNUMBER sleep 1
EOF
)"
fi fi
sxmo_log "Attempting to dial: $NUMBER" sxmo_log "Attempting to dial: $NUMBER"
@@ -33,7 +34,7 @@ EOF
fi fi
if ! CALLID="$( if ! CALLID="$(
mmcli -m any --voice-create-call "number=$NUMBER" | mmcli -m any --voice-create-call="number=$NUMBER" 2>/dev/null |
grep -Eo "Call/[0-9]+" | grep -Eo "Call/[0-9]+" |
grep -oE "[0-9]+" grep -oE "[0-9]+"
)"; then )"; then
@@ -79,16 +80,22 @@ EOF
} }
dial_ussd() { dial_ussd() {
USSD="$1" USSD="$1"
DBUS_MODEM=$(mmcli -J -L | jq -r '."modem-list"[]') sxmo_log "Dialling USSD: $USSD"
OUTPUT="$(doas -n /usr/libexec/msm-modem-uim-selection ussd initiate "$USSD" 2>/dev/null || /usr/libexec/msm-modem-uim-selection ussd initiate "$USSD" 2>/dev/null || true)"
if [ -z "$OUTPUT" ]; then
DBUS_MODEM="$(mmcli -J -L 2>/dev/null | jq -r '."modem-list"[]' 2>/dev/null | head -n1)"
MM_BUS="org.freedesktop.ModemManager1" MM_BUS="org.freedesktop.ModemManager1"
USSD_IFACE="org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd" USSD_IFACE="org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd"
if [ -n "$DBUS_MODEM" ]; then
sxmo_log "Dialling USSD: $USSD" OUTPUT="$(busctl --json=short call "$MM_BUS" "$DBUS_MODEM" "$USSD_IFACE" Initiate "s" "$USSD" 2>/dev/null | jq -r '.data[]' 2>/dev/null || true)"
OUTPUT=$(busctl --json=short call "$MM_BUS" "$DBUS_MODEM" "$USSD_IFACE" Initiate "s" "$USSD" | jq -r '.data[]') fi
fi
sxmo_terminal.sh sxmo_ussd.sh "$DBUS_MODEM" "Unknown" "$OUTPUT" & if [ -n "$OUTPUT" ]; then
sxmo_notify_user.sh "USSD ($USSD): $OUTPUT"
else
sxmo_notify_user.sh --urgency=critical "USSD request failed ($USSD)"
fi
} }
dial_menu() { dial_menu() {
+174
View File
@@ -0,0 +1,174 @@
#!/bin/sh
# SPDX-License-Identifier: AGPL-3.0-only
# Copyright 2022 Sxmo Contributors
# include common definitions
# shellcheck source=scripts/core/sxmo_common.sh
. sxmo_common.sh
# see networkmanager documentation for these state names
statenumtoname() {
case "$1" in
"int32 -1") printf "failed" ;;
"int32 0") printf "unknown" ;;
"int32 1") printf "initializing" ;;
"int32 2") printf "locked" ;;
"int32 3") printf "disabled" ;;
"int32 4") printf "disabling" ;;
"int32 5") printf "enabling" ;;
"int32 6") printf "enabled" ;;
"int32 7") printf "searching" ;;
"int32 8") printf "registered" ;;
"int32 9") printf "disconnecting" ;;
"int32 10") printf "connecting" ;;
"int32 11") printf "connected" ;;
*) printf "ERROR" ;;
esac
}
mainloop() {
# get initial modem state and handle SIM2/QMI PIN lock or unprovisioned slot
(
tries=0
newstate=""
while [ "$tries" -lt 15 ]; do
newstate="$(mmcli -m any -K 2>/dev/null | grep "^modem.generic.state " | cut -d':' -f2 | sed 's/^ //')"
if [ -n "$newstate" ]; then
break
fi
sleep 2
tries=$((tries + 1))
done
uim_st="$(doas -n /usr/libexec/msm-modem-uim-selection status 2>/dev/null || /usr/libexec/msm-modem-uim-selection status 2>/dev/null || true)"
if printf "%s\n" "$uim_st" | grep -qE "SLOT[12]_PIN1_STATE=(enabled-not-verified|blocked)|SLOT[12]_APP_STATE=(pin1|puk1)"; then
newstate="locked"
elif printf "%s\n" "$uim_st" | grep -qE "SLOT[12]_STATE=present" && printf "%s\n" "$uim_st" | grep -q "^PRIMARY_GW_SLOT=0$"; then
doas -n /usr/libexec/msm-modem-uim-selection boot >/dev/null 2>&1 || true
newstate="$(mmcli -m any -K 2>/dev/null | grep "^modem.generic.state " | cut -d':' -f2 | sed 's/^ //')"
fi
if [ -n "$newstate" ]; then
# fake oldstate (boot) and reason (0)
sxmo_hook_modem.sh "boot" "$newstate" "0"
sxmo_hook_statusbar.sh modem
fi
) &
# Monitor for incoming calls
dbus-monitor --system "interface='org.freedesktop.ModemManager1.Modem.Voice',type='signal',member='CallAdded'" |
while read -r line; do
echo "$line" | grep -qE "^signal" && sxmo_modem.sh checkforincomingcalls
done &
# Monitor for incoming texts
dbus-monitor --system "interface='org.freedesktop.ModemManager1.Modem.Messaging',type='signal',member='Added'" |
while read -r line; do
echo "$line" | grep -qE "^signal" && sxmo_modem.sh checkfornewtexts
done &
# Check for new texts periodically
sxmo_run_aligned.sh 300 sxmo_modem.sh checkfornewtexts &
# Monitor for finished calls
dbus-monitor --system "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.freedesktop.ModemManager1.Call'" |
while read -r line; do
echo "$line" | grep -qE "^signal" && sxmo_modem.sh checkforfinishedcalls
done &
# Periodically remove finished calls
sxmo_run_aligned.sh 300 sxmo_modem.sh checkforfinishedcalls &
# Monitor for modem state change
dbus-monitor --system "interface='org.freedesktop.ModemManager1.Modem',type='signal',member='StateChanged'" |
while read -r line; do
if echo "$line" | grep -qE "^signal.*StateChanged"; then
read -r oldstate
read -r newstate
read -r reason
sxmo_hook_modem.sh "$(statenumtoname "$oldstate")" \
"$(statenumtoname "$newstate")" \
"$(echo "$reason" | sed 's/uint32 //')"
sxmo_hook_statusbar.sh modem
fi
done &
# Monitor for 3GPP USSD
dbus-monitor --system "interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',arg0='org.freedesktop.ModemManager1.Modem.Modem3gpp.Ussd'" |
while read -r line; do
case "$line" in
*"/org/freedesktop/ModemManager1/Modem"*)
MODEM=$(echo "$line" | awk -F'[;=]' '{print $6}')
CHANGED=0
;;
*"string \"NetworkNotification"*)
TYPE="NetworkNotification"
CHANGED=1
;;
*"string \"NetworkRequest"*)
TYPE="NetworkRequest"
CHANGED=1
;;
*)
CHANGED=0
;;
esac
if [ "$CHANGED" = "1" ]; then
# Fetch message (third argument) in sxmo_ussd.sh to save time in this loop
sxmo_terminal.sh sxmo_ussd.sh "$MODEM" "$TYPE" "" &
fi
done &
if [ -f "${SXMO_MMS_BASE_DIR:-"$HOME"/.mms/modemmanager}/mms" ]; then
# monitor for received mms
dbus-monitor "interface='org.ofono.mms.Service',type='signal',member='MessageAdded'" |
while read -r line; do
if echo "$line" | grep -q '^object path'; then
MESSAGE_PATH="$(echo "$line" | cut -d\" -f2)"
fi
if echo "$line" | grep -q 'string "received"'; then
sxmo_mms.sh processmms "$MESSAGE_PATH"
fi
done &
fi
if [ -f "${SXMO_VVM_BASE_DIR:-"$HOME"/.vvm/modemmanager}/vvm" ]; then
# monitor for vvm (Visual Voice Mail)
VVM_START=0
dbus-monitor "interface='org.kop316.vvm.Service',type='signal',member='MessageAdded'" |
while read -r line; do
if echo "$line" | grep -q '^object path'; then
VVM_ID="$(echo "$line" | cut -d\" -f2 | rev | cut -d'/' -f1 | rev)"
VVM_START=1
fi
if echo "$line" | grep -q '^]'; then
VVM_START=0
sxmo_vvm.sh processvvm "$VVM_DATE" "$VVM_SENDER" "$VVM_ID" "$VVM_ATTACHMENT"
fi
if [ "$VVM_START" -eq 1 ]; then
if echo "$line" | grep -q '^string "Date"'; then
read -r line
VVM_DATE="$(echo "$line" | cut -d\" -f2)"
elif echo "$line" | grep -q '^string "Sender"'; then
read -r line
VVM_SENDER="$(echo "$line" | cut -d\" -f2)"
elif echo "$line" | grep -q '^string "Attachments"'; then
read -r line
VVM_ATTACHMENT="$(echo "$line" | cut -d\" -f2)"
fi
fi
done &
fi
wait
}
# new session, clean up all phone related files
rm "$XDG_RUNTIME_DIR/sxmo_calls/"* 2>/dev/null || true
rm -f "$XDG_RUNTIME_DIR"/sxmo.ring.pid 2>/dev/null
rm -f "$SXMO_NOTIFDIR"/incomingcall* 2>/dev/null
mainloop
+55 -35
View File
@@ -12,39 +12,50 @@ SXMO_NOTIFDIR="${SXMO_NOTIFDIR:-${XDG_DATA_HOME:-$HOME/.local/share}/sxmo/notifi
[ -z "$SXMO_NOTIFDIR" ] || [ "$SXMO_NOTIFDIR" = "/" ] && SXMO_NOTIFDIR="$HOME/.local/share/sxmo/notifications" [ -z "$SXMO_NOTIFDIR" ] || [ "$SXMO_NOTIFDIR" = "/" ] && SXMO_NOTIFDIR="$HOME/.local/share/sxmo/notifications"
_clear_notif_group() { _clear_notif_group() {
find "$SXMO_NOTIFDIR" -type f | while read -r file; do find "$SXMO_NOTIFDIR" -type f 2>/dev/null | while read -r file; do
if awk NR==1 "$file" | grep -lxq "$1"; then if [ -f "$file" ] && awk NR==1 "$file" 2>/dev/null | grep -lxq "$1"; then
rm "$file" rm -f "$file"
fi fi
done done
} }
_handle_new_notif_file(){ clear_all() {
file="$1" rm -f "$SXMO_NOTIFDIR"/* 2>/dev/null || true
dunstctl close-all >/dev/null 2>&1 || true
dunstctl history-clear >/dev/null 2>&1 || true
pkill -x dunstify >/dev/null 2>&1 || true
sxmo_hook_statusbar.sh notifications >/dev/null 2>&1 || true
}
if [ "$(wc -l "$file" | cut -d' ' -f1)" -lt 3 ]; then _handle_new_notif_file() {
file="$1"
[ -f "$file" ] || return 0
if [ "$(wc -l < "$file" 2>/dev/null || echo 0)" -lt 3 ]; then
sxmo_log "Invalid notification file $file found, deleting!" sxmo_log "Invalid notification file $file found, deleting!"
rm -f "$file" rm -f "$file"
return return 0
fi fi
sxmo_hook_notification.sh "$file" & sxmo_hook_notification.sh "$file" &
group="$(awk NR==1 "$file")" group="$(awk NR==1 "$file" 2>/dev/null)"
action="$(awk NR==2 "$file")" action="$(awk NR==2 "$file" 2>/dev/null)"
msg="$(tail -n+3 "$file" | cut -c1-70)" msg="$(tail -n+3 "$file" 2>/dev/null | cut -c1-70)"
case "$(dunstify --action="default,Open" "$msg")" in (
case "$(dunstify --action="default,Open" "$msg" 2>/dev/null)" in
"2") "2")
#
# _clear_notif_group "$group"
#
;; ;;
"default") "default")
_clear_notif_group "$group" _clear_notif_group "$group"
sxmo_hook_statusbar.sh notifications >/dev/null 2>&1 || true
if [ -n "$action" ] && [ "$action" != "none" ]; then
setsid -f sh -c "$action" > /dev/null 2>&1 setsid -f sh -c "$action" > /dev/null 2>&1
fi
;; ;;
esac esac
) &
} }
_notifications_hook() { _notifications_hook() {
@@ -58,7 +69,7 @@ _notifications_hook() {
monitor() { monitor() {
mkdir -p "$SXMO_NOTIFDIR" mkdir -p "$SXMO_NOTIFDIR"
find "$SXMO_NOTIFDIR" -type f | while read -r file; do find "$SXMO_NOTIFDIR" -type f 2>/dev/null | while read -r file; do
_handle_new_notif_file "$file" _handle_new_notif_file "$file"
done done
@@ -66,18 +77,18 @@ monitor() {
fifo="$(mktemp -u)" fifo="$(mktemp -u)"
mkfifo "$fifo" mkfifo "$fifo"
inotifywait -mq -e attrib,move,delete "$SXMO_NOTIFDIR" >> "$fifo" & inotifywait -mq -e close_write,moved_to,delete "$SXMO_NOTIFDIR" >> "$fifo" &
inotif_pid=$! inotif_pid=$!
finish() { finish() {
kill "$inotif_pid" kill "$inotif_pid" 2>/dev/null || true
rm "$fifo" rm -f "$fifo"
exit exit
} }
trap 'finish' TERM INT EXIT trap 'finish' TERM INT EXIT
while read -r notif_folder notif_type notif_file; do while read -r notif_folder notif_type notif_file; do
if echo "$notif_type" | grep -qE "CREATE|MOVED_TO|ATTRIB"; then if echo "$notif_type" | grep -qE "CLOSE_WRITE|MOVED_TO"; then
_handle_new_notif_file "$notif_folder$notif_file" _handle_new_notif_file "$notif_folder$notif_file"
fi fi
_notifications_hook _notifications_hook
@@ -87,9 +98,10 @@ monitor() {
} }
_menu_lines() { _menu_lines() {
find "$SXMO_NOTIFDIR" -type f | while read -r file; do find "$SXMO_NOTIFDIR" -type f 2>/dev/null | while read -r file; do
msg="$(tail -n+3 "$file" | tr "\n^" " ")" [ -f "$file" ] || continue
hrandmin="$(stat --printf %y "$file" | grep -oE '[0-9]{2}:[0-9]{2}')" msg="$(tail -n+3 "$file" 2>/dev/null | tr "\n^" " ")"
hrandmin="$(stat -c %y "$file" 2>/dev/null | grep -oE '[0-9]{2}:[0-9]{2}' | head -n1)"
cat <<EOF cat <<EOF
$hrandmin $msg^$file $hrandmin $msg^$file
EOF EOF
@@ -98,9 +110,9 @@ EOF
menu() { menu() {
choices="$(cat <<EOF choices="$(cat <<EOF
$icon_cls Close Menu
$icon_del Clear Notifications $icon_del Clear Notifications
$(_menu_lines) $(_menu_lines)
$icon_cls Close Menu
EOF EOF
)" )"
@@ -115,22 +127,27 @@ EOF
exit exit
;; ;;
"$icon_del Clear Notifications") "$icon_del Clear Notifications")
rm "$SXMO_NOTIFDIR"/* clear_all
# we must update statusbar here because this function depends
# on number of files in $SXMO_NOTIFDIR
sxmo_hook_statusbar.sh notifications
exit exit
;; ;;
*) *)
file="$( file="$(
printf "%b" "$choices" | printf "%b" "$choices" |
grep -F "$picked" | grep -F "$picked" |
cut -d^ -f2 cut -d^ -f2 |
head -n1
)" )"
group="$(awk NR==1 "$file")" if [ -n "$file" ] && [ -f "$file" ]; then
action="$(awk NR==2 "$file")" group="$(awk NR==1 "$file" 2>/dev/null)"
action="$(awk NR==2 "$file" 2>/dev/null)"
_clear_notif_group "$group" _clear_notif_group "$group"
rm -f "$file" 2>/dev/null || true
dunstctl close-all >/dev/null 2>&1 || true
sxmo_hook_statusbar.sh notifications >/dev/null 2>&1 || true
if [ -n "$action" ] && [ "$action" != "none" ]; then
setsid -f sh -c "$action" > /dev/null 2>&1 setsid -f sh -c "$action" > /dev/null 2>&1
fi
fi
;; ;;
esac esac
} }
@@ -167,7 +184,7 @@ new() {
action="$1" action="$1"
msg="$2" msg="$2"
touch "$SXMO_NOTIFDIR/$id" mkdir -p "$SXMO_NOTIFDIR"
printf "%s\n%s\n%b\n" "$group" "$action" "$msg" > "$SXMO_NOTIFDIR/$id" printf "%s\n%s\n%b\n" "$group" "$action" "$msg" > "$SXMO_NOTIFDIR/$id"
sxmo_hook_statusbar.sh notifications sxmo_hook_statusbar.sh notifications
@@ -182,17 +199,20 @@ Subcommands:
Watch for new notification, dispatch libnotify events, handle actions Watch for new notification, dispatch libnotify events, handle actions
menu menu
Open a menu to dismiss notifications Open a menu to dismiss notifications
clear
Clear all notifications and close active Dunst popups
new [-i id] [-g group] <action> <message...> new [-i id] [-g group] <action> <message...>
Register a new notification Register a new notification
EOF EOF
} }
cmd="$1" cmd="$1"
shift shift 2>/dev/null || true
case "$cmd" in case "$cmd" in
monitor) monitor "$@";; monitor) monitor "$@" ;;
menu) menu "$@";; menu) menu "$@" ;;
new) new "$@";; clear) clear_all ;;
new) new "$@" ;;
*) *)
usage usage
exit 1 exit 1
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -8,7 +8,7 @@ if [ -r "$CONF_FILE" ]; then
. "$CONF_FILE" . "$CONF_FILE"
fi fi
SIM_WAIT_TIME="${SIM_WAIT_TIME:-${sim_wait_time:-4}}" SIM_WAIT_TIME="${SIM_WAIT_TIME:-${sim_wait_time:-10}}"
PREFERRED_SIM_SLOT="${PREFERRED_SIM_SLOT:-${preferred_sim_slot:-1}}" PREFERRED_SIM_SLOT="${PREFERRED_SIM_SLOT:-${preferred_sim_slot:-1}}"
QMICLI_MODEM="" QMICLI_MODEM=""
@@ -16,15 +16,25 @@ detect_qmicli_modem() {
if ! command -v qmicli >/dev/null 2>&1; then if ! command -v qmicli >/dev/null 2>&1; then
return 1 return 1
fi fi
if [ -e "/dev/modem" ]; then if qmicli --silent -pd qrtr://0 --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="qmicli --silent -d /dev/modem"
return 0
elif [ -e "/dev/wwan0qmi0" ]; then
QMICLI_MODEM="qmicli --silent -d /dev/wwan0qmi0 --device-open-qmi"
return 0
elif qmicli --silent -pd qrtr://0 --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="qmicli --silent -pd qrtr://0" QMICLI_MODEM="qmicli --silent -pd qrtr://0"
return 0 return 0
elif [ -e "/dev/wwan0qmi0" ]; then
if qmicli --silent -pd /dev/wwan0qmi0 --device-open-qmi --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="qmicli --silent -pd /dev/wwan0qmi0 --device-open-qmi"
return 0
elif qmicli --silent -d /dev/wwan0qmi0 --device-open-qmi --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="qmicli --silent -d /dev/wwan0qmi0 --device-open-qmi"
return 0
fi
elif [ -e "/dev/modem" ]; then
if qmicli --silent -pd /dev/modem --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="qmicli --silent -pd /dev/modem"
return 0
elif qmicli --silent -d /dev/modem --uim-noop >/dev/null 2>&1; then
QMICLI_MODEM="qmicli --silent -d /dev/modem"
return 0
fi
fi fi
return 1 return 1
} }
@@ -64,6 +74,8 @@ parse_uim_card_status() {
pin1_state[s] = "not-initialized" pin1_state[s] = "not-initialized"
pin1_ret[s] = 0 pin1_ret[s] = 0
puk1_ret[s] = 0 puk1_ret[s] = 0
want_aid[s] = 0
need_aid_line[s] = 0
} }
} }
/Primary GW:/ { /Primary GW:/ {
@@ -95,7 +107,7 @@ parse_uim_card_status() {
} }
if (!in_app && $0 ~ /Card state:/) { if (!in_app && $0 ~ /Card state:/) {
line = $0 line = $0
sub(/.*Card state: \x27/, "", line) sub(/.*Card state:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
rawstate[cur_slot] = line rawstate[cur_slot] = line
if (line == "present") cstate[cur_slot] = "present" if (line == "present") cstate[cur_slot] = "present"
@@ -104,25 +116,25 @@ parse_uim_card_status() {
} }
if (!in_app && $0 ~ /UPIN state:/) { if (!in_app && $0 ~ /UPIN state:/) {
line = $0 line = $0
sub(/.*UPIN state: \x27/, "", line) sub(/.*UPIN state:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
upin_state[cur_slot] = line upin_state[cur_slot] = line
} }
if (!in_app && $0 ~ /UPIN retries:/) { if (!in_app && $0 ~ /UPIN retries:/) {
line = $0 line = $0
sub(/.*UPIN retries: \x27/, "", line) sub(/.*UPIN retries:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
upin_ret[cur_slot] = line + 0 upin_ret[cur_slot] = line + 0
} }
if (!in_app && $0 ~ /UPUK retries:/) { if (!in_app && $0 ~ /UPUK retries:/) {
line = $0 line = $0
sub(/.*UPUK retries: \x27/, "", line) sub(/.*UPUK retries:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
upuk_ret[cur_slot] = line + 0 upuk_ret[cur_slot] = line + 0
} }
if (in_app && $0 ~ /Application type:/) { if (in_app && $0 ~ /Application type:/) {
line = $0 line = $0
sub(/.*Application type: \x27/, "", line) sub(/.*Application type:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
if (line ~ /^usim/) { if (line ~ /^usim/) {
app_type[cur_slot] = "usim" app_type[cur_slot] = "usim"
@@ -130,13 +142,16 @@ parse_uim_card_status() {
} else if (line ~ /^sim/ && app_type[cur_slot] != "usim") { } else if (line ~ /^sim/ && app_type[cur_slot] != "usim") {
app_type[cur_slot] = "sim" app_type[cur_slot] = "sim"
want_aid[cur_slot] = 1 want_aid[cur_slot] = 1
} else if (app_type[cur_slot] == "none") {
app_type[cur_slot] = "sim"
want_aid[cur_slot] = 1
} else { } else {
want_aid[cur_slot] = 0 want_aid[cur_slot] = 0
} }
} }
if (in_app && $0 ~ /Application state:/ && want_aid[cur_slot] > 0) { if (in_app && $0 ~ /Application state:/ && want_aid[cur_slot] > 0) {
line = $0 line = $0
sub(/.*Application state: \x27/, "", line) sub(/.*Application state:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
app_state[cur_slot] = line app_state[cur_slot] = line
} }
@@ -147,26 +162,26 @@ parse_uim_card_status() {
if (in_app && need_aid_line[cur_slot] == 1) { if (in_app && need_aid_line[cur_slot] == 1) {
line = $0 line = $0
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line) gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
if (line != "") { if (line != "" && line !~ /:.*\x27/) {
aid[cur_slot] = line aid[cur_slot] = line
} }
need_aid_line[cur_slot] = 0 need_aid_line[cur_slot] = 0
} }
if (in_app && $0 ~ /PIN1 state:/ && want_aid[cur_slot] > 0) { if (in_app && $0 ~ /PIN1 state:/ && want_aid[cur_slot] > 0) {
line = $0 line = $0
sub(/.*PIN1 state: \x27/, "", line) sub(/.*PIN1 state:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
pin1_state[cur_slot] = line pin1_state[cur_slot] = line
} }
if (in_app && $0 ~ /PIN1 retries:/ && want_aid[cur_slot] > 0) { if (in_app && $0 ~ /PIN1 retries:/ && want_aid[cur_slot] > 0) {
line = $0 line = $0
sub(/.*PIN1 retries: \x27/, "", line) sub(/.*PIN1 retries:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
pin1_ret[cur_slot] = line + 0 pin1_ret[cur_slot] = line + 0
} }
if (in_app && $0 ~ /PUK1 retries:/ && want_aid[cur_slot] > 0) { if (in_app && $0 ~ /PUK1 retries:/ && want_aid[cur_slot] > 0) {
line = $0 line = $0
sub(/.*PUK1 retries: \x27/, "", line) sub(/.*PUK1 retries:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line) sub(/\x27.*/, "", line)
puk1_ret[cur_slot] = line + 0 puk1_ret[cur_slot] = line + 0
} }
@@ -192,8 +207,116 @@ parse_uim_card_status() {
' '
} }
parse_nas_serving_system() {
raw_nas="$1"
printf "%s\n" "$raw_nas" | awk '
BEGIN {
reg_state = "unknown"
cs_state = "unknown"
ps_state = "unknown"
rat = ""
mcc = ""
mnc = ""
op_desc = ""
roam = "off"
in_rat = 0
in_plmn = 0
in_roam = 0
}
/^[[:space:]]*Registration state:/ {
line = $0
sub(/.*Registration state:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
reg_state = line
in_rat = 0; in_plmn = 0; in_roam = 0
next
}
/^[[:space:]]*CS:/ {
line = $0
sub(/.*CS:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
cs_state = line
next
}
/^[[:space:]]*PS:/ {
line = $0
sub(/.*PS:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
ps_state = line
next
}
/^[[:space:]]*Radio interfaces:/ {
in_rat = 1; in_plmn = 0; in_roam = 0
next
}
/^[[:space:]]*Data service capabilities:/ {
in_rat = 0
next
}
/^[[:space:]]*Current PLMN:/ {
in_plmn = 1; in_rat = 0; in_roam = 0
next
}
/^[[:space:]]*Roaming indicators:/ {
in_roam = 1; in_plmn = 0; in_rat = 0
next
}
/^[[:space:]]*Default roaming status:/ {
line = $0
sub(/.*Default roaming status:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
roam = line
in_roam = 0
next
}
in_rat && /^[[:space:]]*\[[0-9]+\]:/ {
line = $0
sub(/.*\[[0-9]+\]:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
if (line != "" && line != "none" && rat == "") rat = line
}
in_plmn && /MCC:/ {
line = $0
sub(/.*MCC:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
mcc = line
}
in_plmn && /MNC:/ {
line = $0
sub(/.*MNC:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
mnc = line
}
in_plmn && /Description:/ {
line = $0
sub(/.*Description:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
gsub(/[\r\n\t]/, " ", line)
op_desc = line
in_plmn = 0
}
in_roam && /^[[:space:]]*\[[0-9]+\]:/ {
line = $0
sub(/.*\[[0-9]+\]:[[:space:]]*\x27/, "", line)
sub(/\x27.*/, "", line)
if (line == "on") roam = "on"
}
END {
printf "NAS_REG_STATE=%s\n", reg_state
printf "NAS_CS_STATE=%s\n", cs_state
printf "NAS_PS_STATE=%s\n", ps_state
printf "NAS_RAT=%s\n", rat
printf "NAS_MCC=%s\n", mcc
printf "NAS_MNC=%s\n", mnc
printf "NAS_OPERATOR=%s\n", op_desc
printf "NAS_ROAMING=%s\n", roam
}
'
}
save_preferred_slot() { save_preferred_slot() {
target_slot="$1" target_slot="$1"
PREFERRED_SIM_SLOT="$target_slot"
if [ -f "$CONF_FILE" ]; then if [ -f "$CONF_FILE" ]; then
if grep -q "^preferred_sim_slot=" "$CONF_FILE"; then if grep -q "^preferred_sim_slot=" "$CONF_FILE"; then
sed -i "s/^preferred_sim_slot=.*/preferred_sim_slot=$target_slot/" "$CONF_FILE" sed -i "s/^preferred_sim_slot=.*/preferred_sim_slot=$target_slot/" "$CONF_FILE"
@@ -201,7 +324,7 @@ save_preferred_slot() {
printf "\npreferred_sim_slot=%s\n" "$target_slot" >> "$CONF_FILE" printf "\npreferred_sim_slot=%s\n" "$target_slot" >> "$CONF_FILE"
fi fi
else else
printf "sim_wait_time=4\npreferred_sim_slot=%s\n" "$target_slot" > "$CONF_FILE" printf "sim_wait_time=10\npreferred_sim_slot=%s\n" "$target_slot" > "$CONF_FILE"
fi fi
} }
@@ -214,52 +337,154 @@ provision_slot() {
return 2 return 2
fi fi
$QMICLI_MODEM --dms-set-operating-mode=online >/dev/null 2>&1 || true
QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)" QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)"
count=0 count=0
while ! printf "%s" "$QMI_CARDS" | grep -Fq "Card state: 'present'"; do while : ; do
if [ -n "$QMI_CARDS" ]; then
eval "$(parse_uim_card_status "$QMI_CARDS" "$req_slot")"
if [ "$req_slot" = "2" ] && [ "$SLOT2_STATE" = "present" ] && [ "$SLOT2_APP_TYPE" != "none" ]; then
break
elif [ "$req_slot" != "2" ] && [ "$SLOT1_STATE" = "present" ] && [ "$SLOT1_APP_TYPE" != "none" ]; then
break
elif [ "$count" -ge 3 ] && { { [ "$SLOT1_STATE" = "present" ] && [ "$SLOT1_APP_TYPE" != "none" ]; } || { [ "$SLOT2_STATE" = "present" ] && [ "$SLOT2_APP_TYPE" != "none" ]; }; }; then
break
fi
fi
if [ "$count" -ge "$SIM_WAIT_TIME" ]; then if [ "$count" -ge "$SIM_WAIT_TIME" ]; then
echo "No SIM detected after $SIM_WAIT_TIME seconds." break
return 0
fi fi
sleep 1 sleep 1
count=$((count + 1)) count=$((count + 1))
QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)" QMI_CARDS="$($QMICLI_MODEM --uim-get-card-status 2>/dev/null || true)"
done done
if [ -z "$QMI_CARDS" ]; then
echo "No SIM card status returned."
return 0
fi
eval "$(parse_uim_card_status "$QMI_CARDS" "$req_slot")" eval "$(parse_uim_card_status "$QMI_CARDS" "$req_slot")"
chosen_slot="" chosen_slot=""
chosen_aid="" chosen_aid=""
if [ "$req_slot" = "2" ]; then if [ "$req_slot" = "2" ]; then
if [ "$SLOT2_STATE" = "present" ] && [ -n "$SLOT2_AID" ]; then if [ "$SLOT2_STATE" = "present" ]; then
chosen_slot="2" chosen_slot="2"
chosen_aid="$SLOT2_AID" chosen_aid="$SLOT2_AID"
elif [ "$SLOT1_STATE" = "present" ] && [ -n "$SLOT1_AID" ]; then elif [ "$SLOT1_STATE" = "present" ]; then
chosen_slot="1" chosen_slot="1"
chosen_aid="$SLOT1_AID" chosen_aid="$SLOT1_AID"
fi fi
else else
if [ "$SLOT1_STATE" = "present" ] && [ -n "$SLOT1_AID" ]; then if [ "$SLOT1_STATE" = "present" ]; then
chosen_slot="1" chosen_slot="1"
chosen_aid="$SLOT1_AID" chosen_aid="$SLOT1_AID"
elif [ "$SLOT2_STATE" = "present" ] && [ -n "$SLOT2_AID" ]; then elif [ "$SLOT2_STATE" = "present" ]; then
chosen_slot="2" chosen_slot="2"
chosen_aid="$SLOT2_AID" chosen_aid="$SLOT2_AID"
fi fi
fi fi
if [ -z "$chosen_slot" ] || [ -z "$chosen_aid" ]; then if [ -z "$chosen_slot" ]; then
echo "No valid USIM/SIM application found on any slot." echo "No SIM detected after $SIM_WAIT_TIME seconds."
return 0 return 0
fi fi
if [ "$PRIMARY_GW_SLOT" != "0" ]; then if [ "$chosen_slot" != "$PREFERRED_SIM_SLOT" ]; then
save_preferred_slot "$chosen_slot"
fi
# If already provisioned on the chosen slot and application is ready, keep session intact
eval "chosen_app_state=\$SLOT${chosen_slot}_APP_STATE"
if [ "$PRIMARY_GW_SLOT" = "$chosen_slot" ] && [ "$chosen_app_state" = "ready" ]; then
echo "Slot $chosen_slot already active and ready."
return 0
fi
if [ "$PRIMARY_GW_SLOT" != "0" ] && [ "$PRIMARY_GW_SLOT" != "$chosen_slot" ]; then
$QMICLI_MODEM --uim-change-provisioning-session='activate=no,session-type=primary-gw-provisioning' >/dev/null 2>&1 || true $QMICLI_MODEM --uim-change-provisioning-session='activate=no,session-type=primary-gw-provisioning' >/dev/null 2>&1 || true
fi fi
if [ -n "$chosen_aid" ]; then
echo "Selecting AID $chosen_aid on slot $chosen_slot" echo "Selecting AID $chosen_aid on slot $chosen_slot"
$QMICLI_MODEM --uim-change-provisioning-session="slot=$chosen_slot,activate=yes,session-type=primary-gw-provisioning,aid=$chosen_aid" >/dev/null if ! $QMICLI_MODEM --uim-change-provisioning-session="slot=$chosen_slot,activate=yes,session-type=primary-gw-provisioning,aid=$chosen_aid" >/dev/null 2>&1; then
return $? $QMICLI_MODEM --uim-change-provisioning-session="slot=$chosen_slot,activate=yes,session-type=primary-gw-provisioning" >/dev/null 2>&1 || true
fi
else
echo "Selecting slot $chosen_slot"
$QMICLI_MODEM --uim-change-provisioning-session="slot=$chosen_slot,activate=yes,session-type=primary-gw-provisioning" >/dev/null 2>&1 || true
fi
return 0
}
enable_and_register_modem() {
target_slot="${1:-$PREFERRED_SIM_SLOT}"
if detect_qmicli_modem; then
$QMICLI_MODEM --dms-set-operating-mode=online >/dev/null 2>&1 || true
$QMICLI_MODEM --nas-initiate-network-register >/dev/null 2>&1 || true
fi
i=0
mm_state=""
while [ "$i" -lt 12 ]; do
mm_state="$(mmcli -m any -K 2>/dev/null | awk -F': ' '/^modem\.generic\.state / {print $2; exit}' || true)"
if [ -n "$mm_state" ]; then
break
fi
sleep 1
i=$((i + 1))
done
if [ "$mm_state" = "failed" ]; then
if command -v rc-service >/dev/null 2>&1; then
rc-service modemmanager restart >/dev/null 2>&1 || true
elif command -v systemctl >/dev/null 2>&1; then
systemctl restart ModemManager >/dev/null 2>&1 || true
fi
i=0
mm_state=""
while [ "$i" -lt 12 ]; do
mm_state="$(mmcli -m any -K 2>/dev/null | awk -F': ' '/^modem\.generic\.state / {print $2; exit}' || true)"
if [ -n "$mm_state" ] && [ "$mm_state" != "failed" ]; then
break
fi
sleep 1
i=$((i + 1))
done
fi
if [ -z "$mm_state" ] || [ "$mm_state" = "locked" ]; then
return 0
fi
i=0
while [ "$i" -lt 10 ]; do
mm_state="$(mmcli -m any -K 2>/dev/null | awk -F': ' '/^modem\.generic\.state / {print $2; exit}' || true)"
case "$mm_state" in
disabled)
mmcli -m any -e >/dev/null 2>&1 || true
;;
enabled)
mmcli -m any --3gpp-register-home >/dev/null 2>&1 || true
break
;;
searching|registered|connected|connecting)
break
;;
locked)
break
;;
esac
sleep 1
i=$((i + 1))
done
mmcli -m any --3gpp-register-home >/dev/null 2>&1 || true
if command -v sxmo_hook_statusbar.sh >/dev/null 2>&1; then
DISPLAY="${DISPLAY:-:0}" sxmo_hook_statusbar.sh modem >/dev/null 2>&1 || true
fi
} }
cmd="${1:-boot}" cmd="${1:-boot}"
@@ -276,7 +501,8 @@ case "$cmd" in
;; ;;
esac esac
provision_slot "$PREFERRED_SIM_SLOT" 45 provision_slot "$PREFERRED_SIM_SLOT" 45
exit $? ( enable_and_register_modem "$PREFERRED_SIM_SLOT" ) >/dev/null 2>&1 </dev/null &
exit 0
;; ;;
status) status)
@@ -293,6 +519,10 @@ case "$cmd" in
fi fi
echo "MODEM_AVAILABLE=1" echo "MODEM_AVAILABLE=1"
parse_uim_card_status "$QMI_CARDS" "$PREFERRED_SIM_SLOT" parse_uim_card_status "$QMI_CARDS" "$PREFERRED_SIM_SLOT"
QMI_NAS="$($QMICLI_MODEM --nas-get-serving-system 2>/dev/null || true)"
if [ -n "$QMI_NAS" ]; then
parse_nas_serving_system "$QMI_NAS"
fi
exit 0 exit 0
;; ;;
@@ -303,13 +533,14 @@ case "$cmd" in
exit 1 exit 1
fi fi
save_preferred_slot "$target" save_preferred_slot "$target"
SIM_WAIT_TIME=2 SIM_WAIT_TIME=3
provision_slot "$target" 5 provision_slot "$target" 5
if command -v rc-service >/dev/null 2>&1; then if command -v rc-service >/dev/null 2>&1; then
rc-service modemmanager restart >/dev/null 2>&1 || true rc-service modemmanager restart >/dev/null 2>&1 || true
elif command -v systemctl >/dev/null 2>&1; then elif command -v systemctl >/dev/null 2>&1; then
systemctl restart ModemManager >/dev/null 2>&1 || true systemctl restart ModemManager >/dev/null 2>&1 || true
fi fi
( enable_and_register_modem "$target" ) >/dev/null 2>&1 </dev/null &
exit 0 exit 0
;; ;;
@@ -320,18 +551,27 @@ case "$cmd" in
echo "Missing PIN" >&2 echo "Missing PIN" >&2
exit 1 exit 1
fi fi
verified=0
if detect_qmicli_modem; then
if $QMICLI_MODEM --uim-verify-pin="PIN1,$pin,session-type=card-slot-$slot" >/dev/null 2>&1; then
verified=1
elif $QMICLI_MODEM --uim-verify-pin="PIN1,$pin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
verified=1
fi
fi
if [ "$verified" = "0" ]; then
sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)" sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)"
if [ -n "$sim_id" ] && mmcli -i "$sim_id" --pin="$pin" >/dev/null 2>&1; then if [ -n "$sim_id" ] && mmcli -i "$sim_id" --pin="$pin" >/dev/null 2>&1; then
exit 0 verified=1
fi fi
if detect_qmicli_modem; then
if $QMICLI_MODEM --uim-verify-pin="PIN1,$pin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
exit 0
fi fi
if $QMICLI_MODEM --uim-verify-pin="PIN1,$pin,session-type=card-slot-$slot" >/dev/null 2>&1; then if [ "$verified" = "1" ]; then
save_preferred_slot "$slot"
SIM_WAIT_TIME=2
provision_slot "$slot" 3 >/dev/null 2>&1 || true
( enable_and_register_modem "$slot" ) >/dev/null 2>&1 </dev/null &
exit 0 exit 0
fi fi
fi
exit 1 exit 1
;; ;;
@@ -343,18 +583,27 @@ case "$cmd" in
echo "Missing PUK or new PIN" >&2 echo "Missing PUK or new PIN" >&2
exit 1 exit 1
fi fi
unblocked=0
if detect_qmicli_modem; then
if $QMICLI_MODEM --uim-unblock-pin="PIN1,$puk,$newpin,session-type=card-slot-$slot" >/dev/null 2>&1; then
unblocked=1
elif $QMICLI_MODEM --uim-unblock-pin="PIN1,$puk,$newpin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
unblocked=1
fi
fi
if [ "$unblocked" = "0" ]; then
sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)" sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)"
if [ -n "$sim_id" ] && mmcli -i "$sim_id" --puk="$puk" --pin="$newpin" >/dev/null 2>&1; then if [ -n "$sim_id" ] && mmcli -i "$sim_id" --puk="$puk" --pin="$newpin" >/dev/null 2>&1; then
exit 0 unblocked=1
fi fi
if detect_qmicli_modem; then
if $QMICLI_MODEM --uim-unblock-pin="PIN1,$puk,$newpin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
exit 0
fi fi
if $QMICLI_MODEM --uim-unblock-pin="PIN1,$puk,$newpin,session-type=card-slot-$slot" >/dev/null 2>&1; then if [ "$unblocked" = "1" ]; then
save_preferred_slot "$slot"
SIM_WAIT_TIME=2
provision_slot "$slot" 3 >/dev/null 2>&1 || true
( enable_and_register_modem "$slot" ) >/dev/null 2>&1 </dev/null &
exit 0 exit 0
fi fi
fi
exit 1 exit 1
;; ;;
@@ -366,6 +615,14 @@ case "$cmd" in
echo "Usage: $0 set-pin-lock <1|2> <enable|disable> <pin>" >&2 echo "Usage: $0 set-pin-lock <1|2> <enable|disable> <pin>" >&2
exit 1 exit 1
fi fi
if detect_qmicli_modem; then
if $QMICLI_MODEM --uim-set-pin-protection="PIN1,$mode,$pin,session-type=card-slot-$slot" >/dev/null 2>&1; then
exit 0
fi
if $QMICLI_MODEM --uim-set-pin-protection="PIN1,$mode,$pin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
exit 0
fi
fi
sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)" sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)"
if [ -n "$sim_id" ]; then if [ -n "$sim_id" ]; then
if [ "$mode" = "enable" ] && mmcli -i "$sim_id" --enable-pin --pin="$pin" >/dev/null 2>&1; then if [ "$mode" = "enable" ] && mmcli -i "$sim_id" --enable-pin --pin="$pin" >/dev/null 2>&1; then
@@ -374,14 +631,6 @@ case "$cmd" in
exit 0 exit 0
fi fi
fi fi
if detect_qmicli_modem; then
if $QMICLI_MODEM --uim-set-pin-protection="PIN1,$mode,$pin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
exit 0
fi
if $QMICLI_MODEM --uim-set-pin-protection="PIN1,$mode,$pin,session-type=card-slot-$slot" >/dev/null 2>&1; then
exit 0
fi
fi
exit 1 exit 1
;; ;;
@@ -393,17 +642,17 @@ case "$cmd" in
echo "Usage: $0 change-pin <1|2> <oldpin> <newpin>" >&2 echo "Usage: $0 change-pin <1|2> <oldpin> <newpin>" >&2
exit 1 exit 1
fi fi
sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)"
if [ -n "$sim_id" ] && mmcli -i "$sim_id" --change-pin="$newpin" --pin="$oldpin" >/dev/null 2>&1; then
exit 0
fi
if detect_qmicli_modem; then if detect_qmicli_modem; then
if $QMICLI_MODEM --uim-change-pin="PIN1,$oldpin,$newpin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
exit 0
fi
if $QMICLI_MODEM --uim-change-pin="PIN1,$oldpin,$newpin,session-type=card-slot-$slot" >/dev/null 2>&1; then if $QMICLI_MODEM --uim-change-pin="PIN1,$oldpin,$newpin,session-type=card-slot-$slot" >/dev/null 2>&1; then
exit 0 exit 0
fi fi
if $QMICLI_MODEM --uim-change-pin="PIN1,$oldpin,$newpin,session-type=primary-gw-provisioning" >/dev/null 2>&1; then
exit 0
fi
fi
sim_id="$(mmcli -m any 2>/dev/null | grep -oE 'SIM/[0-9]+' | head -n1 | cut -d'/' -f2 || true)"
if [ -n "$sim_id" ] && mmcli -i "$sim_id" --change-pin="$newpin" --pin="$oldpin" >/dev/null 2>&1; then
exit 0
fi fi
exit 1 exit 1
;; ;;
@@ -505,7 +754,12 @@ case "$cmd" in
exit 1 exit 1
fi fi
resp="" resp=""
for candidate in "$port" /dev/wwan0at1 /dev/wwan0at0; do if [ "$PREFERRED_SIM_SLOT" = "2" ]; then
at_candidates="$port /dev/wwan0at1 /dev/wwan0at0"
else
at_candidates="$port /dev/wwan0at0 /dev/wwan0at1"
fi
for candidate in $at_candidates; do
case "$candidate" in case "$candidate" in
wwan0at0|wwan0at1) wwan0at0|wwan0at1)
dev="/dev/$candidate" dev="/dev/$candidate"
@@ -518,7 +772,7 @@ case "$cmd" in
;; ;;
esac esac
if [ -n "$dev" ] && [ -c "$dev" ]; then if [ -n "$dev" ] && [ -c "$dev" ]; then
resp="$(timeout 2 sh -c ' resp="$(timeout 3 sh -c '
dev="$1" dev="$1"
cmd="$2" cmd="$2"
tmpf="/tmp/at_resp_$$.txt" tmpf="/tmp/at_resp_$$.txt"
@@ -528,7 +782,7 @@ case "$cmd" in
sleep 0.04 sleep 0.04
printf "%s\r\n" "$cmd" > "$dev" 2>/dev/null || true printf "%s\r\n" "$cmd" > "$dev" 2>/dev/null || true
i=0 i=0
while [ "$i" -lt 12 ]; do while [ "$i" -lt 16 ]; do
if grep -qE "(^|[[:space:]])(OK|ERROR|\+CME ERROR|\+CMS ERROR)([[:space:]]|$)" "$tmpf" 2>/dev/null; then if grep -qE "(^|[[:space:]])(OK|ERROR|\+CME ERROR|\+CMS ERROR)([[:space:]]|$)" "$tmpf" 2>/dev/null; then
break break
fi fi
@@ -558,6 +812,84 @@ case "$cmd" in
exit 1 exit 1
;; ;;
ussd)
ussd_mode="${2:-initiate}"
shift 2 2>/dev/null || true
ussd_code="$*"
if [ "$ussd_mode" = "cancel" ]; then
mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1 || true
"$0" at auto 'AT+CUSD=2' >/dev/null 2>&1 || true
echo "USSD session cancelled."
exit 0
fi
if [ -z "$ussd_code" ]; then
echo "ERROR: Empty USSD code"
exit 1
fi
mm_state="$(mmcli -m any -K 2>/dev/null | awk -F': ' '$1 == "modem.generic.state" {print $2; exit}')"
if [ "$mm_state" = "disabled" ] || [ -z "$mm_state" ]; then
mmcli -m any -e >/dev/null 2>&1 || true
sleep 1
fi
if [ "$ussd_mode" = "respond" ]; then
mm_out="$(mmcli -m any --3gpp-ussd-respond="$ussd_code" 2>&1)"
mm_rc=$?
else
mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1 || true
mm_out="$(mmcli -m any --3gpp-ussd-initiate="$ussd_code" 2>&1)"
mm_rc=$?
fi
if [ "$mm_rc" -eq 0 ] && [ -n "$mm_out" ]; then
printf "%s\n" "$mm_out" | sed -e "s/^USSD session initiated; new reply from network: '\(.*\)'$/\1/" -e "s/^USSD response sent; new reply from network: '\(.*\)'$/\1/"
exit 0
fi
# Fallback to direct AT+CUSD on the active SIM AT port
if [ "$PREFERRED_SIM_SLOT" = "2" ]; then
at_candidates="/dev/wwan0at1 /dev/wwan0at0"
else
at_candidates="/dev/wwan0at0 /dev/wwan0at1"
fi
for dev in $at_candidates; do
[ -c "$dev" ] || continue
cusd_out="$(timeout 12 sh -c '
dev="$1"
code="$2"
tmpf="/tmp/ussd_resp_$$.txt"
rm -f "$tmpf"
cat "$dev" > "$tmpf" 2>/dev/null &
rpid=$!
sleep 0.05
printf "AT+CSCS=\"GSM\";+CUSD=1,\"%s\",15\r\n" "$code" > "$dev" 2>/dev/null || true
i=0
while [ "$i" -lt 80 ]; do
if grep -qE "\+CUSD:[[:space:]]*[0-9]+" "$tmpf" 2>/dev/null; then
break
fi
if grep -qE "(^|[[:space:]])(\+CME ERROR|\+CMS ERROR|ERROR)([[:space:]]|$)" "$tmpf" 2>/dev/null; then
break
fi
sleep 0.1
i=$((i + 1))
done
kill "$rpid" 2>/dev/null || true
wait "$rpid" 2>/dev/null || true
tr -d "\r" < "$tmpf" 2>/dev/null || true
rm -f "$tmpf"
' _ "$dev" "$ussd_code" 2>/dev/null || true)"
cusd_msg="$(printf "%s\n" "$cusd_out" | sed -n 's/.*+CUSD:[[:space:]]*[0-9]*,[[:space:]]*"\([^"]*\)".*/\1/p' | tail -n1)"
if [ -n "$cusd_msg" ]; then
printf "%s\n" "$cusd_msg"
exit 0
fi
done
if [ -n "$mm_out" ]; then
printf "%s\n" "$mm_out"
else
echo "ERROR: USSD request failed"
fi
exit 1
;;
sim-contacts-import) sim-contacts-import)
req_slot="${2:-auto}" req_slot="${2:-auto}"
if ! wait_for_qmicli_modem 3; then if ! wait_for_qmicli_modem 3; then
@@ -688,8 +1020,8 @@ case "$cmd" in
# Method 2: Direct QMI UIM EF_ADN (3F00:7F10:6F3A) record fallback # Method 2: Direct QMI UIM EF_ADN (3F00:7F10:6F3A) record fallback
if [ ! -s "$tmp_contacts" ]; then if [ ! -s "$tmp_contacts" ]; then
adn_attr="$($QMICLI_MODEM --uim-get-file-attributes=0x3F00,0x7F10,0x6F3A 2>/dev/null || true)" adn_attr="$($QMICLI_MODEM --uim-get-file-attributes=0x3F00,0x7F10,0x6F3A 2>/dev/null || true)"
reclen="$(printf "%s\n" "$adn_attr" | awk -F"'" '/Record length:/ {print $2 + 0; exit}')" reclen="$(printf "%s\n" "$adn_attr" | awk '/Record (size|length):/ {gsub(/[^0-9]/, "", $NF); print $NF + 0; exit}')"
reccount="$(printf "%s\n" "$adn_attr" | awk -F"'" '/Record count:/ {print $2 + 0; exit}')" reccount="$(printf "%s\n" "$adn_attr" | awk '/Record count:/ {gsub(/[^0-9]/, "", $NF); print $NF + 0; exit}')"
if [ -n "$reclen" ] && [ -n "$reccount" ] && [ "$reclen" -ge 14 ] 2>/dev/null && [ "$reccount" -ge 1 ] 2>/dev/null; then if [ -n "$reclen" ] && [ -n "$reccount" ] && [ "$reclen" -ge 14 ] 2>/dev/null && [ "$reccount" -ge 1 ] 2>/dev/null; then
if [ "$reccount" -gt 250 ]; then if [ "$reccount" -gt 250 ]; then
reccount=250 reccount=250
@@ -8,7 +8,7 @@
horizontal_padding = 6 horizontal_padding = 6
frame_width = 2 frame_width = 2
frame_color = "#005577" frame_color = "#005577"
idle_threshold = 120 idle_threshold = 0
font = Fira Code 9.5 font = Fira Code 9.5
markup = full markup = full
max_icon_size = 24 max_icon_size = 24
@@ -22,16 +22,16 @@
[urgency_low] [urgency_low]
background = "#222222" background = "#222222"
foreground = "#cccccc" foreground = "#cccccc"
timeout = 8 timeout = 5
[urgency_normal] [urgency_normal]
background = "#1c1c1c" background = "#1c1c1c"
foreground = "#ffffff" foreground = "#ffffff"
frame_color = "#005577" frame_color = "#005577"
timeout = 12 timeout = 8
[urgency_critical] [urgency_critical]
background = "#900000" background = "#900000"
foreground = "#ffffff" foreground = "#ffffff"
frame_color = "#ff0000" frame_color = "#ff0000"
timeout = 0 timeout = 10
@@ -15,22 +15,68 @@ newstate="$2"
sxmo_debug "$2 (old: $1 [reason: $3])" sxmo_debug "$2 (old: $1 [reason: $3])"
case "$newstate" in open_sim_unlock_ui() {
"locked") (
sxmo_log "State is locked. Opening SIM PIN/PUK unlock dialog." i=0
while [ "$i" -lt 40 ] && [ ! -p /tmp/dwm_plugin_modem.fifo ]; do
sleep 0.2
i=$((i + 1))
done
if command -v xmodem >/dev/null 2>&1; then if command -v xmodem >/dev/null 2>&1; then
xmodem unlock >/dev/null 2>&1 & DISPLAY="${DISPLAY:-:0}" xmodem unlock >/dev/null 2>&1 &
elif ! pgrep -f "sxmo_unlocksim.sh" >/dev/null; then elif ! pgrep -f "sxmo_unlocksim.sh" >/dev/null; then
sxmo_unlocksim.sh & sxmo_unlocksim.sh &
fi fi
) &
}
case "$newstate" in
"locked")
sxmo_log "State is locked. Opening SIM PIN/PUK unlock dialog."
open_sim_unlock_ui
;;
"failed")
sxmo_log "State is failed. Checking QMI UIM status for SIM2/PIN lock."
(
uim_st="$(doas -n /usr/libexec/msm-modem-uim-selection status 2>/dev/null || /usr/libexec/msm-modem-uim-selection status 2>/dev/null || true)"
if printf "%s\n" "$uim_st" | grep -qE "SLOT[12]_PIN1_STATE=(enabled-not-verified|blocked)|SLOT[12]_APP_STATE=(pin1|puk1)"; then
sxmo_log "QMI UIM reports locked SIM while ModemManager state is failed; opening unlock UI."
open_sim_unlock_ui
elif printf "%s\n" "$uim_st" | grep -qE "SLOT[12]_STATE=present" && printf "%s\n" "$uim_st" | grep -q "^PRIMARY_GW_SLOT=0$"; then
now_ts="$(date +%s 2>/dev/null || echo 0)"
last_ts="$(cat /tmp/sxmo_modem_reprov.stamp 2>/dev/null || echo 0)"
if [ $((now_ts - last_ts)) -ge 25 ]; then
echo "$now_ts" > /tmp/sxmo_modem_reprov.stamp
sxmo_log "Unprovisioned SIM detected in failed state; running msm-modem-uim-selection boot."
doas -n /usr/libexec/msm-modem-uim-selection boot >/dev/null 2>&1 || true
sxmo_hook_statusbar.sh modem >/dev/null 2>&1 || true
fi
fi
) &
;;
"disabled")
sxmo_log "State is disabled. Enabling modem and registering on network."
(
mmcli -m any -e >/dev/null 2>&1 || true
sleep 1
mmcli -m any --3gpp-register-home >/dev/null 2>&1 || true
sxmo_hook_statusbar.sh modem >/dev/null 2>&1 || true
) &
;; ;;
"enabling") "enabling")
sxmo_log "State is enabling. Clearing stale call files." sxmo_log "State is enabling. Clearing stale call files."
rm "$XDG_RUNTIME_DIR"/sxmo_calls/* 2>/dev/null || true rm "$XDG_RUNTIME_DIR"/sxmo_calls/* 2>/dev/null || true
rm -f "$SXMO_NOTIFDIR"/incomingcall* 2>/dev/null || true rm -f "$SXMO_NOTIFDIR"/incomingcall* 2>/dev/null || true
;; ;;
"registered") "enabled")
sxmo_log "State is registered. Checking for calls and messages." sxmo_log "State is enabled. Requesting 3GPP network registration."
(
mmcli -m any --3gpp-register-home >/dev/null 2>&1 || true
sxmo_hook_statusbar.sh modem >/dev/null 2>&1 || true
) &
;;
"registered"|"connected")
sxmo_log "State is $newstate. Checking for calls and messages."
# kill the pin-entry dmenu if still open # kill the pin-entry dmenu if still open
pkill -f sxmo_unlocksim.sh 2>/dev/null || true pkill -f sxmo_unlocksim.sh 2>/dev/null || true
@@ -59,6 +59,13 @@ set_modem() {
MODEMSTATE="$(printf %s "$MMCLI" | jq -r .modem.generic.state)" MODEMSTATE="$(printf %s "$MMCLI" | jq -r .modem.generic.state)"
fi fi
if [ "$MODEMSTATE" = "failed" ] || [ "$MODEMSTATE" = "nomodem" ]; then
uim_st="$(doas -n /usr/libexec/msm-modem-uim-selection status 2>/dev/null || /usr/libexec/msm-modem-uim-selection status 2>/dev/null || true)"
if printf "%s\n" "$uim_st" | grep -qE "SLOT[12]_PIN1_STATE=(enabled-not-verified|blocked)|SLOT[12]_APP_STATE=(pin1|puk1)"; then
MODEMSTATE="locked"
fi
fi
case "$MODEMSTATE" in case "$MODEMSTATE" in
nomodem) nomodem)
fgcolor=red fgcolor=red
@@ -119,7 +126,7 @@ set_modem() {
sxmobar -a -f "$fgcolor" -b "$bgcolor" -t "$style" \ sxmobar -a -f "$fgcolor" -b "$bgcolor" -t "$style" \
modem-state 10 "$MODEMSTATECMP" modem-state 10 "$MODEMSTATECMP"
if [ "$MODEMSTATE" = nomodem ]; then if [ "$MODEMSTATE" = nomodem ] || [ "$MODEMSTATE" = locked ] || [ "$MODEMSTATE" = failed ] || [ "$MODEMSTATE" = unknown ]; then
sxmobar -d modem-tech sxmobar -d modem-tech
sxmobar -d modem-signal sxmobar -d modem-signal
return return
@@ -131,7 +138,7 @@ set_modem() {
style=normal style=normal
# see https://www.freedesktop.org/software/ModemManager/api/latest/ModemManager-Flags-and-Enumerations.html#MMModemAccessTechnology # see https://www.freedesktop.org/software/ModemManager/api/latest/ModemManager-Flags-and-Enumerations.html#MMModemAccessTechnology
USEDTECHS="$(printf %s "$MMCLI" | jq -r '.modem.generic."access-technologies"[]')" USEDTECHS="$(printf %s "$MMCLI" | jq -r '.modem.generic."access-technologies"[]? // empty' 2>/dev/null)"
case "$USEDTECHS" in case "$USEDTECHS" in
*5gnr*) *5gnr*)
MODEMTECHCMP="$icon_modem_fiveg" MODEMTECHCMP="$icon_modem_fiveg"
@@ -178,7 +185,10 @@ set_modem() {
case "$MODEMSTATE" in case "$MODEMSTATE" in
registered|connected|connecting|disconnecting) registered|connected|connecting|disconnecting)
MODEMSIGNAL="$(printf %s "$MMCLI" | jq -r '.modem.generic."signal-quality".value')" MODEMSIGNAL="$(printf %s "$MMCLI" | jq -r '.modem.generic."signal-quality".value // 0' 2>/dev/null)"
case "$MODEMSIGNAL" in
''|*[!0-9]*) MODEMSIGNAL=0 ;;
esac
if [ "$MODEMSIGNAL" -lt 33 ]; then if [ "$MODEMSIGNAL" -lt 33 ]; then
MODEMSIGNALCMP="$icon_modem_signal_1" MODEMSIGNALCMP="$icon_modem_signal_1"
elif [ "$MODEMSIGNAL" -lt 66 ]; then elif [ "$MODEMSIGNAL" -lt 66 ]; then
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+187 -23
View File
@@ -62,12 +62,15 @@ struct SimSlotInfo {
bool needsPin() const { bool needsPin() const {
return state == "present" && return state == "present" &&
(pin1State == "enabled-not-verified" || appState == "pin1"); (pin1State == "enabled-not-verified" ||
appState.rfind("pin1", 0) == 0);
} }
bool needsPuk() const { bool needsPuk() const {
return state == "present" && return state == "present" &&
(pin1State == "blocked" || appState == "puk1"); (pin1State == "blocked" ||
appState.rfind("puk1", 0) == 0 ||
appState == "pin1-blocked");
} }
bool isPinEnabled() const { bool isPinEnabled() const {
@@ -88,6 +91,13 @@ struct ModemStatusData {
std::string mmFailedReason; std::string mmFailedReason;
std::string mmPowerState = "off"; std::string mmPowerState = "off";
std::string mmUnlockRequired; std::string mmUnlockRequired;
std::string mmRegState;
std::string nasRegState;
std::string nasOperator;
std::string nasMcc;
std::string nasMnc;
std::string nasRoaming;
std::string nasRadio;
int mmPinRetries = -1; int mmPinRetries = -1;
int mmPukRetries = -1; int mmPukRetries = -1;
std::string operatorName; std::string operatorName;
@@ -103,6 +113,56 @@ struct ModemStatusData {
std::string simOperatorName; std::string simOperatorName;
std::string currentModes; std::string currentModes;
int modePresetIdx = 0; int modePresetIdx = 0;
bool isNetworkRegistered() const {
return mmRegState == "home" || mmRegState == "roaming" ||
nasRegState == "registered" ||
mmState == "registered" || mmState == "connected";
}
bool isNetworkSearching() const {
return mmRegState == "searching" || nasRegState == "searching" ||
mmState == "searching" || mmState == "enabling" ||
mmState == "connecting";
}
std::string regStatusLabel() const {
if (slots[0].needsPuk() || slots[1].needsPuk() ||
mmUnlockRequired == "sim-puk") {
return "SIM PUK Required";
}
if (slots[0].needsPin() || slots[1].needsPin() ||
mmUnlockRequired == "sim-pin" || mmState == "locked") {
return "SIM PIN Required";
}
if (mmRegState == "home" ||
(nasRegState == "registered" && nasRoaming != "on")) {
return "Registered (Home)";
}
if (mmRegState == "roaming" ||
(nasRegState == "registered" && nasRoaming == "on")) {
return "Registered (Roaming)";
}
if (mmState == "connected" || mmState == "registered") {
return "Registered";
}
if (isNetworkSearching()) {
return "Searching...";
}
if (mmRegState == "denied" || nasRegState == "registration-denied") {
return "Registration Denied";
}
if (slots[0].state != "present" && slots[1].state != "present") {
return "No SIM Inserted";
}
if (primaryGwSlot == 0) {
return "SIM Not Activated";
}
if (mmState == "disabled") {
return "Modem Disabled";
}
return "Not Registered";
}
}; };
struct RadioModePreset { struct RadioModePreset {
@@ -201,6 +261,12 @@ ModemStatusData queryModemAndSimStatus() {
if (!uimKv["PRIMARY_GW_SLOT"].empty()) { if (!uimKv["PRIMARY_GW_SLOT"].empty()) {
d.primaryGwSlot = std::atoi(uimKv["PRIMARY_GW_SLOT"].c_str()); d.primaryGwSlot = std::atoi(uimKv["PRIMARY_GW_SLOT"].c_str());
} }
d.nasRegState = uimKv["NAS_REG_STATE"];
d.nasOperator = uimKv["NAS_OPERATOR"];
d.nasMcc = uimKv["NAS_MCC"];
d.nasMnc = uimKv["NAS_MNC"];
d.nasRoaming = uimKv["NAS_ROAMING"];
d.nasRadio = uimKv["NAS_RADIO"];
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
std::string pfx = "SLOT" + std::to_string(i + 1) + "_"; std::string pfx = "SLOT" + std::to_string(i + 1) + "_";
@@ -244,6 +310,7 @@ ModemStatusData queryModemAndSimStatus() {
d.mmFailedReason = cleanMmVal(mmKv["modem.generic.state-failed-reason"]); d.mmFailedReason = cleanMmVal(mmKv["modem.generic.state-failed-reason"]);
d.mmPowerState = cleanMmVal(mmKv["modem.generic.power-state"]); d.mmPowerState = cleanMmVal(mmKv["modem.generic.power-state"]);
d.mmUnlockRequired = cleanMmVal(mmKv["modem.generic.unlock-required"]); d.mmUnlockRequired = cleanMmVal(mmKv["modem.generic.unlock-required"]);
d.mmRegState = cleanMmVal(mmKv["modem.3gpp.registration-state"]);
d.operatorName = cleanMmVal(mmKv["modem.3gpp.operator-name"]); d.operatorName = cleanMmVal(mmKv["modem.3gpp.operator-name"]);
d.operatorCode = cleanMmVal(mmKv["modem.3gpp.operator-code"]); d.operatorCode = cleanMmVal(mmKv["modem.3gpp.operator-code"]);
d.accessTech = cleanMmVal(mmKv["modem.generic.access-technologies.value[1]"]); d.accessTech = cleanMmVal(mmKv["modem.generic.access-technologies.value[1]"]);
@@ -309,6 +376,16 @@ ModemStatusData queryModemAndSimStatus() {
} }
} }
if (d.operatorName.empty() && !d.nasOperator.empty()) {
d.operatorName = d.nasOperator;
}
if (d.operatorCode.empty() && !d.nasMcc.empty() && !d.nasMnc.empty()) {
d.operatorCode = d.nasMcc + d.nasMnc;
}
if (d.accessTech.empty() && !d.nasRadio.empty() && d.nasRadio != "none") {
d.accessTech = d.nasRadio;
}
// Sync ModemManager lock info into active slot if QMI didn't report retries // Sync ModemManager lock info into active slot if QMI didn't report retries
int actIdx = (d.primaryGwSlot >= 1 && d.primaryGwSlot <= 2) int actIdx = (d.primaryGwSlot >= 1 && d.primaryGwSlot <= 2)
? (d.primaryGwSlot - 1) ? (d.primaryGwSlot - 1)
@@ -395,8 +472,8 @@ int runModemApp(int argc, char** argv) {
return 0; return 0;
} }
writeIntFile(daemonPidPath, static_cast<int>(::getpid()));
int fifoFd = setupPluginDaemonFifo("modem"); int fifoFd = setupPluginDaemonFifo("modem");
writeIntFile(daemonPidPath, static_cast<int>(::getpid()));
struct sigaction sa {}; struct sigaction sa {};
sa.sa_handler = handleSignal; sa.sa_handler = handleSignal;
@@ -702,10 +779,24 @@ int runModemApp(int argc, char** argv) {
line2 = "LOCKED (Enter PIN)"; line2 = "LOCKED (Enter PIN)";
line2Col = &colOrange; line2Col = &colOrange;
} else if (s.isActiveProvisioned) { } else if (s.isActiveProvisioned) {
std::string op = status.operatorName.empty() ? "Ready" : status.operatorName; std::string reg = status.regStatusLabel();
if (!status.ownNumber.empty()) op += " " + status.ownNumber; if (!status.operatorName.empty()) {
line2 = op; reg += " - " + status.operatorName;
}
if (!status.accessTech.empty()) {
reg += " [" + status.accessTech + "]";
}
line2 = reg;
if (status.isNetworkRegistered()) {
line2Col = &colGreen; line2Col = &colGreen;
} else if (status.isNetworkSearching()) {
line2Col = &colOrange;
} else {
line2Col = &colRed;
}
} else if (status.primaryGwSlot == 0) {
line2 = "Present (Unactivated - Press Select)";
line2Col = &colOrange;
} else { } else {
line2 = "Present (Standby " + s.appType + ")"; line2 = "Present (Standby " + s.appType + ")";
line2Col = &colBlue; line2Col = &colBlue;
@@ -726,6 +817,9 @@ int runModemApp(int argc, char** argv) {
else if (pState == "blocked") pState = "PUK Req"; else if (pState == "blocked") pState = "PUK Req";
line3 = pState + " (PIN:" + std::to_string(s.pin1Retries) + line3 = pState + " (PIN:" + std::to_string(s.pin1Retries) +
" PUK:" + std::to_string(s.puk1Retries) + ")"; " PUK:" + std::to_string(s.puk1Retries) + ")";
if (s.isActiveProvisioned && !status.ownNumber.empty()) {
line3 += " " + status.ownNumber;
}
} else { } else {
line3 = "Insert nano-SIM & restart/switch"; line3 = "Insert nano-SIM & restart/switch";
} }
@@ -736,8 +830,18 @@ int runModemApp(int argc, char** argv) {
// Quick Action Rows below the 2 SIM cards (4 rows, 24px each) // Quick Action Rows below the 2 SIM cards (4 rows, 24px each)
int actSlot = (simRowSel == 1) ? 2 : 1; int actSlot = (simRowSel == 1) ? 2 : 1;
int otherSlot = (status.preferredSlot == 1) ? 2 : 1; int otherSlot = (status.preferredSlot == 1) ? 2 : 1;
if (status.primaryGwSlot == 0) {
if (status.slots[1].state == "present" && status.slots[0].state != "present") {
otherSlot = 2;
} else if (status.slots[0].state == "present" && status.slots[1].state != "present") {
otherSlot = 1;
}
}
std::string quickRows[4] = { std::string quickRows[4] = {
std::string(kIconRefresh) + " Switch Active to SIM " + std::to_string(otherSlot), std::string(kIconRefresh) +
(status.primaryGwSlot == 0
? (" Activate SIM " + std::to_string(otherSlot))
: (" Switch Active to SIM " + std::to_string(otherSlot))),
std::string(kIconUnlock) + " Unlock SIM (PIN / PUK)", std::string(kIconUnlock) + " Unlock SIM (PIN / PUK)",
std::string(kIconLock) + " Enable / Disable SIM PIN", std::string(kIconLock) + " Enable / Disable SIM PIN",
std::string(kIconKey) + " Change SIM PIN Code", std::string(kIconKey) + " Change SIM PIN Code",
@@ -762,29 +866,31 @@ int runModemApp(int argc, char** argv) {
XSetForeground(dpy, gc, pt.spec.border); XSetForeground(dpy, gc, pt.spec.border);
XDrawRectangle(dpy, pix, gc, 4, contentY, kWinWidth - 9, 101); XDrawRectangle(dpy, pix, gc, 4, contentY, kWinWidth - 9, 101);
std::string stStr = status.mmPresent ? status.mmState : "nomodem"; std::string regStr = status.regStatusLabel();
if (!status.mmFailedReason.empty()) {
stStr += " (" + status.mmFailedReason + ")";
}
XftColor* stCol = &colRed; XftColor* stCol = &colRed;
if (status.mmState == "connected" || status.mmState == "registered") { if (status.isNetworkRegistered()) {
stCol = &colGreen; stCol = &colGreen;
} else if (status.mmState == "searching" || status.mmState == "connecting") { } else if (status.isNetworkSearching()) {
stCol = &colOrange; stCol = &colOrange;
} }
drawPango(layoutMain, stCol, 10, contentY + 2, 22, drawPango(layoutMain, stCol, 10, contentY + 2, 22,
"State: " + stStr, 185); "Net: " + regStr, 185);
drawSignalBars(kWinWidth - 32, contentY + 6, status.signalQuality); drawSignalBars(kWinWidth - 32, contentY + 6, status.signalQuality);
std::string opLine = "Operator: " + std::string opLine = "Operator: " +
(status.operatorName.empty() ? "--" : status.operatorName); (status.operatorName.empty() ? "--" : status.operatorName);
if (!status.operatorCode.empty() &&
status.operatorName != status.operatorCode) {
opLine += " (" + status.operatorCode + ")";
}
if (!status.accessTech.empty()) { if (!status.accessTech.empty()) {
opLine += " [" + status.accessTech + "]"; opLine += " [" + status.accessTech + "]";
} }
drawPango(layoutSmall, &colLight, 10, contentY + 24, 18, opLine, 220); drawPango(layoutSmall, &colLight, 10, contentY + 24, 18, opLine, 220);
std::string sigLine = "Signal: " + std::to_string(status.signalQuality) + std::string stStr = status.mmPresent ? status.mmState : "nomodem";
"% | Slot: SIM " + std::string sigLine = "Modem: " + stStr + " | Sig: " +
std::to_string(status.signalQuality) + "% | SIM " +
(status.primaryGwSlot > 0 ? std::to_string(status.primaryGwSlot) : "--"); (status.primaryGwSlot > 0 ? std::to_string(status.primaryGwSlot) : "--");
drawPango(layoutSmall, &colLight, 10, contentY + 42, 18, sigLine, 220); drawPango(layoutSmall, &colLight, 10, contentY + 42, 18, sigLine, 220);
@@ -1090,6 +1196,15 @@ int runModemApp(int argc, char** argv) {
}; };
auto showWindow = [&](const std::string& arg = "") { auto showWindow = [&](const std::string& arg = "") {
std::string a = toLowerStr(trimStr(arg));
if (visible &&
(modal == ModalMode::VerifyPin || modal == ModalMode::UnblockPuk) &&
(a == "unlock" || a == "pin" || a == "puk")) {
XRaiseWindow(dpy, win);
grabKeys();
render();
return;
}
closeOtherPlugins("modem"); closeOtherPlugins("modem");
refreshData(true); refreshData(true);
applyOpenArg(arg); applyOpenArg(arg);
@@ -1308,16 +1423,36 @@ int runModemApp(int argc, char** argv) {
auto triggerSimRowAction = [&](int row) { auto triggerSimRowAction = [&](int row) {
if (row == 0 || row == 1) { if (row == 0 || row == 1) {
targetSlot = row + 1; int sNum = row + 1;
const auto& s = status.slots[row];
if (s.needsPin() || s.needsPuk()) {
openPinOrPukModalForSlot(sNum);
} else if (s.state == "present" && !s.isActiveProvisioned) {
switchSimSlotNow(sNum);
} else {
targetSlot = sNum;
actionSel = 0; actionSel = 0;
modal = ModalMode::SimActions; modal = ModalMode::SimActions;
}
} else if (row == 2) { } else if (row == 2) {
int otherSlot = (status.preferredSlot == 1) ? 2 : 1; int otherSlot = (status.preferredSlot == 1) ? 2 : 1;
if (status.primaryGwSlot == 0) {
if (status.slots[1].state == "present" && status.slots[0].state != "present") {
otherSlot = 2;
} else if (status.slots[0].state == "present" && status.slots[1].state != "present") {
otherSlot = 1;
}
}
switchSimSlotNow(otherSlot); switchSimSlotNow(otherSlot);
} else if (row == 3) { } else if (row == 3) {
int slot = (status.primaryGwSlot >= 1 && status.primaryGwSlot <= 2) int slot = (status.primaryGwSlot >= 1 && status.primaryGwSlot <= 2)
? status.primaryGwSlot ? status.primaryGwSlot
: status.preferredSlot; : status.preferredSlot;
if (status.slots[0].needsPin() || status.slots[0].needsPuk()) {
slot = 1;
} else if (status.slots[1].needsPin() || status.slots[1].needsPuk()) {
slot = 2;
}
openPinOrPukModalForSlot(slot); openPinOrPukModalForSlot(slot);
} else if (row == 4) { } else if (row == 4) {
targetSlot = (status.primaryGwSlot >= 1 && status.primaryGwSlot <= 2) targetSlot = (status.primaryGwSlot >= 1 && status.primaryGwSlot <= 2)
@@ -1446,14 +1581,18 @@ int runModemApp(int argc, char** argv) {
ussdResponse.clear(); ussdResponse.clear();
ussdWaitingReply = false; ussdWaitingReply = false;
} else if (radioRowSel == 2) { } else if (radioRowSel == 2) {
bool powerOn = (status.mmPowerState == "on" || bool powerOn = (status.mmPowerState == "on" &&
status.mmState == "registered" || status.mmState != "disabled" &&
status.mmState == "connected" || status.mmState != "failed");
status.mmState == "searching");
setBanner(powerOn ? "Disabling modem..." : "Enabling modem..."); setBanner(powerOn ? "Disabling modem..." : "Enabling modem...");
render(); render();
runCaptureCmd(powerOn ? "mmcli -m any -d >/dev/null 2>&1" if (powerOn) {
: "mmcli -m any -e >/dev/null 2>&1"); runCaptureCmd("mmcli -m any -d >/dev/null 2>&1");
} else {
runCaptureCmd(
"doas -n /usr/libexec/msm-modem-uim-selection boot >/dev/null 2>&1 "
"|| mmcli -m any -e >/dev/null 2>&1");
}
refreshData(false); refreshData(false);
runAsyncCmd("sxmo_hook_statusbar.sh modem >/dev/null 2>&1"); runAsyncCmd("sxmo_hook_statusbar.sh modem >/dev/null 2>&1");
} else if (radioRowSel == 3) { } else if (radioRowSel == 3) {
@@ -1494,6 +1633,9 @@ int runModemApp(int argc, char** argv) {
} }
}; };
int autoCheckStage = isPreload ? 0 : 2;
int64_t nextAutoCheckMs = nowMs() + 2500;
if (!isPreload) { if (!isPreload) {
showWindow(openArg); showWindow(openArg);
} }
@@ -1841,6 +1983,28 @@ int runModemApp(int argc, char** argv) {
timeoutMs = static_cast<int>(rRem); timeoutMs = static_cast<int>(rRem);
} }
} }
} else if (autoCheckStage < 2) {
int64_t aRem = nextAutoCheckMs - now;
if (aRem <= 0) {
++autoCheckStage;
nextAutoCheckMs = now + 4500;
refreshData(false);
if (status.slots[0].needsPin() || status.slots[0].needsPuk() ||
status.slots[1].needsPin() || status.slots[1].needsPuk() ||
status.mmState == "locked") {
autoCheckStage = 2;
showWindow("unlock");
} else if ((status.slots[0].state == "present" ||
status.slots[1].state == "present") &&
(status.primaryGwSlot == 0 ||
status.mmState == "disabled")) {
runAsyncCmd(
"doas -n /usr/libexec/msm-modem-uim-selection boot >/dev/null "
"2>&1");
}
} else if (timeoutMs < 0 || aRem < timeoutMs) {
timeoutMs = static_cast<int>(aRem);
}
} }
struct pollfd pfds[2]; struct pollfd pfds[2];
+15 -14
View File
@@ -183,7 +183,14 @@ inline bool sendPluginCommand(const char* name, const std::string& cmdLine) {
return false; return false;
} }
std::string fifoPath = getPluginFifoPath(name); std::string fifoPath = getPluginFifoPath(name);
int fd = ::open(fifoPath.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC); int fd = -1;
for (int attempt = 0; attempt < 6; ++attempt) {
fd = ::open(fifoPath.c_str(), O_WRONLY | O_NONBLOCK | O_CLOEXEC);
if (fd >= 0) {
break;
}
::usleep(25000);
}
if (fd < 0) { if (fd < 0) {
return false; return false;
} }
@@ -1651,20 +1658,12 @@ inline std::string sendModemUssd(const std::string& input,
if (clean.empty()) return "Empty USSD request."; if (clean.empty()) return "Empty USSD request.";
int rc = 0; int rc = 0;
std::string cmd; std::string mode = (isReply && !isUssdCode(clean)) ? "respond" : "initiate";
if (isReply && !isUssdCode(clean)) { std::string cmd =
cmd = "mmcli -m any --3gpp-ussd-respond=" + shellQuote(clean) + " 2>&1"; "doas -n /usr/libexec/msm-modem-uim-selection ussd " + mode + " " +
} else { shellQuote(clean) + " 2>&1";
cmd = "mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1; "
"mmcli -m any --3gpp-ussd-initiate=" + shellQuote(clean) + " 2>&1";
}
std::string out = trimStr(runCaptureCmd(cmd, &rc)); std::string out = trimStr(runCaptureCmd(cmd, &rc));
size_t q1 = out.find('\'');
size_t q2 = out.rfind('\'');
if (q1 != std::string::npos && q2 != std::string::npos && q2 > q1) {
out = out.substr(q1 + 1, q2 - q1 - 1);
}
if (out.empty()) { if (out.empty()) {
out = (rc == 0) ? "USSD request completed." : "USSD request failed."; out = (rc == 0) ? "USSD request completed." : "USSD request failed.";
} }
@@ -1698,7 +1697,9 @@ inline std::string sendModemUssd(const std::string& input,
} }
inline void cancelModemUssd() { inline void cancelModemUssd() {
runAsyncCmd("mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1"); runAsyncCmd(
"doas -n /usr/libexec/msm-modem-uim-selection ussd cancel >/dev/null 2>&1 "
"|| mmcli -m any --3gpp-ussd-cancel >/dev/null 2>&1");
} }
} // namespace bananarchy } // namespace bananarchy
+107 -24
View File
@@ -816,6 +816,97 @@ write_str_file(const char *path, const char *val)
return n > 0; return n > 0;
} }
static int
dunst_window_visible(void)
{
if (!dpy)
return 0;
Window r_ret, p_ret, *children = NULL;
unsigned int nchildren = 0;
int found = 0;
if (!XQueryTree(dpy, root, &r_ret, &p_ret, &children, &nchildren) || !children)
return 0;
for (unsigned int i = 0; i < nchildren; i++) {
XWindowAttributes wa;
if (!XGetWindowAttributes(dpy, children[i], &wa) || wa.map_state != IsViewable)
continue;
XClassHint ch = { NULL, NULL };
if (XGetClassHint(dpy, children[i], &ch)) {
if ((ch.res_class && strcasecmp(ch.res_class, "Dunst") == 0) ||
(ch.res_name && strcasecmp(ch.res_name, "dunst") == 0))
found = 1;
if (ch.res_name)
XFree(ch.res_name);
if (ch.res_class)
XFree(ch.res_class);
if (found)
break;
}
}
XFree(children);
return found;
}
static void
unmap_dunst_windows(void)
{
if (!dpy)
return;
Window r_ret, p_ret, *children = NULL;
unsigned int nchildren = 0;
int unmapped = 0;
if (!XQueryTree(dpy, root, &r_ret, &p_ret, &children, &nchildren) || !children)
return;
for (unsigned int i = 0; i < nchildren; i++) {
XClassHint ch = { NULL, NULL };
if (XGetClassHint(dpy, children[i], &ch)) {
if ((ch.res_class && strcasecmp(ch.res_class, "Dunst") == 0) ||
(ch.res_name && strcasecmp(ch.res_name, "dunst") == 0)) {
XUnmapWindow(dpy, children[i]);
unmapped = 1;
}
if (ch.res_name)
XFree(ch.res_name);
if (ch.res_class)
XFree(ch.res_class);
}
}
XFree(children);
if (unmapped)
XFlush(dpy);
}
static void
clear_all_notifications_now(void)
{
const char *ndir = getenv("SXMO_NOTIFDIR");
char default_dir[256];
if (!ndir || !ndir[0]) {
const char *home = getenv("HOME");
snprintf(default_dir, sizeof(default_dir), "%s/.local/share/sxmo/notifications",
(home && home[0]) ? home : "/home/user");
ndir = default_dir;
}
DIR *d = opendir(ndir);
if (d) {
struct dirent *ent;
while ((ent = readdir(d)) != NULL) {
if (ent->d_name[0] == '.')
continue;
char fpath[512];
snprintf(fpath, sizeof(fpath), "%s/%s", ndir, ent->d_name);
unlink(fpath);
}
closedir(d);
}
unmap_dunst_windows();
spawn_detached_sh(
"dunstctl close-all >/dev/null 2>&1; "
"dunstctl history-clear >/dev/null 2>&1; "
"pkill -x dunstify >/dev/null 2>&1; "
"sxmo_hook_statusbar.sh notifications >/dev/null 2>&1");
}
static void static void
handle_builtin_toggle(const char *toggle_name) handle_builtin_toggle(const char *toggle_name)
{ {
@@ -914,27 +1005,7 @@ handle_builtin_toggle(const char *toggle_name)
} }
spawn_detached_sh("sxmo_hook_statusbar.sh audio"); spawn_detached_sh("sxmo_hook_statusbar.sh audio");
} else if (strcmp(toggle_name, "notif_clear_all") == 0) { } else if (strcmp(toggle_name, "notif_clear_all") == 0) {
const char *ndir = getenv("SXMO_NOTIFDIR"); clear_all_notifications_now();
char default_dir[256];
if (!ndir || !ndir[0]) {
const char *home = getenv("HOME");
snprintf(default_dir, sizeof(default_dir), "%s/.local/share/sxmo/notifications",
(home && home[0]) ? home : "/home/user");
ndir = default_dir;
}
DIR *d = opendir(ndir);
if (d) {
struct dirent *ent;
while ((ent = readdir(d)) != NULL) {
if (ent->d_name[0] == '.')
continue;
char fpath[512];
snprintf(fpath, sizeof(fpath), "%s/%s", ndir, ent->d_name);
unlink(fpath);
}
closedir(d);
}
spawn_detached_sh("sxmo_hook_statusbar.sh notifs");
} }
} }
@@ -1031,7 +1102,10 @@ activate_selection(unsigned int ev_state)
fclose(fp); fclose(fp);
} }
unlink(fpath); unlink(fpath);
spawn_detached_sh("sxmo_hook_statusbar.sh notifs"); unmap_dunst_windows();
spawn_detached_sh(
"dunstctl close-all >/dev/null 2>&1; "
"sxmo_hook_statusbar.sh notifications >/dev/null 2>&1");
hide_menu(); hide_menu();
menu_mode = MODE_NONE; menu_mode = MODE_NONE;
if (action_cmd[0] && strcmp(action_cmd, "none") != 0) if (action_cmd[0] && strcmp(action_cmd, "none") != 0)
@@ -1040,6 +1114,11 @@ activate_selection(unsigned int ev_state)
} }
if (strncmp(act, "@toggle:", 8) == 0) { if (strncmp(act, "@toggle:", 8) == 0) {
handle_builtin_toggle(act + 8); handle_builtin_toggle(act + 8);
if (strcmp(act + 8, "notif_clear_all") == 0) {
hide_menu();
menu_mode = MODE_NONE;
return;
}
char cur_menu[64]; char cur_menu[64];
snprintf(cur_menu, sizeof(cur_menu), "%s", current_builtin_menu); snprintf(cur_menu, sizeof(cur_menu), "%s", current_builtin_menu);
open_builtin_menu(cur_menu, cur_idx); open_builtin_menu(cur_menu, cur_idx);
@@ -1906,9 +1985,13 @@ open_builtin_menu(const char *menu_name, int keep_idx)
reset_session_defaults(1, "Sys"); reset_session_defaults(1, "Sys");
append_active_call_entries(); append_active_call_entries();
int nnotifs = count_notifications(); int nnotifs = count_notifications();
int dunst_vis = dunst_window_visible();
if (nnotifs > 0) { if (nnotifs > 0) {
snprintf(label, sizeof(label), "%s Notifications (%d)", ICON_BEL, nnotifs); snprintf(label, sizeof(label), "%s Notifications (%d)", ICON_BEL, nnotifs);
add_menu_item(label, "@menu:notifications", 1); add_menu_item(label, "@menu:notifications", 1);
} else if (dunst_vis) {
snprintf(label, sizeof(label), "%s Clear Notification", ICON_BEL);
add_menu_item(label, "@toggle:notif_clear_all", 0);
} }
const char *sh = getenv("SHELL"); const char *sh = getenv("SHELL");
if (!sh || !sh[0]) if (!sh || !sh[0])
@@ -2105,9 +2188,9 @@ open_builtin_menu(const char *menu_name, int keep_idx)
add_menu_item(label, "@close", 0); add_menu_item(label, "@close", 0);
} else if (strcmp(menu_name, "notifications") == 0) { } else if (strcmp(menu_name, "notifications") == 0) {
reset_session_defaults(1, "Notifs"); reset_session_defaults(1, "Notifs");
populate_notifications_menu();
snprintf(label, sizeof(label), "%s Clear All Notifications", ICON_CLS); snprintf(label, sizeof(label), "%s Clear All Notifications", ICON_CLS);
add_menu_item(label, "@toggle:notif_clear_all", 1); add_menu_item(label, "@toggle:notif_clear_all", 0);
populate_notifications_menu();
snprintf(label, sizeof(label), "%s System Menu", ICON_MNU); snprintf(label, sizeof(label), "%s System Menu", ICON_MNU);
add_menu_item(label, "@menu:sys", 1); add_menu_item(label, "@menu:sys", 1);
snprintf(label, sizeof(label), "%s Close Menu", ICON_CLS); snprintf(label, sizeof(label), "%s Close Menu", ICON_CLS);