223 lines
4.7 KiB
Go
223 lines
4.7 KiB
Go
package iwd
|
|
|
|
import (
|
|
"log"
|
|
"sync"
|
|
|
|
"github.com/godbus/dbus/v5"
|
|
)
|
|
|
|
// SignalMonitor monitors D-Bus signals from iwd
|
|
type SignalMonitor struct {
|
|
conn *dbus.Conn
|
|
station *Station
|
|
|
|
// Signal channel
|
|
signalChan chan *dbus.Signal
|
|
|
|
// Callbacks
|
|
onStateChange func(state StationState, ssid string)
|
|
onScanComplete func()
|
|
|
|
// Control
|
|
stopChan chan struct{}
|
|
mu sync.RWMutex
|
|
running bool
|
|
|
|
// State tracking
|
|
lastScanning bool
|
|
}
|
|
|
|
// NewSignalMonitor creates a new signal monitor
|
|
func NewSignalMonitor(conn *dbus.Conn, station *Station) *SignalMonitor {
|
|
return &SignalMonitor{
|
|
conn: conn,
|
|
station: station,
|
|
signalChan: make(chan *dbus.Signal, 100),
|
|
stopChan: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
// OnStateChange registers a callback for state changes
|
|
func (sm *SignalMonitor) OnStateChange(callback func(state StationState, ssid string)) {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
sm.onStateChange = callback
|
|
}
|
|
|
|
// OnScanComplete registers a callback for scan completion
|
|
func (sm *SignalMonitor) OnScanComplete(callback func()) {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
sm.onScanComplete = callback
|
|
}
|
|
|
|
// Start begins monitoring D-Bus signals
|
|
func (sm *SignalMonitor) Start() error {
|
|
sm.mu.Lock()
|
|
if sm.running {
|
|
sm.mu.Unlock()
|
|
return nil
|
|
}
|
|
sm.running = true
|
|
sm.mu.Unlock()
|
|
|
|
// Subscribe to PropertiesChanged signals for Station interface
|
|
stationPath := sm.station.GetPath()
|
|
|
|
// Add signal match for PropertiesChanged on Station interface
|
|
matchOptions := []dbus.MatchOption{
|
|
dbus.WithMatchObjectPath(stationPath),
|
|
dbus.WithMatchInterface("org.freedesktop.DBus.Properties"),
|
|
dbus.WithMatchMember("PropertiesChanged"),
|
|
}
|
|
|
|
if err := sm.conn.AddMatchSignal(matchOptions...); err != nil {
|
|
sm.mu.Lock()
|
|
sm.running = false
|
|
sm.mu.Unlock()
|
|
return err
|
|
}
|
|
|
|
// Register signal channel
|
|
sm.conn.Signal(sm.signalChan)
|
|
|
|
// Get initial scanning state
|
|
scanning, err := sm.station.IsScanning()
|
|
if err == nil {
|
|
sm.lastScanning = scanning
|
|
}
|
|
|
|
// Start monitoring goroutine
|
|
go sm.monitor()
|
|
|
|
log.Printf("D-Bus signal monitoring started for station %s", stationPath)
|
|
return nil
|
|
}
|
|
|
|
// Stop stops monitoring D-Bus signals
|
|
func (sm *SignalMonitor) Stop() {
|
|
sm.mu.Lock()
|
|
if !sm.running {
|
|
sm.mu.Unlock()
|
|
return
|
|
}
|
|
sm.running = false
|
|
sm.mu.Unlock()
|
|
|
|
// Signal stop
|
|
close(sm.stopChan)
|
|
|
|
// Remove signal channel
|
|
sm.conn.RemoveSignal(sm.signalChan)
|
|
|
|
log.Printf("D-Bus signal monitoring stopped")
|
|
}
|
|
|
|
// monitor is the main signal processing loop
|
|
func (sm *SignalMonitor) monitor() {
|
|
for {
|
|
select {
|
|
case sig := <-sm.signalChan:
|
|
sm.handleSignal(sig)
|
|
case <-sm.stopChan:
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
// handleSignal processes a D-Bus signal
|
|
func (sm *SignalMonitor) handleSignal(sig *dbus.Signal) {
|
|
// Only process PropertiesChanged signals
|
|
if sig.Name != "org.freedesktop.DBus.Properties.PropertiesChanged" {
|
|
return
|
|
}
|
|
|
|
// Verify signal is from Station interface
|
|
if len(sig.Body) < 2 {
|
|
return
|
|
}
|
|
|
|
interfaceName, ok := sig.Body[0].(string)
|
|
if !ok || interfaceName != StationInterface {
|
|
return
|
|
}
|
|
|
|
// Parse changed properties
|
|
changedProps, ok := sig.Body[1].(map[string]dbus.Variant)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
// Check for State property change
|
|
if stateVariant, ok := changedProps["State"]; ok {
|
|
if state, ok := stateVariant.Value().(string); ok {
|
|
sm.handleStateChange(StationState(state))
|
|
}
|
|
}
|
|
|
|
// Check for Scanning property change
|
|
if scanningVariant, ok := changedProps["Scanning"]; ok {
|
|
if scanning, ok := scanningVariant.Value().(bool); ok {
|
|
sm.handleScanningChange(scanning)
|
|
}
|
|
}
|
|
|
|
// Check for ConnectedNetwork property change
|
|
if _, ok := changedProps["ConnectedNetwork"]; ok {
|
|
// Network connection changed, trigger state update
|
|
sm.handleConnectionChange()
|
|
}
|
|
}
|
|
|
|
// handleStateChange processes a state change
|
|
func (sm *SignalMonitor) handleStateChange(state StationState) {
|
|
sm.mu.RLock()
|
|
callback := sm.onStateChange
|
|
sm.mu.RUnlock()
|
|
|
|
if callback == nil {
|
|
return
|
|
}
|
|
|
|
// Get connected SSID if connected
|
|
ssid := ""
|
|
if state == StateConnected {
|
|
network, err := sm.station.GetConnectedNetwork()
|
|
if err == nil {
|
|
props, err := network.GetProperties()
|
|
if err == nil {
|
|
ssid = props.Name
|
|
}
|
|
}
|
|
}
|
|
|
|
callback(state, ssid)
|
|
}
|
|
|
|
// handleScanningChange processes scanning state changes
|
|
func (sm *SignalMonitor) handleScanningChange(scanning bool) {
|
|
// Detect scan completion (transition from true to false)
|
|
if sm.lastScanning && !scanning {
|
|
sm.mu.RLock()
|
|
callback := sm.onScanComplete
|
|
sm.mu.RUnlock()
|
|
|
|
if callback != nil {
|
|
callback()
|
|
}
|
|
}
|
|
|
|
sm.lastScanning = scanning
|
|
}
|
|
|
|
// handleConnectionChange processes connection changes
|
|
func (sm *SignalMonitor) handleConnectionChange() {
|
|
// Get current state and trigger state change callback
|
|
state, err := sm.station.GetState()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
sm.handleStateChange(state)
|
|
}
|