From 0dfe7b80807e5316060c87d38e1fe7a5f07c084c Mon Sep 17 00:00:00 2001 From: "Robin E. R. Davies" Date: Wed, 20 Nov 2024 14:50:05 -0500 Subject: [PATCH] Use DBUS to detect presence of Wi-Fi devices. --- react/src/PiPedalModel.tsx | 402 +++++++++++++---------------------- react/src/SettingsDialog.tsx | 9 +- src/HotspotManager.cpp | 66 +++++- src/HotspotManager.hpp | 5 + src/PiPedalModel.cpp | 126 ++++++----- src/PiPedalModel.hpp | 7 + src/PiPedalSocket.cpp | 11 + todo.txt | 3 + 8 files changed, 316 insertions(+), 313 deletions(-) diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx index cb37bb8..17facf1 100644 --- a/react/src/PiPedalModel.tsx +++ b/react/src/PiPedalModel.tsx @@ -56,6 +56,18 @@ export enum State { HotspotChanging, }; +function getErrorMessage(error: any) +{ + if (error instanceof Error) + { + return (error as Error).message; + } + if (!error) + { + return ""; + } + return error.toString(); +} class UpdatedError extends Error { }; export function wantsReloadingScreen(state: State) { @@ -742,6 +754,10 @@ export class PiPedalModel //implements PiPedalModel } else if (message === "onNetworkChanging") { this.onNetworkChanging(body as boolean); } + else if (message === "onHasWifiChanged") { + let hasWifi = body as boolean; + this.hasWifiDevice.set(hasWifi); + } } @@ -912,7 +928,7 @@ export class PiPedalModel //implements PiPedalModel } } } - onSocketReconnected() { + async onSocketReconnected(): Promise { this.cancelOnNetworkChanging(); this.cancelAndroidReconnectTimer(); @@ -920,97 +936,20 @@ export class PiPedalModel //implements PiPedalModel this.androidHost?.setDisconnected(false); } - this.expectDisconnect(ReconnectReason.Disconnected); // the next expected disconnect will be an actual disconnect. if (this.visibilityState.get() === VisibilityState.Hidden) return; - + // reload state, but not configuration. - this.getWebSocket().request("hello") - .then(clientId => { - this.clientId = clientId; - return this.getUpdateStatus(); // detects whether server has been upgraded. - }) - .then((updateStatus) => { + this.clientId = await this.getWebSocket().request("hello"); - return this.getWebSocket().request("plugins"); - }) - .then(data => { - this.ui_plugins.set(UiPlugin.deserialize_array(data)); - return this.getWebSocket().request("currentPedalboard"); - }) - .then(data => { - this.setModelPedalboard(new Pedalboard().deserialize(data)); + let newServerVersion = this.serverVersion = await this.getWebSocket().request("version"); + if (newServerVersion.serverVersion !== this.serverVersion.serverVersion) + { + this.reloadPage(); + return; + } - return this.getWebSocket().request("getShowStatusMonitor"); - }) - .then(data => { - this.showStatusMonitor.set(data); - - return this.getWebSocket().request("getWifiConfigSettings"); - }) - .then(data => { - this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data)); - - - return this.getWebSocket().request("getWifiDirectConfigSettings"); - }) - .then(data => { - this.wifiDirectConfigSettings.set(new WifiDirectConfigSettings().deserialize(data)); - - - return this.getWebSocket().request("getGovernorSettings"); - }) - .then(data => { - this.governorSettings.set(new GovernorSettings().deserialize(data)); - - - return this.getWebSocket().request("getJackServerSettings"); - }) - .then(data => { - this.jackServerSettings.set(new JackServerSettings().deserialize(data)); - - return this.getWebSocket().request("getPresets"); - }) - .then((data) => { - this.presets.set(new PresetIndex().deserialize(data)); - - return this.getWebSocket().request("getJackConfiguration"); - }) - .then((data) => { - // data.isValid = false; - // data.errorState = "Jack Audio server not running." - - this.jackConfiguration.set(new JackConfiguration().deserialize(data)); - - return this.getWebSocket().request("getJackSettings"); - }) - .then((data) => { - this.jackSettings.set(new JackChannelSelection().deserialize(data)); - - return this.getWebSocket().request("getBankIndex"); - }) - .then((data) => { - this.banks.set(new BankIndex().deserialize(data)); - - return this.getWebSocket().request("getFavorites"); - }) - .then((data) => { - this.favorites.set(data); - - return this.getWebSocket().request("getSystemMidiBindings"); - }) - .then((data) => { - let bindings = MidiBinding.deserialize_array(data); - this.systemMidiBindings.set(bindings); - - this.setState(State.Ready); - }) - .catch((what) => { - if (what instanceof UpdatedError) { - // do nothing. a page reload is imminent and unavoidable as soon as we return to the dispatcher. - } else { - this.onError(what.toString()); - } - }) + // anything could have changed while we were disconnected. + await this.loadServerState(); } makeSocketServerUrl(hostName: string, port: number): string { return "ws://" + hostName + ":" + port + "/pipedal"; @@ -1026,179 +965,142 @@ export class PiPedalModel //implements PiPedalModel debug: boolean = false; enableAutoUpdate: boolean = false; - requestConfig(): Promise { - const myRequest = new Request(this.varRequest('config.json')); - return fetch(myRequest) - .then( - (response) => { - return response.json(); - } - ) - .then(data => { - this.enableAutoUpdate = !!data.enable_auto_update; - this.hasWifiDevice.set(!!data.has_wifi_device); - if (data.max_upload_size) { - this.maxPresetUploadSize = data.max_upload_size; - } - if (data.fakeAndroid) { - this.androidHost = new FakeAndroidHost(); - } - this.debug = !!data.debug; - let { socket_server_port, socket_server_address, max_upload_size } = data; - if ((!socket_server_address) || socket_server_address === "*") { - socket_server_address = window.location.hostname; - } - if (!socket_server_port) socket_server_port = 8080; - let socket_server = this.makeSocketServerUrl(socket_server_address, socket_server_port); - let var_server_url = this.makeVarServerUrl("http", socket_server_address, socket_server_port); + + async requestConfig(): Promise { + + try { + const myRequest = new Request(this.varRequest('config.json')); + let response: Response = await fetch(myRequest); + let data = await response.json(); + + this.enableAutoUpdate = !!data.enable_auto_update; + this.hasWifiDevice.set(!!data.has_wifi_device); + if (data.max_upload_size) { + this.maxPresetUploadSize = data.max_upload_size; + } + if (data.fakeAndroid) { + this.androidHost = new FakeAndroidHost(); + } + this.debug = !!data.debug; + let { socket_server_port, socket_server_address, max_upload_size } = data; + if ((!socket_server_address) || socket_server_address === "*") { + socket_server_address = window.location.hostname; + } + if (!socket_server_port) socket_server_port = 8080; + let socket_server = this.makeSocketServerUrl(socket_server_address, socket_server_port); + let var_server_url = this.makeVarServerUrl("http", socket_server_address, socket_server_port); + + this.socketServerUrl = socket_server; + this.varServerUrl = var_server_url; + this.maxFileUploadSize = parseInt(max_upload_size); + } catch (error: any) + { + this.setError("Can't connect to server. " + getErrorMessage(error)); + return false; + } + + this.webSocket = new PiPedalSocket( + this.socketServerUrl, + { + onMessageReceived: this.onSocketMessage, + onError: this.onSocketError, + onConnectionLost: this.onSocketConnectionLost, + onReconnect: this.onSocketReconnected, + onReconnecting: this.onSocketReconnecting + } + ); + + try { + await this.webSocket.connect(); + } catch (error) { + this.setError("Failed to connect to server. (" + getErrorMessage(error)); + return false; + + } + try { + let isoFetch = await fetch(new Request('iso_codes.json')); + this.countryCodes = (await isoFetch.json()) as { [Name: string]: string }; - this.socketServerUrl = socket_server; - this.varServerUrl = var_server_url; - this.maxFileUploadSize = parseInt(max_upload_size); - this.webSocket = new PiPedalSocket( - this.socketServerUrl, - { - onMessageReceived: this.onSocketMessage, - onError: this.onSocketError, - onConnectionLost: this.onSocketConnectionLost, - onReconnect: this.onSocketReconnected, - onReconnecting: this.onSocketReconnecting - } - ); - return this.webSocket.connect(); - }) - .catch((error) => { - this.setError("Failed to connect to server. (" + this.socketServerUrl + ")"); - return false; - }) - .then(() => { - return this.getUpdateStatus(); - }) - .then((updateStatus) => { - const isoRequest = new Request('iso_codes.json'); - return fetch(isoRequest); - }) - .then((response) => { - return response.json(); - }) - .then((countryCodes) => { - this.countryCodes = countryCodes as { [Name: string]: string }; + this.clientId = (await this.getWebSocket().request("hello")) as number; - return this.getWebSocket().request("hello"); - }) - .then((clientId) => { - this.clientId = clientId; + this.preloadImages( (await this.getWebSocket().request("imageList"))); + } catch (error) { + this.setError("Failed to establish connection. " + getErrorMessage(error)); + return false; + } + return await this.loadServerState(); + } + async loadServerState() : Promise + { + try + { + this.serverVersion = await this.getWebSocket().request("version"); - return this.getWebSocket().request("imageList"); - }) - .then((data) => { - this.preloadImages(data); - return true; - }) - .catch((error) => { - this.setError("Failed to connect to server. " + error.toString()); - return false; - }) - .then((succeeded) => { - if (!succeeded) return false; - return this.getWebSocket().request("version").then(data => { - this.serverVersion = data; - return this.getWebSocket().request("plugins"); - }) - .then(data => { - this.ui_plugins.set(UiPlugin.deserialize_array(data)); + this.updateStatus.set(new UpdateStatus().deserialize(await this.getUpdateStatus())); - return this.getWebSocket().request("currentPedalboard"); - }) - .then(data => { - this.setModelPedalboard(new Pedalboard().deserialize(data)); + this.hasWifiDevice.set(await this.getWebSocket().request("getHasWifi")); + + this.ui_plugins.set( + UiPlugin.deserialize_array(await this.getWebSocket().request("plugins")) + ); + this.setModelPedalboard( + new Pedalboard().deserialize( + await this.getWebSocket().request("currentPedalboard") + ) + ); + this.plugin_classes.set(new PluginClass().deserialize( + await this.getWebSocket().request("pluginClasses") + )); + this.validatePluginClasses(this.plugin_classes.get()); - return this.getWebSocket().request("pluginClasses"); - }) - .then((data) => { + this.presets.set( + new PresetIndex().deserialize( + await this.getWebSocket().request("getPresets") + ) + ); + this.wifiConfigSettings.set( + new WifiConfigSettings().deserialize( + await this.getWebSocket().request("getWifiConfigSettings") + )); + this.wifiDirectConfigSettings.set( + new WifiDirectConfigSettings().deserialize( + await this.getWebSocket().request("getWifiDirectConfigSettings") + )); + this.governorSettings.set(new GovernorSettings().deserialize( + await this.getWebSocket().request("getGovernorSettings") + )); + this.showStatusMonitor.set( + await this.getWebSocket().request("getShowStatusMonitor") + ); + this.jackServerSettings.set( + new JackServerSettings().deserialize( + await this.getWebSocket().request("getJackServerSettings") + ) + ); + this.jackConfiguration.set(new JackConfiguration().deserialize( + await this.getWebSocket().request("getJackConfiguration") + )); + this.jackSettings.set(new JackChannelSelection().deserialize( + await this.getWebSocket().request("getJackSettings") + )); + this.banks.set(new BankIndex().deserialize(await this.getWebSocket().request("getBankIndex"))); - this.plugin_classes.set(new PluginClass().deserialize(data)); - this.validatePluginClasses(this.plugin_classes.get()); + this.favorites.set(await this.getWebSocket().request("getFavorites")); - return this.getWebSocket().request("getPresets"); - }) - .then((data) => { - this.presets.set(new PresetIndex().deserialize(data)); + this.systemMidiBindings.set(MidiBinding.deserialize_array(await this.getWebSocket().request("getSystemMidiBindings"))); - return this.getWebSocket().request("getWifiConfigSettings"); - }) - .then(data => { - this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data)); + // load at lest once before we allow a reconnect. + this.getWebSocket().canReconnect = true; - return this.getWebSocket().request("getWifiDirectConfigSettings"); - }) - .then(data => { - this.wifiDirectConfigSettings.set(new WifiDirectConfigSettings().deserialize(data)); - - return this.getWebSocket().request("getGovernorSettings"); - }) - .then(data => { - this.governorSettings.set(new GovernorSettings().deserialize(data)); - - return this.getWebSocket().request("getShowStatusMonitor"); - }) - .then(data => { - this.showStatusMonitor.set(data); - - return this.getWebSocket().request("getJackServerSettings"); - }) - .then(data => { - this.jackServerSettings.set(new JackServerSettings().deserialize(data)); - - return this.getWebSocket().request("getJackConfiguration"); - }) - .then((data) => { - // data.isValid = false; - // data.errorState = "Jack Audio server not running." - - this.jackConfiguration.set(new JackConfiguration().deserialize(data)); - - return this.getWebSocket().request("getJackSettings"); - }) - .then((data) => { - this.jackSettings.set(new JackChannelSelection().deserialize(data)); - - return this.getWebSocket().request("getBankIndex"); - }) - .then((data) => { - this.banks.set(new BankIndex().deserialize(data)); - if (this.webSocket) { - // MUST not allow reconnect until at least one complete load has finished. - this.webSocket.canReconnect = true; - } - - return this.getWebSocket().request("getFavorites"); - }) - .then((data) => { - this.favorites.set(data); - - return this.getWebSocket().request("getSystemMidiBindings"); - }) - .then((data) => { - let bindings = MidiBinding.deserialize_array(data); - this.systemMidiBindings.set(bindings); - - if (this.webSocket) { - // MUST not allow reconnect until at least one complete load has finished. - this.webSocket.canReconnect = true; - } - this.setState(State.Ready); - return true; - }) - .catch((error) => { - this.setError("Failed to fetch server state.\n\n" + error.toString()); - return false; - }); - - }) - ; + this.setState(State.Ready); + return true; + } + catch(error) { + this.setError("Failed to fetch server state.\n\n" + getErrorMessage(error)); + return false; + } } diff --git a/react/src/SettingsDialog.tsx b/react/src/SettingsDialog.tsx index fce513f..a08b7b5 100644 --- a/react/src/SettingsDialog.tsx +++ b/react/src/SettingsDialog.tsx @@ -212,9 +212,13 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( this.handleGovernorSettingsChanged = this.handleGovernorSettingsChanged.bind(this); this.handleConnectionStateChanged = this.handleConnectionStateChanged.bind(this); this.handleShowStatusMonitorChanged = this.handleShowStatusMonitorChanged.bind(this); + this.handleHasWifiChanged = this.handleHasWifiChanged.bind(this); } + handleHasWifiChanged(newValue: boolean) { + this.setState({hasWifiDevice: newValue}); + } handleShowStatusMonitorChanged(): void { this.setState({ showStatusMonitor: this.model.showStatusMonitor.get() }); @@ -322,6 +326,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( if (active !== this.active) { this.active = active; if (active) { + this.model.hasWifiDevice.addOnChangedHandler(this.handleHasWifiChanged); this.model.state.addOnChangedHandler(this.handleConnectionStateChanged); this.model.showStatusMonitor.addOnChangedHandler(this.handleShowStatusMonitorChanged); this.model.jackSettings.addOnChangedHandler(this.handleJackSettingsChanged); @@ -350,6 +355,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( this.handleJackServerSettingsChanged(); this.handleWifiConfigSettingsChanged(); this.handleWifiDirectConfigSettingsChanged(); + this.setState({hasWifiDevice: this.model.hasWifiDevice.get()}); + this.timerHandle = setInterval(() => this.tick(), 1000); } else { if (this.timerHandle) { @@ -364,7 +371,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged); this.model.wifiDirectConfigSettings.removeOnChangedHandler(this.handleWifiDirectConfigSettingsChanged); this.model.governorSettings.removeOnChangedHandler(this.handleGovernorSettingsChanged); - + this.model.hasWifiDevice.removeOnChangedHandler(this.handleHasWifiChanged); } } diff --git a/src/HotspotManager.cpp b/src/HotspotManager.cpp index 9bb8134..a6ba8db 100644 --- a/src/HotspotManager.cpp +++ b/src/HotspotManager.cpp @@ -60,6 +60,7 @@ namespace pipedal::impl virtual bool CancelPost(PostHandle handle) override; virtual void SetNetworkChangingListener(NetworkChangingListener &&listener) override; + virtual void SetHasWifiListener(HasWifiListener &&listener) override; private: enum class State @@ -75,6 +76,11 @@ namespace pipedal::impl void SetState(State state); State state = State::Initial; + bool hasWifi = false; + HasWifiListener hasWifiListener; + void SetHasWifi(bool hasWifi); + virtual bool GetHasWifi() override ; + void onClose(); void onError(const std::string &message); void onInitialize(); @@ -224,6 +230,7 @@ void HotspotManagerImpl::onInitialize() } void HotspotManagerImpl::onDisconnect() { + SetHasWifi(false); if (state != State::Initial && state != State::WaitingForNetworkManager) { WaitForNetworkManager(); @@ -242,6 +249,8 @@ void HotspotManagerImpl::UpdateNetworkManagerStatus() auto ethernetDevice = GetDevice(NM_DEVICE_TYPE_ETHERNET); auto wlanDevice = GetDevice(NM_DEVICE_TYPE_WIFI); + SetHasWifi(!!wlanDevice); + if (ethernetDevice && wlanDevice) { if (state == State::Initial || state == State::WaitingForNetworkManager) @@ -291,6 +300,7 @@ void HotspotManagerImpl::ReleaseNetworkManager() this->onDeviceRemovedHandle = INVALID_DBUS_EVENT_HANDLE; this->onDeviceAddedHandle = INVALID_DBUS_EVENT_HANDLE; + SetHasWifi(false); } void HotspotManagerImpl::onDevicesChanged() @@ -401,6 +411,8 @@ void HotspotManagerImpl::onStartMonitoring() OnEthernetStateChanged(ethernetDevice->State()); OnWlanStateChanged(wlanDevice->State()); + SetHasWifi(true); + StartScanTimer(); onAccessPointChanged(); } @@ -863,11 +875,11 @@ void HotspotManagerImpl::StartHotspot() settings["ipv4"]["method"] = "shared"; settings["ipv6"]["method"] = "shared"; settings["ipv6"]["addr-gen-mode"] = 1; // "stable-privacy"; - - //////////////////////////////////////////////////////////////// - // Create connection - settings["ipv4"]["method"] = sdbus::Variant(std::string("shared")); + //////////////////////////////////////////////////////////////// + + // Create connection + settings["ipv4"]["method"] = sdbus::Variant(std::string("shared")); // settings["ipv6"]["method"] = sdbus::Variant(std::string("ignore")); settings["ipv4"]["address-data"] = sdbus::Variant(std::vector>{{{"address", sdbus::Variant("192.168.60.1")}, @@ -1014,6 +1026,32 @@ bool HotspotManagerImpl::CancelPost(PostHandle handle) return dbusDispatcher.CancelPost(handle); } +void HotspotManagerImpl::SetHasWifiListener(HasWifiListener &&listener) +{ + std::lock_guard lock{this->networkChangingListenerMutex}; + this->hasWifiListener = listener; + if (!this->closed && this->hasWifiListener) + { + this->hasWifiListener(this->hasWifi); + } +} +bool HotspotManagerImpl::GetHasWifi() { + std::lock_guard lock{this->networkChangingListenerMutex}; + return hasWifi; +} +void HotspotManagerImpl::SetHasWifi(bool hasWifi) +{ + std::lock_guard lock{this->networkChangingListenerMutex}; + if (hasWifi != this->hasWifi) + { + this->hasWifi = hasWifi; + if (this->hasWifiListener) + { + this->hasWifiListener(this->hasWifi); + } + } +} + void HotspotManagerImpl::SetNetworkChangingListener(NetworkChangingListener &&listener) { std::lock_guard lock{this->networkChangingListenerMutex}; @@ -1034,23 +1072,29 @@ std::vector HotspotManagerImpl::GetKnownWifiNetworks() return this->knownWifiNetworks; } - -static bool is_wireless_sysfs(const std::string& ifname) { +static bool is_wireless_sysfs(const std::string &ifname) +{ return fs::exists("/sys/class/net/" + ifname + "/wireless"); } -static std::vector get_wireless_interfaces_sysfs() { +static std::vector get_wireless_interfaces_sysfs() +{ std::vector wireless_interfaces; const std::string net_path = "/sys/class/net"; - try { - for (const auto& entry : fs::directory_iterator(net_path)) { + try + { + for (const auto &entry : fs::directory_iterator(net_path)) + { std::string ifname = entry.path().filename(); - if (is_wireless_sysfs(ifname)) { + if (is_wireless_sysfs(ifname)) + { wireless_interfaces.push_back(ifname); } } - } catch (const fs::filesystem_error& e) { + } + catch (const fs::filesystem_error &e) + { std::cerr << "Error accessing " << net_path << ": " << e.what() << std::endl; } diff --git a/src/HotspotManager.hpp b/src/HotspotManager.hpp index 1e66f01..7c0d1e3 100644 --- a/src/HotspotManager.hpp +++ b/src/HotspotManager.hpp @@ -55,6 +55,11 @@ namespace pipedal { virtual void SetNetworkChangingListener(NetworkChangingListener &&listener) = 0; + using HasWifiListener = std::function; + virtual void SetHasWifiListener(HasWifiListener &&listener) = 0; + virtual bool GetHasWifi() = 0; + + using PostHandle = uint64_t; using PostCallback = std::function; diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index 010930b..2c307ca 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -99,29 +99,33 @@ PiPedalModel::PiPedalModel() { OnNetworkChanging(ethernetConnected, hotspotEnabling); }); + hotspotManager->SetHasWifiListener( + [this](bool hasWifi) + { + this->SetHasWifi(hasWifi); + }); // don't actuall start the hotspotManager until after LV2 is initialized (in order to avoid logging oddities) } -void PrepareSnapshostsForSave(Pedalboard&pedalboard) +void PrepareSnapshostsForSave(Pedalboard &pedalboard) { - if (pedalboard.selectedSnapshot() != -1) { - auto¤tSnapshot = pedalboard.snapshots()[pedalboard.selectedSnapshot()]; - if (!currentSnapshot || currentSnapshot->isModified_) + if (pedalboard.selectedSnapshot() != -1) + { + auto ¤tSnapshot = pedalboard.snapshots()[pedalboard.selectedSnapshot()]; + if (!currentSnapshot || currentSnapshot->isModified_) { pedalboard.selectedSnapshot(-1); - } } - for (auto &snapshot: pedalboard.snapshots()) + for (auto &snapshot : pedalboard.snapshots()) { - if (snapshot) { + if (snapshot) + { snapshot->isModified_ = false; } } } - - void PiPedalModel::Close() { std::unique_ptr oldAudioHost; @@ -139,8 +143,8 @@ void PiPedalModel::Close() } // take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us) - std::vector t {subscribers.begin(),subscribers.end()}; - for (auto&subscriber: t) + std::vector t{subscribers.begin(), subscribers.end()}; + for (auto &subscriber : t) { subscriber->Close(); } @@ -351,7 +355,8 @@ void PiPedalModel::RemoveNotificationSubsription(std::shared_ptrGetEffect(pedalItemId); - if (!effect) { + if (!effect) + { return; } if (effect->IsVst3()) @@ -384,7 +389,8 @@ void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId) PedalboardItem *item = pedalboard.GetItem(instanceId); if (item != nullptr) { - if (!audioHost) { + if (!audioHost) + { return; } bool changed = this->audioHost->UpdatePluginState(*item); @@ -395,7 +401,7 @@ void PiPedalModel::OnNotifyMaybeLv2StateChanged(uint64_t instanceId) Lv2PluginState newState = item->lv2State(); - FireLv2StateChanged(instanceId,newState); + FireLv2StateChanged(instanceId, newState); } } } @@ -481,7 +487,6 @@ void PiPedalModel::FireJackConfigurationChanged(const JackConfiguration &jackCon { // noify subscribers. - std::vector t{subscribers.begin(), subscribers.end()}; for (auto &subscriber : t) { @@ -531,15 +536,13 @@ void PiPedalModel::SetPedalboard(int64_t clientId, Pedalboard &pedalboard) } } - - void PiPedalModel::SetSnapshot(int64_t selectedSnapshot) { std::lock_guard lock(mutex); if (this->pedalboard.ApplySnapshot(selectedSnapshot, pluginHost)) { this->pedalboard.selectedSnapshot(selectedSnapshot); - for (auto snapshot: this->pedalboard.snapshots()) + for (auto snapshot : this->pedalboard.snapshots()) { if (snapshot) { @@ -554,7 +557,6 @@ void PiPedalModel::SetSnapshots(std::vector> &snapshot { std::lock_guard lock(mutex); - UpdateVst3Settings(pedalboard); this->pedalboard.snapshots(std::move(snapshots)); @@ -562,7 +564,7 @@ void PiPedalModel::SetSnapshots(std::vector> &snapshot if (selectedSnapshot != -1) { this->pedalboard.selectedSnapshot(selectedSnapshot); - for (auto &snapshot: pedalboard.snapshots()) + for (auto &snapshot : pedalboard.snapshots()) { if (snapshot) { @@ -570,12 +572,11 @@ void PiPedalModel::SetSnapshots(std::vector> &snapshot } } } - + this->FirePedalboardChanged(-1, false); // notify clients (but don't change the running pedalboard, because it's still the same) // this means that all clients get an up-to-date copy of the snapshots AND the currently selected snapshot if that applies // (and a fresh copy of the pedalboard settings as well, which is harmless, since they have not changed) - this->SetPresetChanged(-1, true,false); - + this->SetPresetChanged(-1, true, false); } void PiPedalModel::UpdateCurrentPedalboard(int64_t clientId, Pedalboard &pedalboard) @@ -638,12 +639,13 @@ void PiPedalModel::SetPresetChanged(int64_t clientId, bool value, bool changeSna { if (changeSnapshotSelect && value && this->pedalboard.selectedSnapshot() != -1) { - auto & snapshot = this->pedalboard.snapshots()[pedalboard.selectedSnapshot()]; - if (snapshot) { + auto &snapshot = this->pedalboard.snapshots()[pedalboard.selectedSnapshot()]; + if (snapshot) + { if (!snapshot->isModified_) { - snapshot->isModified_ = true; - FireSnapshotModified(pedalboard.selectedSnapshot(),true); + snapshot->isModified_ = true; + FireSnapshotModified(pedalboard.selectedSnapshot(), true); } } @@ -664,10 +666,9 @@ void PiPedalModel::FireSnapshotModified(int64_t snapshotIndex, bool modified) std::vector t{subscribers.begin(), subscribers.end()}; for (auto &subscriber : t) { - subscriber->OnSnapshotModified(snapshotIndex,modified); + subscriber->OnSnapshotModified(snapshotIndex, modified); } } - } void PiPedalModel::FireSelectedSnapshotChanged(int64_t selectedSnapshot) @@ -752,15 +753,15 @@ void PiPedalModel::UpdateVst3Settings(Pedalboard &pedalboard) #endif } -void PiPedalModel::FireLv2StateChanged(int64_t instanceId, const Lv2PluginState &lv2State) { +void PiPedalModel::FireLv2StateChanged(int64_t instanceId, const Lv2PluginState &lv2State) +{ std::lock_guard guard{mutex}; std::vector t{subscribers.begin(), subscribers.end()}; for (auto &subscriber : t) { - subscriber->OnLv2StateChanged(instanceId,lv2State); + subscriber->OnLv2StateChanged(instanceId, lv2State); } - } // referesh the plugin state for all plugins. @@ -774,13 +775,12 @@ bool PiPedalModel::SyncLv2State() { if (audioHost->UpdatePluginState(*item)) { - FireLv2StateChanged(item->instanceId(),item->lv2State()); + FireLv2StateChanged(item->instanceId(), item->lv2State()); changed = true; } } } return changed; - } void PiPedalModel::SaveCurrentPreset(int64_t clientId) { @@ -790,7 +790,6 @@ void PiPedalModel::SaveCurrentPreset(int64_t clientId) SyncLv2State(); PrepareSnapshostsForSave(pedalboard); - storage.SaveCurrentPreset(this->pedalboard); this->SetPresetChanged(clientId, false); } @@ -819,15 +818,14 @@ int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string & return presetId; } - int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &name, int64_t saveAfterInstanceId) { std::lock_guard guard{mutex}; SyncLv2State(); auto pedalboard = this->pedalboard.DeepCopy(); - PrepareSnapshostsForSave(pedalboard); - + PrepareSnapshostsForSave(pedalboard); + UpdateVst3Settings(pedalboard); pedalboard.name(name); int64_t result = storage.SaveCurrentPresetAs(pedalboard, name, saveAfterInstanceId); @@ -873,8 +871,8 @@ void PiPedalModel::NextBank(Direction direction) } size_t index = 0; for (size_t i = 0; i < bankIndex.entries().size(); ++i) - { - auto&entry = bankIndex.entries()[i]; + { + auto &entry = bankIndex.entries()[i]; if (entry.instanceId() == bankIndex.selectedBank()) { index = i; @@ -888,15 +886,19 @@ void PiPedalModel::NextBank(Direction direction) { index = 0; } - } else { + } + else + { if (index == 0) { - index = bankIndex.entries().size()-1; - } else { + index = bankIndex.entries().size() - 1; + } + else + { --index; } } - this->OpenBank(-1,bankIndex.entries()[index].instanceId()); + this->OpenBank(-1, bankIndex.entries()[index].instanceId()); } void PiPedalModel::NextPreset(Direction direction) { @@ -966,11 +968,13 @@ void PiPedalModel::OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest } } -void PiPedalModel::OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex,int64_t snapshotRequestId) +void PiPedalModel::OnNotifyMidiRealtimeSnapshotRequest(int32_t snapshotIndex, int64_t snapshotRequestId) { - try { + try + { SetSnapshot((int64_t)snapshotIndex); - } catch (const std::exception&e) + } + catch (const std::exception &e) { Lv2Log::error(SS("SetSnapshot failed. " << e.what())); } @@ -1006,7 +1010,6 @@ void PiPedalModel::OnNotifyNextMidiBank(const RealtimeNextMidiProgramRequest &re } } - void PiPedalModel::OnNotifyMidiProgramChange(RealtimeMidiProgramRequest &midiProgramRequest) { std::lock_guard guard{mutex}; @@ -2054,7 +2057,9 @@ void PiPedalModel::OnPatchSetReply(uint64_t instanceId, LV2_URID patchSetPropert if (properties.contains(propertyUri) && properties[propertyUri] == atomObject) { // do noting. - } else { + } + else + { properties[propertyUri] = std::move(atomObject); } @@ -2234,7 +2239,6 @@ std::vector PiPedalModel::GetAlsaDevices() result.push_back(MakeDummyDeviceInfo(8)); #endif return result; - } const std::filesystem::path &PiPedalModel::GetWebRoot() const @@ -2617,7 +2621,6 @@ void PiPedalModel::OnNetworkChanged(bool ethernetConnected, bool hotspotConnecte FireNetworkChanged(); } - void PiPedalModel::OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) { try @@ -2698,3 +2701,24 @@ void PiPedalModel::RequestShutdown(bool restart) } } } + +void PiPedalModel::SetHasWifi(bool hasWifi) +{ + std::lock_guard lock(mutex); + if (this->hasWifi != hasWifi) + { + this->hasWifi = hasWifi; + + std::vector t{subscribers.begin(), subscribers.end()}; + + for (auto &subscriber : t) + { + subscriber->OnHasWifiChanged(hasWifi); + } + } +} +bool PiPedalModel::GetHasWifi() +{ + std::lock_guard lock(mutex); + return hasWifi; +} \ No newline at end of file diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index e8963d7..14bd646 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -92,6 +92,7 @@ namespace pipedal virtual void OnErrorMessage(const std::string &message) = 0; virtual void OnLv2PluginsChanging() = 0; virtual void OnNetworkChanging(bool hotspotConnected) = 0; + virtual void OnHasWifiChanged(bool hasWifi) = 0; virtual void Close() = 0; }; @@ -107,6 +108,9 @@ namespace pipedal using NetworkChangedListener = std::function; private: + bool hasWifi = false; + + void SetHasWifi(bool hasWifi); std::unique_ptr hotspotManager; std::unique_ptr updater; @@ -257,6 +261,9 @@ namespace pipedal Increase, Decrease }; + + bool GetHasWifi(); + void NextBank(Direction direction = Direction::Increase); void PreviousBank() { NextBank(Direction::Decrease); } void NextPreset(Direction direction = Direction::Increase); diff --git a/src/PiPedalSocket.cpp b/src/PiPedalSocket.cpp index 612ee1c..6e3b028 100644 --- a/src/PiPedalSocket.cpp +++ b/src/PiPedalSocket.cpp @@ -1080,6 +1080,11 @@ public: UpdateStatus updateStatus = model.GetUpdateStatus(); this->Reply(replyTo, "getUpdateStatus", updateStatus); } + else if (message == "getHasWifi") + { + bool result = model.GetHasWifi(); + this->Reply(replyTo, "getHasWifi",result); + } else if (message == "updateNow") { std::string updateUrl; @@ -1727,6 +1732,12 @@ private: Send("onLv2PluginsChanging", true); Flush(); } + virtual void OnHasWifiChanged(bool hasWifi){ + Send("onHasWifiChanged", hasWifi); + Flush(); + + } + virtual void OnNetworkChanging(bool hotspotConnected) override { try diff --git a/todo.txt b/todo.txt index 3d52fb7..cb32218 100644 --- a/todo.txt +++ b/todo.txt @@ -1,3 +1,6 @@ + +Hotspot monitoring - fails when WiFi network is disabled. + Not all country codes are valid (e.g. Antarctica)