((resolve, reject) => {
@@ -1939,24 +2014,20 @@ class PiPedalModelImpl implements PiPedalModel {
this.zoomedUiControl.set(undefined);
}
- setFavorite(pluginUrl: string, isFavorite: boolean): void
- {
+ setFavorite(pluginUrl: string, isFavorite: boolean): void {
let favorites = this.favorites.get();
let newFavorites: FavoritesList = {};
- Object.assign(newFavorites,favorites);
- if (isFavorite)
- {
+ Object.assign(newFavorites, favorites);
+ if (isFavorite) {
newFavorites[pluginUrl] = true;
} else {
- if (newFavorites[pluginUrl])
- {
+ if (newFavorites[pluginUrl]) {
delete newFavorites[pluginUrl];
}
}
this.favorites.set(newFavorites);
- if (this.webSocket)
- {
- this.webSocket.send("setFavorites",newFavorites);
+ if (this.webSocket) {
+ this.webSocket.send("setFavorites", newFavorites);
}
// stub: update server.
}
@@ -1971,6 +2042,21 @@ class PiPedalModelImpl implements PiPedalModel {
}
}
+ isAndroidHosted(): boolean { return this.androidHost !== undefined; }
+
+ getAndroidHostVersion(): string {
+ if (this.androidHost) {
+ return this.androidHost.getHostVersion();
+ }
+ return "";
+ }
+ chooseNewDevice(): void {
+ if (this.androidHost) {
+ return this.androidHost.chooseNewDevice();
+ }
+
+ }
+
};
let instance: PiPedalModel | undefined = undefined;
diff --git a/react/src/SettingsDialog.tsx b/react/src/SettingsDialog.tsx
index 65040d3..06f39c3 100644
--- a/react/src/SettingsDialog.tsx
+++ b/react/src/SettingsDialog.tsx
@@ -35,13 +35,15 @@ import JackServerSettings from './JackServerSettings';
import JackServerSettingsDialog from './JackServerSettingsDialog';
import JackHostStatus from './JackHostStatus';
import WifiConfigSettings from './WifiConfigSettings';
+import WifiDirectConfigSettings from './WifiDirectConfigSettings';
import WifiConfigDialog from './WifiConfigDialog';
+import WifiDirectConfigDialog from './WifiDirectConfigDialog';
import DialogEx from './DialogEx'
import GovernorSettings from './GovernorSettings';
-import Slide, {SlideProps} from '@mui/material/Slide';
+import Slide, { SlideProps } from '@mui/material/Slide';
import { createStyles, Theme } from '@mui/material/styles';
-import { WithStyles, withStyles} from '@mui/styles';
+import { WithStyles, withStyles } from '@mui/styles';
@@ -60,8 +62,10 @@ interface SettingsDialogState {
governorSettings: GovernorSettings;
wifiConfigSettings: WifiConfigSettings;
+ wifiDirectConfigSettings: WifiDirectConfigSettings;
showWifiConfigDialog: boolean;
+ showWifiDirectConfigDialog: boolean;
showGovernorSettingsDialog: boolean;
showInputSelectDialog: boolean;
showOutputSelectDialog: boolean;
@@ -69,6 +73,7 @@ interface SettingsDialogState {
showJackServerSettingsDialog: boolean;
shuttingDown: boolean;
restarting: boolean;
+ isAndroidHosted: boolean;
};
@@ -150,15 +155,19 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
jackStatus: undefined,
jackSettings: this.model.jackSettings.get(),
wifiConfigSettings: this.model.wifiConfigSettings.get(),
- governorSettings: this.model.governorSettings.get(),
+ wifiDirectConfigSettings: this.model.wifiDirectConfigSettings.get(),
+ governorSettings: this.model.governorSettings.get(),
showWifiConfigDialog: false,
+ showWifiDirectConfigDialog: false,
showGovernorSettingsDialog: false,
showInputSelectDialog: false,
showOutputSelectDialog: false,
showMidiSelectDialog: false,
showJackServerSettingsDialog: false,
shuttingDown: false,
- restarting: false
+ restarting: false,
+ isAndroidHosted: this.model.isAndroidHosted()
+
};
@@ -166,6 +175,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
+ this.handleWifiDirectConfigSettingsChanged = this.handleWifiDirectConfigSettingsChanged.bind(this);
this.handleGovernorSettingsChanged = this.handleGovernorSettingsChanged.bind(this);
this.handleConnectionStateChanged = this.handleConnectionStateChanged.bind(this);
@@ -173,6 +183,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
handleConnectionStateChanged(): void {
if (this.model.state.get() === State.Ready) {
+ this.setState({ isAndroidHosted: this.model.isAndroidHosted() });
if (this.state.shuttingDown || this.state.restarting) {
this.setState({ shuttingDown: false, restarting: false });
}
@@ -189,6 +200,18 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.showAlert(err);
});
}
+
+ handleApplyWifiDirectConfig(wifiDirectConfigSettings: WifiDirectConfigSettings): void {
+ this.setState({ showWifiDirectConfigDialog: false });
+ this.model.setWifiDirectConfigSettings(wifiDirectConfigSettings)
+ .then(() => {
+
+ })
+ .catch((err) => {
+ this.model.showAlert(err);
+ });
+ }
+
handleApplyGovernorSettings(governor: string): void {
this.model.setGovernorSettings(governor)
.then(() => {
@@ -206,6 +229,14 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
}
)
}
+ handleWifiDirectConfigSettingsChanged(): void {
+ this.setState(
+ {
+ wifiDirectConfigSettings: this.model.wifiDirectConfigSettings.get()
+ }
+ )
+ }
+
handleGovernorSettingsChanged(): void {
this.setState(
{
@@ -257,6 +288,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
+ this.model.wifiDirectConfigSettings.addOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
this.model.governorSettings.addOnChangedHandler(this.handleGovernorSettingsChanged);
this.model.getJackStatus()
.then((jackStatus) =>
@@ -276,6 +308,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.handleJackSettingsChanged();
this.handleJackServerSettingsChanged();
this.handleWifiConfigSettingsChanged();
+ this.handleWifiDirectConfigSettingsChanged();
this.timerHandle = setInterval(() => this.tick(), 1000);
} else {
if (this.timerHandle) {
@@ -286,7 +319,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
- this.model.wifiConfigSettings.removeOnChangedHandler(this.handleGovernorSettingsChanged);
+ this.model.wifiDirectConfigSettings.removeOnChangedHandler(this.handleWifiDirectConfigSettingsChanged);
+ this.model.governorSettings.removeOnChangedHandler(this.handleGovernorSettingsChanged);
}
}
@@ -340,7 +374,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
let newSelection = this.state.jackSettings.clone();
newSelection.inputMidiPorts = channels;
this.model.setJackSettings(newSelection);
-
+
}
this.setState({
showMidiSelectDialog: false,
@@ -385,6 +419,11 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
showWifiConfigDialog: true
});
}
+ handleShowWifiDirectConfigDialog() {
+ this.setState({
+ showWifiDirectConfigDialog: true
+ });
+ }
handleShowGovernorSettingsDialogDialog() {
this.setState({
showGovernorSettingsDialog: true
@@ -461,7 +500,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
(
{JackHostStatus.getDisplayView("", this.state.jackStatus)}
- { JackHostStatus.getCpuInfo("Governor:\u00A0", this.state.jackStatus)}
+ {JackHostStatus.getCpuInfo("Governor:\u00A0", this.state.jackStatus)}
)
}
@@ -534,8 +573,42 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
-
SYSTEM
+
CONNECTION
+
+ {
+ this.state.isAndroidHosted &&
+ (
+
this.model.chooseNewDevice() } >
+
+
+
+ Connect to a difference device
+
+
+
+
+
+
+ )
+ }
+
+
this.handleShowWifiDirectConfigDialog()} >
+
+
+
+ Configure Wi-Fi Direct hotspot
+
+ {this.state.wifiDirectConfigSettings.getSummaryText()}
+
+
+
+
+
this.handleShowWifiConfigDialog()} >
@@ -547,8 +620,13 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
+
+
+
+ SYSTEM
+
this.handleShowGovernorSettingsDialogDialog()} >
@@ -556,13 +634,26 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
CPU Governor
- {this.state.governorSettings.governor }
+ {this.state.governorSettings.governor}
-
-
+
+ this.handleShowGovernorSettingsDialogDialog()} >
+
+
+
+ Show status monitor on main screen.
+
+ Enabled
+
+
+
+
+
this.handleRestart()} >
@@ -571,7 +662,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.state.restarting ? (
Rebooting...
) : (
- Reboot
+ Reboot PiPedal
)
}
@@ -610,25 +701,25 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
{
(this.state.showGovernorSettingsDialog) &&
(
- this.setState({showGovernorSettingsDialog: false})}
+ onClose={() => this.setState({ showGovernorSettingsDialog: false })}
onOk={(selectedValue) => {
this.model.setGovernorSettings(selectedValue)
- .then(()=>{
+ .then(() => {
- })
- .catch(error => {
- this.model.showAlert(error);
- });
-
- this.setState({showGovernorSettingsDialog: false})
+ })
+ .catch(error => {
+ this.model.showAlert(error);
+ });
+
+ this.setState({ showGovernorSettingsDialog: false })
}}
- >
+ >
)
@@ -648,6 +739,10 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
onClose={() => this.setState({ showWifiConfigDialog: false })}
onOk={(wifiConfigSettings) => this.handleApplyWifiConfig(wifiConfigSettings)}
/>
+ this.setState({ showWifiDirectConfigDialog: false })}
+ onOk={(wifiDirectConfigSettings: WifiDirectConfigSettings) => this.handleApplyWifiDirectConfig(wifiDirectConfigSettings)}
+ />
diff --git a/react/src/TemporaryDrawer.tsx b/react/src/TemporaryDrawer.tsx
index d0fc893..98b01c3 100644
--- a/react/src/TemporaryDrawer.tsx
+++ b/react/src/TemporaryDrawer.tsx
@@ -39,8 +39,8 @@ const drawerStyles = (theme: Theme) => {
},
drawer_header: {
- color: 'white',
- background: theme.palette.primary.main,
+ color: theme.palette.primary.main,
+ background: 'white',
}
})};
diff --git a/react/src/WifiDirectConfigDialog.tsx b/react/src/WifiDirectConfigDialog.tsx
new file mode 100644
index 0000000..6bedfd4
--- /dev/null
+++ b/react/src/WifiDirectConfigDialog.tsx
@@ -0,0 +1,400 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Robin E. R. Davies
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+import React from 'react';
+import Button from '@mui/material/Button';
+import TextField from '@mui/material/TextField';
+import DialogEx from './DialogEx';
+import DialogActions from '@mui/material/DialogActions';
+import DialogContent from '@mui/material/DialogContent';
+import DialogTitle from '@mui/material/DialogTitle';
+import Switch from '@mui/material/Switch';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import ResizeResponsiveComponent from './ResizeResponsiveComponent';
+import WifiDirectConfigSettings from './WifiDirectConfigSettings';
+import { Theme } from '@mui/material/styles';
+import { WithStyles } from '@mui/styles';
+import withStyles from '@mui/styles/withStyles';
+import createStyles from '@mui/styles/createStyles';
+import Select from '@mui/material/Select';
+import FormControl from '@mui/material/FormControl';
+import InputLabel from '@mui/material/InputLabel';
+import MenuItem from '@mui/material/MenuItem';
+import WifiChannel from './WifiChannel';
+import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
+
+const styles = (theme: Theme) => createStyles({
+ pgraph: {
+ paddingBottom: 16
+ }
+});
+
+declare let crypto: any;
+
+const NBSP = "\u00A0";
+
+export interface WifiDirectConfigProps extends WithStyles {
+ open: boolean,
+ wifiDirectConfigSettings: WifiDirectConfigSettings,
+ onOk: (wifiDirectConfigSettings: WifiDirectConfigSettings) => void,
+ onClose: () => void
+}
+
+export interface WifiDirectConfigState {
+ fullScreen: boolean;
+ enabled: boolean;
+ name: string;
+ pin: string;
+ nameError: boolean;
+ nameErrorMessage: string;
+ pinError: boolean;
+ pinErrorMessage: string;
+ countryCode: string;
+ channel: string;
+ wifiChannels: WifiChannel[];
+}
+
+
+
+const WifiDirectConfigDialog = withStyles(styles, { withTheme: true })(
+ class extends ResizeResponsiveComponent {
+
+ refName: React.RefObject;
+ refPassword: React.RefObject;
+ model: PiPedalModel;
+
+ constructor(props: WifiDirectConfigProps) {
+ super(props);
+ this.model = PiPedalModelFactory.getInstance();
+ this.state = {
+ fullScreen: false,
+ enabled: this.props.wifiDirectConfigSettings.enable,
+ name: this.props.wifiDirectConfigSettings.hotspotName,
+ pin: this.props.wifiDirectConfigSettings.pin,
+ nameError: false,
+ nameErrorMessage: NBSP,
+ pinError: false,
+ pinErrorMessage: NBSP,
+ countryCode: this.props.wifiDirectConfigSettings.countryCode,
+ channel: this.props.wifiDirectConfigSettings.channel,
+ wifiChannels: []
+ };
+ this.model.getWifiChannels(this.props.wifiDirectConfigSettings.countryCode)
+ .then((wifiChannels_) => {
+ this.setState({ wifiChannels: wifiChannels_ });
+ }).catch((err) => { });
+
+ this.refName = React.createRef();
+ this.refPassword = React.createRef();
+ }
+ mounted: boolean = false;
+
+ handleEnableChanged(e: any) {
+ this.setState({ enabled: e.target.checked });
+ }
+
+
+ onWindowSizeChanged(width: number, height: number): void {
+ this.setState({ fullScreen: height < 200 })
+ }
+
+
+ componentDidMount() {
+ super.componentDidMount();
+ this.mounted = true;
+ }
+ componentWillUnmount() {
+ super.componentWillUnmount();
+ this.mounted = false;
+ }
+
+ componentDidUpdate(prevProps: WifiDirectConfigProps) {
+
+ if (this.props.open && !prevProps.open) {
+ this.setState({
+ enabled: this.props.wifiDirectConfigSettings.enable,
+ name: this.props.wifiDirectConfigSettings.hotspotName,
+ pin: this.props.wifiDirectConfigSettings.pin,
+ countryCode: this.props.wifiDirectConfigSettings.countryCode,
+ channel: this.props.wifiDirectConfigSettings.channel
+ });
+
+ if (this.props.wifiDirectConfigSettings.countryCode !== this.state.countryCode) {
+ this.model.getWifiChannels(this.props.wifiDirectConfigSettings.countryCode)
+ .then((wifiChannels) => {
+ this.setState({ wifiChannels: wifiChannels });
+ })
+ .catch((error) => { });
+
+ }
+
+ }
+
+ }
+ utf8Encode: TextEncoder = new TextEncoder();
+
+ validateName(name: string): void {
+ if (name.length === 0) {
+ throw new RangeError("Required.");
+ }
+ let bytes = this.utf8Encode.encode("DIRECT-XX-" + name);
+ if (bytes.length > 31) {
+ throw new RangeError("Too long.");
+ }
+ }
+ validatePin(pin: string) {
+ if (pin.length === 0) throw new Error("Required.");
+ if (pin.length !== 8) {
+ throw new RangeError("Must be exactly 8 digits.");
+ }
+ for (let i = 0; i < pin.length; ++i) {
+ let c = pin.charCodeAt(i);
+ if (c < '0'.charCodeAt(0) || c > '9'.charCodeAt(0)) {
+ throw new RangeError("Must be exactly 8 digits.");
+ }
+ }
+ }
+
+ handleOk() {
+ let nameError = false;
+ let nameErrorMessage = NBSP;
+ let pinError = false;
+ let pinErrorMessage = NBSP;
+ if (this.state.enabled) {
+ let name = this.state.name;
+ try {
+ this.validateName(name);
+ } catch (e: any) {
+ nameError = true; nameErrorMessage = "* " + e.message;
+ }
+ let pin = this.state.pin;
+ try {
+ this.validatePin(pin);
+ } catch (e: any) {
+ pinError = true; pinErrorMessage = "* " + e.message;
+ }
+
+ let error = pinError || nameError || this.state.countryCode === "";
+ if (error) {
+ this.setState({ nameError: nameError, nameErrorMessage: nameErrorMessage, pinError: pinError, pinErrorMessage: pinErrorMessage });
+ } else {
+ let wifiDirectConfigSettings = new WifiDirectConfigSettings();
+ wifiDirectConfigSettings.valid = true;
+
+ wifiDirectConfigSettings.enable = this.state.enabled;
+ wifiDirectConfigSettings.hotspotName = this.state.name;
+ wifiDirectConfigSettings.countryCode = this.state.countryCode;
+ wifiDirectConfigSettings.channel = this.state.channel;
+ wifiDirectConfigSettings.pin = this.state.pin;
+ this.props.onOk(wifiDirectConfigSettings);
+ }
+ } else {
+ let wifiDirectConfigSettings = new WifiDirectConfigSettings();
+ wifiDirectConfigSettings.valid = true;
+ wifiDirectConfigSettings.enable = false;
+ this.props.onOk(wifiDirectConfigSettings);
+
+ }
+
+ }
+ preventPasswordPrompt() {
+ }
+ handleChannelChange(e: any) {
+ let value = e.target.value as string;
+ this.setState({ channel: value });
+ }
+ handleCountryChanged(e: any) {
+ let value = e.target.value as string;
+
+ this.setState({ countryCode: value });
+ this.model.getWifiChannels(value)
+ .then(wifiChannels => {
+ this.setState({ wifiChannels: wifiChannels });
+ })
+ .catch((error) => { });
+ }
+
+ generateRandomPin(): string {
+ // prefer crypto if it's available.
+ let zero = "0".charCodeAt(0);
+ try {
+ let randomValue = new Int32Array(8);
+ crypto.getRandomValues(randomValue);
+
+ let s: string = "";
+ for (let i = 0; i < 8; ++i) {
+ let k = randomValue[i];
+ if (k < 0) k = -k;
+ k = k % 10;
+
+ s += String.fromCharCode(zero + k);
+ }
+ return s;
+
+ } catch (e) {
+
+ }
+ let zero2 = "a".charCodeAt(0);
+ let s2 = "";
+ for (let i = 0; i < 8; ++i) {
+ let k = Math.floor(Math.random() * 10);
+ if (k < 0) k = -k;
+ k = k % 10;
+
+ s2 += String.fromCharCode(zero2 + k);
+ }
+ return s2;
+ }
+
+ onGenerateRandomPin() {
+ let newPin = this.generateRandomPin();
+ this.setState({ pin: newPin, pinError: false, pinErrorMessage: NBSP });
+ if (this.refPassword.current) {
+ this.refPassword.current.value = newPin;
+ }
+ }
+
+ render() {
+ let props = this.props;
+ let { open, onClose } = props;
+
+ const handleClose = () => {
+ this.preventPasswordPrompt();
+ // let preventPasswordPrompt changes settle (it's HARD to prevent a password prompt)
+ setTimeout(() => {
+ onClose();
+ }, 100);
+ };
+
+ return (
+
+ {this.state.fullScreen && (
+ Wi-fi Hotspot
+ )}
+
+
+ this.handleEnableChanged(e)}
+ color="secondary"
+ />
+ )}
+ label="Enable Wi-Fi Direct Hotspot"
+ />
+
+ this.setState({ name: e.target.value, nameError: false, nameErrorMessage: NBSP })}
+ inputRef={this.refName}
+ disabled={!this.state.enabled}
+ variant="filled"
+ />
+
+
+
{ this.setState({ pin: e.target.value, pinError: false, pinErrorMessage: NBSP }); }}
+ label="Pin"
+ error={this.state.pinError}
+ helperText={this.state.pinErrorMessage}
+ defaultValue={this.state.pin}
+ disabled={!this.state.enabled}
+ variant="filled"
+ />
+
+
+
+
+
+
+
+
+ Country
+
+
+
+
+
+
+
+
+ Channel
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+ });
+
+export default WifiDirectConfigDialog;
\ No newline at end of file
diff --git a/react/src/WifiDirectConfigSettings.tsx b/react/src/WifiDirectConfigSettings.tsx
new file mode 100644
index 0000000..f2bd0be
--- /dev/null
+++ b/react/src/WifiDirectConfigSettings.tsx
@@ -0,0 +1,67 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2022 Robin E. R. Davies
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+
+export default class WifiDirectConfigSettings {
+ deserialize(input: any) : WifiDirectConfigSettings{
+ this.valid = input.valid;
+ this.rebootRequired = input.rebootRequired;
+ this.enable = input.enable;
+ this.pinChanged = input.pinChanged;
+ this.hotspotName = input.hotspotName;
+ this.countryCode = input.countryCode;
+ this.pin = input.pin;
+ this.channel = input.channel;
+
+ return this;
+ }
+ clone() : WifiDirectConfigSettings {
+ return new WifiDirectConfigSettings().deserialize(this);
+ }
+ valid: boolean = true;
+ enable: boolean = true;
+ rebootRequired = false;
+ hotspotName: string = "pipedal";
+ pinChanged: boolean = false;
+ pin: string = "";
+ countryCode: string = "US";
+ channel: string = "6";
+
+ getSummaryText() {
+ let result: string;
+ if (!this.valid) {
+ result = "Not available.";
+ } else if (!this.enable) {
+ result = "Disabled.";
+ } else {
+ result = this.hotspotName;
+ }
+ if (this.rebootRequired)
+ {
+ result += " (Restart required)";
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/src/AvahiService.cpp b/src/AvahiService.cpp
index 4fbd70b..92e4e11 100644
--- a/src/AvahiService.cpp
+++ b/src/AvahiService.cpp
@@ -156,7 +156,7 @@ void AvahiService::create_group(AvahiClient *c)
if ((ret = avahi_entry_group_add_service(
group,
AVAHI_IF_UNSPEC,
- AVAHI_PROTO_UNSPEC,
+ AVAHI_PROTO_INET, // IPv4 for now, until we figure out why dnsqmasq borks IPv6 routing.)
(AvahiPublishFlags)0,
name,
PIPEDAL_SERVICE_TYPE,
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 60d48ee..3eb5259 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 3.16.0)
set (CMAKE_INSTALL_PREFIX "/usr/")
+set (USE_PCH 1)
+
include(FindPkgConfig)
@@ -95,6 +97,7 @@ set (PIPEDAL_SOURCES
P2pConfigFiles.hpp
AvahiService.cpp AvahiService.hpp
DeviceIdFile.cpp DeviceIdFile.hpp
+ WriteTemplateFile.cpp WriteTemplateFile.hpp
StdErrorCapture.hpp StdErrorCapture.cpp
Ipv6Helpers.cpp Ipv6Helpers.hpp
@@ -152,30 +155,45 @@ set (PIPEDAL_SOURCES
)
+
configure_file(config.hpp.in config.hpp)
+include_directories( ${pipedald_SOURCE_DIR}/. ../build/src)
+
+set (PIPEDAL_INCLUDES
+ ${LV2DEV_INCLUDE_DIRS}
+ ${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ${LVDEV_INCLUDE_DIRS}
+)
+
+set(PIPEDAL_LIBS libpipedald pthread atomic stdc++fs asound avahi-common avahi-client systemd
+ ${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} )
+
+
+##########################
+
+add_library(libpipedald STATIC ${PIPEDAL_SOURCES})
+
+target_include_directories(libpipedald PRIVATE ${PIPEDAL_INCLUDES}
+ )
+
+if(${USE_PCH})
+ target_precompile_headers(libpipedald PRIVATE pch.h)
+endif()
+
#################################
-add_executable(pipedald ${PIPEDAL_SOURCES}
+add_executable(pipedald
asan_options.cpp # disable leak checking for sanitize=address.
main.cpp
)
+target_include_directories(pipedald PRIVATE ${PIPEDAL_INCLUDES})
-include_directories( ${pipedald_SOURCE_DIR}/. ../build/src)
-
-target_precompile_headers(pipedald PRIVATE pch.h)
-
-
-target_include_directories(pipedald PRIVATE
- ${LV2DEV_INCLUDE_DIRS}
- ${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ${LVDEV_INCLUDE_DIRS}
- )
-
-target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs asound avahi-common avahi-client
- ${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd SDBusCpp::sdbus-c++
+target_link_libraries(pipedald PRIVATE
+
+ ${PIPEDAL_LIBS}
)
#################################
-add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp
+add_executable(pipedaltest testMain.cpp
jsonTest.cpp
AvahiServiceTest.cpp
WifiChannelsTest.cpp
@@ -192,20 +210,17 @@ add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp
MemDebug.hpp
)
-configure_file(config.hpp.in config.hpp)
include_directories( ${pipedald_SOURCE_DIR}/. ../build/src)
+if(${USE_PCH})
target_precompile_headers(pipedaltest PRIVATE pch.h)
+endif()
-target_include_directories(pipedaltest PRIVATE
- .
- ${LV2DEV_INCLUDE_DIRS}
- ${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS}
+target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}
)
-target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs asound avahi-common avahi-client
- ${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd SDBusCpp::sdbus-c++
+target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS}
)
diff --git a/src/ConfigUtil.cpp b/src/ConfigUtil.cpp
index d8d235b..f70beb8 100644
--- a/src/ConfigUtil.cpp
+++ b/src/ConfigUtil.cpp
@@ -48,7 +48,7 @@ static std::string trim(const std::string &value)
size_t start = 0;
size_t end = value.length();
- while (start < end && ( value[start] == ' ' || value[start] == '\t'))
+ while (start < end && (value[start] == ' ' || value[start] == '\t'))
++start;
while (end > start && (value[end - 1] == ' ' || value[end - 1] == '\t'))
@@ -96,11 +96,36 @@ static std::string unquote(const std::string &value)
}
return ss.str();
}
-
}
return value;
}
-bool ConfigUtil::GetConfigLine(const std::string & filePath,const std::string & key, std::string *pValue)
+
+std::string ConfigUtil::QuoteString(const std::string &value)
+{
+ std::stringstream s;
+
+ s << '"';
+ for (char c : value)
+ {
+ switch (c)
+ {
+ case '\n':
+ break;
+ case '"':
+ s << "\\\"";
+ break;
+ case '\\':
+ s << "\\\\";
+ break;
+ default:
+ s << c;
+ break;
+ }
+ }
+ s << '"';
+ return s.str();
+}
+bool ConfigUtil::GetConfigLine(const std::string &filePath, const std::string &key, std::string *pValue)
{
ifstream f;
diff --git a/src/ConfigUtil.hpp b/src/ConfigUtil.hpp
index 388b292..fcc51ff 100644
--- a/src/ConfigUtil.hpp
+++ b/src/ConfigUtil.hpp
@@ -30,5 +30,8 @@ namespace pipedal {
class ConfigUtil {
public:
static bool GetConfigLine(const std::string & filePath,const std::string & key, std::string *pValue);
+
+ static std::string QuoteString(const std::string &value);
+
};
}
\ No newline at end of file
diff --git a/src/Ipv6Helpers.hpp b/src/Ipv6Helpers.hpp
index d790c2e..7acccb8 100644
--- a/src/Ipv6Helpers.hpp
+++ b/src/Ipv6Helpers.hpp
@@ -19,6 +19,7 @@
#pragma once
+#include
namespace pipedal {
diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp
index 580b52b..d5c188b 100644
--- a/src/PiPedalModel.cpp
+++ b/src/PiPedalModel.cpp
@@ -678,11 +678,42 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
delete[] t;
}
}
+void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &wifiDirectConfigSettings)
+{
+ std::lock_guard guard(mutex);
+
+ adminClient.SetWifiDirectConfig(wifiDirectConfigSettings);
+
+ this->storage.SetWifiDirectConfigSettings(wifiDirectConfigSettings);
+
+ {
+ IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
+ for (size_t i = 0; i < subscribers.size(); ++i)
+ {
+ t[i] = this->subscribers[i];
+ }
+ size_t n = this->subscribers.size();
+
+ WifiDirectConfigSettings tWifiDirectConfigSettings = storage.GetWifiDirectConfigSettings(); // (the passwordless version)
+
+ for (size_t i = 0; i < n; ++i)
+ {
+ t[i]->OnWifiDirectConfigSettingsChanged(tWifiDirectConfigSettings);
+ }
+ delete[] t;
+ }
+}
+
WifiConfigSettings PiPedalModel::GetWifiConfigSettings()
{
std::lock_guard guard(mutex);
return this->storage.GetWifiConfigSettings();
}
+WifiDirectConfigSettings PiPedalModel::GetWifiDirectConfigSettings()
+{
+ std::lock_guard guard(mutex);
+ return this->storage.GetWifiDirectConfigSettings();
+}
JackConfiguration PiPedalModel::GetJackConfiguration()
{
diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp
index ffedd15..c66ac76 100644
--- a/src/PiPedalModel.hpp
+++ b/src/PiPedalModel.hpp
@@ -33,6 +33,7 @@
#include "PiPedalConfiguration.hpp"
#include "JackServerSettings.hpp"
#include "WifiConfigSettings.hpp"
+#include "WifiDirectConfigSettings.hpp"
#include "AdminClient.hpp"
@@ -62,6 +63,7 @@ public:
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyAtomOutput(int64_t clientModel,uint64_t instanceId,const std::string&atomType, const std::string&atomJson) = 0;
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0;
+ virtual void OnWifiDirectConfigSettingsChanged(const WifiDirectConfigSettings&wifiDirectConfigSettings) = 0;
virtual void OnGovernorSettingsChanged(const std::string &governor) = 0;
virtual void OnFavoritesChanged(const std::map &favorites) = 0;
virtual void Close() = 0;
@@ -207,6 +209,9 @@ public:
void SetWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings();
+ void SetWifiDirectConfigSettings(const WifiDirectConfigSettings&wifiConfigSettings);
+ WifiDirectConfigSettings GetWifiDirectConfigSettings();
+
void SetGovernorSettings(const std::string& governor);
GovernorSettings GetGovernorSettings();
diff --git a/src/PiPedalSocket.cpp b/src/PiPedalSocket.cpp
index e870c6e..21d8b52 100644
--- a/src/PiPedalSocket.cpp
+++ b/src/PiPedalSocket.cpp
@@ -32,6 +32,7 @@
#include "AdminClient.hpp"
#include "WifiConfigSettings.hpp"
+#include "WifiDirectConfigSettings.hpp"
#include "WifiChannels.hpp"
#include "SysExec.hpp"
#include "PiPedalAlsa.hpp"
@@ -812,6 +813,28 @@ public:
this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings());
}
+ else if (message == "setWifiDirectConfigSettings") {
+ WifiDirectConfigSettings wifiDirectConfigSettings;
+ pReader->read(&wifiDirectConfigSettings);
+ if (!GetAdminClient().CanUseShutdownClient())
+ {
+ throw PiPedalException("Can't change server settings when running interactively.");
+ }
+ std::string fromAddress = this->getFromAddress();
+ if (!IsOnLocalSubnet(fromAddress))
+ {
+ throw PiPedalException("Permission denied. Not on local subnet.");
+ }
+
+
+ this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings);
+ this->Reply(replyTo,"setWifiDirectConfigSettings");
+ }
+ else if (message == "getWifiDirectConfigSettings") {
+ this->Reply(replyTo, "getWifiDirectConfigSettings", model.GetWifiDirectConfigSettings());
+
+ }
+
else if (message == "getGovernorSettings") {
this->Reply(replyTo, "getGovernorSettings", model.GetGovernorSettings());
@@ -1462,10 +1485,13 @@ public:
{
Send("onJackServerSettingsChanged",jackServerSettings);
}
-
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) {
Send("onWifiConfigSettingsChanged",wifiConfigSettings);
}
+
+ virtual void OnWifiDirectConfigSettingsChanged(const WifiDirectConfigSettings&wifiConfigSettings) {
+ Send("onWifiDirectConfigSettingsChanged",wifiConfigSettings);
+ }
virtual void OnGovernorSettingsChanged(const std::string& governor) {
Send("onGovernorSettingsChanged",governor);
}
diff --git a/src/PluginPreset.hpp b/src/PluginPreset.hpp
index 49deeb3..ba07734 100644
--- a/src/PluginPreset.hpp
+++ b/src/PluginPreset.hpp
@@ -22,6 +22,7 @@
#include "json.hpp"
#include "PiPedalException.hpp"
#include
+#include