|
2 | 2 | * PROJECT: ReactOS Shell |
3 | 3 | * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) |
4 | 4 | * PURPOSE: ReactOS Wizard for Wireless Network Connections (WLAN Scanning Routines) |
5 | | - * COPYRIGHT: Copyright 2024 Vitaly Orekhov <[email protected]> |
| 5 | + * COPYRIGHT: Copyright 2024-2025 Vitaly Orekhov <[email protected]> |
6 | 6 | */ |
7 | 7 | #include "main.h" |
8 | 8 |
|
9 | | -#include <string> |
10 | | -#include <unordered_set> |
| 9 | +#include <algorithm> |
| 10 | +#include <numeric> |
| 11 | +#include <vector> |
| 12 | + |
| 13 | +/* Convert SSID from UTF-8 to UTF-16 */ |
| 14 | +static ATL::CStringW APNameToUnicode(PDOT11_SSID dot11Ssid) |
| 15 | +{ |
| 16 | + int iSSIDLengthWide = MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<LPCSTR>(dot11Ssid->ucSSID), dot11Ssid->uSSIDLength, NULL, 0); |
| 17 | + |
| 18 | + ATL::CStringW cswSSID = ATL::CStringW(L"", iSSIDLengthWide); |
| 19 | + MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<LPCSTR>(dot11Ssid->ucSSID), dot11Ssid->uSSIDLength, cswSSID.GetBuffer(), iSSIDLengthWide); |
| 20 | + |
| 21 | + return cswSSID; |
| 22 | +} |
| 23 | + |
| 24 | +void CWlanWizard::TryInsertToKnown(std::set<DWORD>& setProfiles, DWORD dwIndex) |
| 25 | +{ |
| 26 | + PWLAN_AVAILABLE_NETWORK pWlanNetwork = &this->lstWlanNetworks->Network[dwIndex]; |
| 27 | + |
| 28 | + if ((pWlanNetwork->dwFlags & WLAN_AVAILABLE_NETWORK_HAS_PROFILE) == WLAN_AVAILABLE_NETWORK_HAS_PROFILE) |
| 29 | + { |
| 30 | + std::wstring_view wsvSSID = APNameToUnicode(&pWlanNetwork->dot11Ssid); |
| 31 | + |
| 32 | + if (wsvSSID == pWlanNetwork->strProfileName) |
| 33 | + setProfiles.insert(dwIndex); |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +void CWlanWizard::TryInsertToAdHoc(std::set<DWORD>& setAdHoc, DWORD dwIndex) |
| 38 | +{ |
| 39 | + PWLAN_AVAILABLE_NETWORK pWlanNetwork = &this->lstWlanNetworks->Network[dwIndex]; |
| 40 | + |
| 41 | + if (pWlanNetwork->dot11BssType == dot11_BSS_type_independent) |
| 42 | + setAdHoc.insert(dwIndex); |
| 43 | +} |
| 44 | + |
| 45 | +DWORD CWlanWizard::TryFindConnected(DWORD dwIndex) |
| 46 | +{ |
| 47 | + PWLAN_AVAILABLE_NETWORK pWlanNetwork = &this->lstWlanNetworks->Network[dwIndex]; |
| 48 | + |
| 49 | + if ((pWlanNetwork->dwFlags & WLAN_AVAILABLE_NETWORK_CONNECTED) == WLAN_AVAILABLE_NETWORK_CONNECTED) |
| 50 | + return dwIndex; |
| 51 | + |
| 52 | + return MAXDWORD; |
| 53 | +} |
11 | 54 |
|
12 | 55 | LRESULT CWlanWizard::OnScanNetworks(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) |
13 | 56 | { |
@@ -95,68 +138,99 @@ LRESULT CWlanWizard::OnScanNetworks(WORD wNotifyCode, WORD wID, HWND hWndCtl, BO |
95 | 138 |
|
96 | 139 | m_SidebarButtonAS.EnableWindow(); |
97 | 140 | m_SidebarButtonSN.EnableWindow(); |
98 | | - |
99 | | - /* |
100 | | - * WIP: Single pass sorting of discovered networks. |
101 | | - * TODO: Restore ad-hoc network positioning in the end of the list. |
102 | | - */ |
| 141 | + |
103 | 142 | if (this->lstWlanNetworks->dwNumberOfItems > 0) |
104 | 143 | { |
105 | | - /* Add discovered networks to listbox and sort networks by signal level */ |
106 | | - std::unordered_set<std::wstring_view> ucAPsWithProfiles; |
107 | | - WLAN_SIGNAL_QUALITY ulPrevSignalQuality = 0, ulWorstSignalQuality = 100; |
108 | | - |
109 | | - for (; |
110 | | - this->lstWlanNetworks->dwIndex <= this->lstWlanNetworks->dwNumberOfItems - 1; |
111 | | - ++this->lstWlanNetworks->dwIndex) |
| 144 | + auto vecIndexesBySignalQuality = std::vector<DWORD>(this->lstWlanNetworks->dwNumberOfItems); |
| 145 | + DWORD dwConnectedTo = MAXDWORD; |
| 146 | + std::set<DWORD> setDiscoveredAdHocIndexes; |
| 147 | + std::set<DWORD> setAPsWithProfiles; |
| 148 | + std::iota(vecIndexesBySignalQuality.begin(), vecIndexesBySignalQuality.end(), 0); |
| 149 | + |
| 150 | + /* Sort networks by signal level */ |
| 151 | + std::sort(vecIndexesBySignalQuality.begin(), vecIndexesBySignalQuality.end(), [&](auto left, auto right) |
| 152 | + { |
| 153 | + TryInsertToAdHoc(setDiscoveredAdHocIndexes, left); |
| 154 | + TryInsertToAdHoc(setDiscoveredAdHocIndexes, right); |
| 155 | + |
| 156 | + /* Try to determine if we are connected currently to anything. |
| 157 | + * Once found, these two steps are skipped. */ |
| 158 | + if (dwConnectedTo == MAXDWORD) |
| 159 | + dwConnectedTo = TryFindConnected(left); |
| 160 | + |
| 161 | + if (dwConnectedTo == MAXDWORD) |
| 162 | + dwConnectedTo = TryFindConnected(right); |
| 163 | + |
| 164 | + /* Count network as known if it fully matches SSID with profile name */ |
| 165 | + TryInsertToKnown(setAPsWithProfiles, left); |
| 166 | + TryInsertToKnown(setAPsWithProfiles, right); |
| 167 | + |
| 168 | + return this->lstWlanNetworks->Network[left].wlanSignalQuality > this->lstWlanNetworks->Network[right].wlanSignalQuality; |
| 169 | + }); |
| 170 | + |
| 171 | + /* Remove networks that do not have profile name exactly matching SSID */ |
| 172 | + for (const auto& dwKnownAPIdx : setAPsWithProfiles) |
112 | 173 | { |
113 | | - WLAN_AVAILABLE_NETWORK wlanNetwork = this->lstWlanNetworks->Network[this->lstWlanNetworks->dwIndex]; |
114 | | - std::wstring_view wsvSSID; |
115 | | - int iInsertAt = -1; |
| 174 | + PWLAN_AVAILABLE_NETWORK wlanNetWithProfile = &this->lstWlanNetworks->Network[dwKnownAPIdx]; |
| 175 | + |
| 176 | + vecIndexesBySignalQuality.erase(std::remove_if(vecIndexesBySignalQuality.begin(), vecIndexesBySignalQuality.end(), [&](const DWORD& dwAP) |
| 177 | + { |
| 178 | + if (dwKnownAPIdx == dwAP) |
| 179 | + return false; |
116 | 180 |
|
117 | | - // Convert SSID from UTF-8 to UTF-16 |
118 | | - int iSSIDLengthWide = MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<LPCSTR>(wlanNetwork.dot11Ssid.ucSSID), wlanNetwork.dot11Ssid.uSSIDLength, NULL, 0); |
| 181 | + bool bProfileNameIsSSID = ATL::CStringW(wlanNetWithProfile->strProfileName) == APNameToUnicode(&this->lstWlanNetworks->Network[dwAP].dot11Ssid); |
| 182 | + bool bHasProfile = (this->lstWlanNetworks->Network[dwAP].dwFlags & WLAN_AVAILABLE_NETWORK_HAS_PROFILE) != 0; |
| 183 | + |
| 184 | + return bProfileNameIsSSID && !bHasProfile; |
| 185 | + }), vecIndexesBySignalQuality.end()); |
| 186 | + } |
| 187 | + |
| 188 | + DPRINT("Discovered %lu access points (%u are known)\n", this->lstWlanNetworks->dwNumberOfItems, setAPsWithProfiles.size()); |
| 189 | + |
| 190 | + /* Shift all ad hoc networks to end */ |
| 191 | + for (const auto& dwAdHocIdx : setDiscoveredAdHocIndexes) |
| 192 | + { |
| 193 | + auto iter = std::find(vecIndexesBySignalQuality.begin(), vecIndexesBySignalQuality.end(), dwAdHocIdx); |
119 | 194 |
|
120 | | - ATL::CStringW cswSSID = ATL::CStringW(L"", iSSIDLengthWide); |
121 | | - MultiByteToWideChar(CP_UTF8, 0, reinterpret_cast<LPCSTR>(wlanNetwork.dot11Ssid.ucSSID), wlanNetwork.dot11Ssid.uSSIDLength, cswSSID.GetBuffer(), iSSIDLengthWide); |
122 | | - wsvSSID = std::wstring_view(cswSSID.GetBuffer()); |
| 195 | + if (iter != vecIndexesBySignalQuality.end()) |
| 196 | + { |
| 197 | + auto idx = iter - vecIndexesBySignalQuality.begin(); |
| 198 | + std::rotate(vecIndexesBySignalQuality.begin() + idx, vecIndexesBySignalQuality.begin() + idx + 1, vecIndexesBySignalQuality.end()); |
| 199 | + } |
| 200 | + } |
123 | 201 |
|
124 | | - // TODO: filter access points by profile |
125 | | - if (ucAPsWithProfiles.find(wsvSSID) != ucAPsWithProfiles.end()) |
126 | | - continue; |
| 202 | + /* Finally, move currently connected network to beginning */ |
| 203 | + if (dwConnectedTo != MAXDWORD) |
| 204 | + { |
| 205 | + auto connectedIdx = std::find(vecIndexesBySignalQuality.begin(), vecIndexesBySignalQuality.end(), dwConnectedTo) - vecIndexesBySignalQuality.begin(); |
127 | 206 |
|
128 | | - /* This allows discarding duplicated entries that with APs we had already connected earlier */ |
129 | | - if (wlanNetwork.dwFlags & WLAN_AVAILABLE_NETWORK_HAS_PROFILE) |
130 | | - ucAPsWithProfiles.emplace(wsvSSID); |
| 207 | + if (connectedIdx > 0) |
| 208 | + { |
| 209 | + auto iter = vecIndexesBySignalQuality.begin(); |
| 210 | + std::rotate(iter, iter + connectedIdx, iter + connectedIdx + 1); |
| 211 | + } |
| 212 | + } |
131 | 213 |
|
132 | | - /* Sort by descending signal order */ |
133 | | - if (this->lstWlanNetworks->dwIndex == 0 || wlanNetwork.wlanSignalQuality > ulPrevSignalQuality) |
134 | | - iInsertAt = 0; |
135 | | - else if (wlanNetwork.wlanSignalQuality < ulPrevSignalQuality) |
136 | | - iInsertAt = 1; |
| 214 | + for (const auto& dwNetwork : vecIndexesBySignalQuality) |
| 215 | + { |
| 216 | + WLAN_AVAILABLE_NETWORK wlanNetwork = this->lstWlanNetworks->Network[dwNetwork]; |
| 217 | + ATL::CStringW cswSSID = APNameToUnicode(&wlanNetwork.dot11Ssid); |
137 | 218 |
|
138 | 219 | if (cswSSID.IsEmpty()) |
139 | 220 | cswSSID.LoadStringW(IDS_WLANWIZ_HIDDEN_NETWORK); |
140 | 221 |
|
141 | 222 | LRESULT iItemIdx = m_ListboxWLAN.SendMessageW(LB_INSERTSTRING, |
142 | | - iInsertAt, |
| 223 | + -1, |
143 | 224 | reinterpret_cast<LPARAM>(cswSSID.GetBuffer())); |
144 | 225 |
|
145 | 226 | m_ListboxWLAN.SendMessageW(LB_SETITEMDATA, |
146 | 227 | iItemIdx, |
147 | | - static_cast<LPARAM>(this->lstWlanNetworks->dwIndex)); |
148 | | - |
149 | | - ulPrevSignalQuality = wlanNetwork.wlanSignalQuality; |
150 | | - |
151 | | - if (ulPrevSignalQuality < ulWorstSignalQuality) |
152 | | - ulWorstSignalQuality = ulPrevSignalQuality; |
| 228 | + static_cast<LPARAM>(dwNetwork)); |
153 | 229 | } |
154 | 230 | } |
155 | 231 |
|
156 | 232 | /* Show discovered networks */ |
157 | | - RECT rc; |
158 | 233 | m_ListboxWLAN.EnableWindow(); |
159 | | - m_ListboxWLAN.GetClientRect(&rc); |
160 | 234 | m_ListboxWLAN.Invalidate(); |
161 | 235 | m_ListboxWLAN.SetFocus(); |
162 | 236 |
|
|
0 commit comments