11#include " wifi.h"
22#include " common/processing.h"
3+ #include " util/stringUtils.h"
34
45#import < CoreWLAN/CoreWLAN.h>
56
@@ -20,7 +21,7 @@ @interface CWInterface()
2021 return CFBridgingRelease (result);
2122}
2223
23- const char * ffDetectWifi (FFlist* result)
24+ static const char * detectWifiByCoreWlan (FFlist* result)
2425{
2526 NSArray <CWInterface*>* interfaces = CWWiFiClient.sharedWiFiClient .interfaces ;
2627 if (!interfaces)
@@ -211,3 +212,104 @@ @interface CWInterface()
211212 }
212213 return NULL ;
213214}
215+
216+ static const char * detectWifiBySystemProfiler (FFlist* result)
217+ {
218+ // Warning: costs about 2s on my machine
219+
220+ FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate ();
221+ if (ffProcessAppendStdOut (&buffer, (char * const []) {
222+ " system_profiler" ,
223+ " SPAirPortDataType" ,
224+ " -xml" ,
225+ " -detailLevel" ,
226+ " basic" ,
227+ NULL
228+ }) != NULL )
229+ return " Starting `system_profiler SPAirPortDataType -xml -detailLevel basic` failed" ;
230+
231+ NSArray * arr = [NSPropertyListSerialization propertyListWithData: [NSData dataWithBytes: buffer.chars length: buffer.length]
232+ options: NSPropertyListImmutable
233+ format: nil
234+ error: nil ];
235+ if (!arr || !arr.count )
236+ return " system_profiler SPAirPortDataType returned an empty array" ;
237+
238+ for (NSDictionary * data in arr[0 ][@" _items" ])
239+ {
240+ for (NSDictionary * inf in data[@" spairport_airport_interfaces" ])
241+ {
242+ if (![inf[@" spairport_wireless_card_type" ] isEqualToString: @" spairport_wireless_card_type_wifi (0x14E4, 0x4387)" ]) continue ;
243+
244+ FFWifiResult* item = (FFWifiResult*)ffListAdd (result);
245+ ffStrbufInitS (&item->inf .description , [inf[@" _name" ] UTF8String ]);
246+ ffStrbufInit (&item->inf .status );
247+ ffStrbufInit (&item->conn .status );
248+ ffStrbufInit (&item->conn .ssid );
249+ ffStrbufInit (&item->conn .bssid );
250+ ffStrbufInit (&item->conn .protocol );
251+ ffStrbufInit (&item->conn .security );
252+ item->conn .signalQuality = 0.0 /0.0 ;
253+ item->conn .rxRate = 0.0 /0.0 ;
254+ item->conn .txRate = 0.0 /0.0 ;
255+
256+ NSString * status = inf[@" spairport_status_information" ]; // spairport_status_connected spairport_status_disassociated spairport_status_off
257+
258+ ffStrbufSetStatic (&item->inf .status , [status isEqualToString: @" spairport_status_off" ] ? " Power Off" : " Power On" );
259+
260+ NSDictionary * station = inf[@" spairport_current_network_information" ];
261+ if (!station) continue ;
262+
263+ ffStrbufSetS (&item->conn .status , status.UTF8String + strlen (" spairport_status_" ));
264+ item->conn .status .chars [0 ] = (char ) toupper (item->conn .status .chars [0 ]);
265+
266+ ffStrbufSetS (&item->conn .ssid , [station[@" _name" ] UTF8String ]);
267+
268+ ffStrbufSetS (&item->conn .protocol , [station[@" spairport_network_phymode" ] UTF8String ]);
269+ if (ffStrbufStartsWithS (&item->conn .protocol , " 802.11" ))
270+ {
271+ const char * subProtocol = item->conn .protocol .chars + strlen (" 802.11" );
272+ if (ffStrEquals (subProtocol, " be" ))
273+ ffStrbufSetStatic (&item->conn .protocol , " 802.11be (Wi-Fi 7)" );
274+ else if (ffStrEquals (subProtocol, " ax" ))
275+ ffStrbufSetStatic (&item->conn .protocol , " 802.11ax (Wi-Fi 6)" );
276+ else if (ffStrEquals (subProtocol, " ac" ))
277+ ffStrbufSetStatic (&item->conn .protocol , " 802.11ac (Wi-Fi 5)" );
278+ else if (ffStrEquals (subProtocol, " n" ))
279+ ffStrbufSetStatic (&item->conn .protocol , " 802.11n (Wi-Fi 4)" );
280+ }
281+
282+ ffStrbufSetS (&item->conn .security , [station[@" spairport_security_mode" ] UTF8String ]);
283+ ffStrbufSubstrAfterFirstS (&item->conn .security , " _mode_" );
284+ if (ffStrbufEqualS (&item->conn .security , " none" ))
285+ ffStrbufSetStatic (&item->conn .security , " Insecure" );
286+ else
287+ {
288+ ffStrbufReplaceAllC (&item->conn .security , ' _' , ' ' );
289+ if (ffStrbufStartsWithS (&item->conn .security , " wpa" ))
290+ {
291+ item->conn .security .chars [0 ] = ' W' ;
292+ item->conn .security .chars [1 ] = ' P' ;
293+ item->conn .security .chars [2 ] = ' A' ;
294+ char * sub = strchr (item->conn .security .chars , ' ' );
295+ if (sub && sub[1 ])
296+ sub[1 ] = (char ) toupper (sub[1 ]);
297+ }
298+ }
299+
300+ double rssiValue = strtod ([station[@" spairport_signal_noise" ] UTF8String ], nil );
301+ item->conn .signalQuality = (double ) (rssiValue >= -50 ? 100 : rssiValue <= -100 ? 0 : (rssiValue + 100 ) * 2 );
302+ item->conn .txRate = (double ) [station[@" spairport_network_rate" ] longValue ];
303+ }
304+ }
305+
306+ return NULL ;
307+ }
308+
309+ const char * ffDetectWifi (FFlist* result)
310+ {
311+ if (@available (macOS 15.0 , *))
312+ return detectWifiBySystemProfiler (result);
313+ else
314+ return detectWifiByCoreWlan (result);
315+ }
0 commit comments