Ubuntu 21.04 aarch64 portability; Bug fixes.
This commit is contained in:
@@ -21,6 +21,7 @@ add_custom_command(
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react
|
||||
|
||||
DEPENDS
|
||||
src/SearchControl.tsx
|
||||
src/WifiChannel.tsx
|
||||
src/JackServerSettingsDialog.tsx
|
||||
src/JackServerSettings.tsx
|
||||
|
||||
@@ -86,6 +86,7 @@ const appStyles = ({ palette, spacing, mixins }: Theme) => createStyles({
|
||||
zIndex: 2010
|
||||
},
|
||||
errorContent: {
|
||||
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
|
||||
+157
-12
@@ -18,7 +18,6 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import React, { ReactNode, SyntheticEvent } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { createStyles, withStyles, WithStyles, Theme } from '@material-ui/core/styles';
|
||||
import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { UiPlugin, PluginType } from './Lv2Plugin';
|
||||
@@ -41,6 +40,8 @@ import ClearIcon from '@material-ui/icons/Clear';
|
||||
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
|
||||
import { TransitionProps } from '@material-ui/core/transitions/transition';
|
||||
import Slide from '@material-ui/core/Slide';
|
||||
import SearchControl from './SearchControl';
|
||||
import SearchFilter from './SearchFilter';
|
||||
|
||||
|
||||
export type CloseEventHandler = () => void;
|
||||
@@ -154,9 +155,12 @@ interface PluginGridProps extends WithStyles<typeof pluginGridStyles> {
|
||||
type PluginGridState = {
|
||||
selected_uri?: string,
|
||||
hover_uri?: string,
|
||||
search_string: string;
|
||||
search_collapsed: boolean;
|
||||
filterType: PluginType,
|
||||
client_width: number,
|
||||
client_height: number,
|
||||
grid_cell_width: number,
|
||||
minimumItemWidth: number,
|
||||
|
||||
}
|
||||
@@ -173,10 +177,12 @@ export const LoadPluginDialog =
|
||||
class extends ResizeResponsiveComponent<PluginGridProps, PluginGridState>
|
||||
{
|
||||
model: PiPedalModel;
|
||||
searchInputRef: React.RefObject<HTMLInputElement>;
|
||||
|
||||
constructor(props: PluginGridProps) {
|
||||
super(props);
|
||||
this.model = PiPedalModelFactory.getInstance();
|
||||
this.searchInputRef = React.createRef<HTMLInputElement>();
|
||||
|
||||
let filterType_ = PluginType.Plugin; // i.e. "Any".
|
||||
let persistedFilter = window.localStorage.getItem(FILTER_STORAGE_KEY);
|
||||
@@ -187,21 +193,68 @@ export const LoadPluginDialog =
|
||||
this.state = {
|
||||
selected_uri: this.props.uri,
|
||||
hover_uri: "",
|
||||
search_string: "",
|
||||
search_collapsed: true,
|
||||
filterType: filterType_,
|
||||
client_width: window.innerWidth,
|
||||
client_height: window.innerHeight,
|
||||
grid_cell_width: this.getCellWidth(window.innerWidth),
|
||||
minimumItemWidth: props.minimumItemWidth ? props.minimumItemWidth : 220
|
||||
};
|
||||
|
||||
this.updateWindowSize = this.updateWindowSize.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.handleOk = this.handleOk.bind(this);
|
||||
this.handleSearchStringReady = this.handleSearchStringReady.bind(this);
|
||||
this.handleKeyPress = this.handleKeyPress.bind(this);
|
||||
}
|
||||
|
||||
nominal_column_width: number = 250;
|
||||
margin_reserve: number = 30;
|
||||
|
||||
getCellWidth(width: number)
|
||||
{
|
||||
let gridWidth = width-this.margin_reserve;
|
||||
let columns = Math.floor((gridWidth)/this.nominal_column_width);
|
||||
if (columns < 1) columns = 1;
|
||||
|
||||
return Math.floor(gridWidth/columns);
|
||||
}
|
||||
|
||||
handleKeyPress(e: React.KeyboardEvent<HTMLDivElement>)
|
||||
{
|
||||
let searchInput = this.searchInputRef.current;
|
||||
if (searchInput && e.target !== searchInput) // if the search input doesn't have focus.
|
||||
{
|
||||
if (this.searchInputRef.current) // we do have one, right?
|
||||
{
|
||||
if (e.key.length === 1)
|
||||
{
|
||||
if (/[a-zA-Z0-9]/.exec(e.key)) // if it's alpha-numeric.
|
||||
{
|
||||
if (this.state.search_collapsed) // and search is collapsed.
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
let newValue = searchInput.value + e.key;
|
||||
searchInput.value = newValue; // add the key. to the value.
|
||||
searchInput.focus();
|
||||
this.setState({
|
||||
search_string: newValue,
|
||||
search_collapsed: false})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateWindowSize() {
|
||||
this.setState({
|
||||
client_width: window.innerWidth,
|
||||
client_height: window.innerHeight
|
||||
client_height: window.innerHeight,
|
||||
grid_cell_width: this.getCellWidth(window.innerWidth)
|
||||
});
|
||||
}
|
||||
componentDidMount() {
|
||||
@@ -214,6 +267,16 @@ export const LoadPluginDialog =
|
||||
window.removeEventListener('resize', this.updateWindowSize);
|
||||
}
|
||||
|
||||
componentDidUpdate(oldProps: PluginGridProps) {
|
||||
if (oldProps.open !== this.props.open)
|
||||
{
|
||||
if (this.props.open)
|
||||
{
|
||||
this.setState({ search_string: "", search_collapsed: true});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onWindowSizeChanged(width: number, height: number): void {
|
||||
super.onWindowSizeChanged(width, height);
|
||||
|
||||
@@ -324,18 +387,48 @@ export const LoadPluginDialog =
|
||||
return result;
|
||||
|
||||
}
|
||||
applySearchFilter(plugins: UiPlugin[],searchString: string): UiPlugin[]
|
||||
{
|
||||
let results: { score: number; plugin: UiPlugin}[] = [];
|
||||
let searchFilter = new SearchFilter(searchString);
|
||||
|
||||
for (let i = 0; i < plugins.length; ++i)
|
||||
{
|
||||
let plugin = plugins[i];
|
||||
let score = searchFilter.score(plugin.name,plugin.plugin_display_type,plugin.author_name);
|
||||
if (score !== 0)
|
||||
{
|
||||
results.push({score:score, plugin:plugin});
|
||||
}
|
||||
}
|
||||
results.sort((left: {score:number; plugin: UiPlugin},right: {score:number; plugin: UiPlugin}) => {
|
||||
if (right.score < left.score) return -1;
|
||||
if (right.score > left.score) return 1;
|
||||
return left.plugin.name.localeCompare(right.plugin.name);
|
||||
});
|
||||
let t: UiPlugin[] = [];
|
||||
for (let i = 0; i < results.length; ++i)
|
||||
{
|
||||
t.push(results[i].plugin);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
filterPlugins(plugins: UiPlugin[]): ReactNode[] {
|
||||
let result: ReactNode[] = [];
|
||||
let filterType = this.state.filterType;
|
||||
let classes = this.props.classes;
|
||||
let rootClass = this.model.plugin_classes.get();
|
||||
if (this.state.search_string.length !== 0)
|
||||
{
|
||||
plugins = this.applySearchFilter(plugins,this.state.search_string);
|
||||
}
|
||||
|
||||
for (let i = 0; i < plugins.length; ++i) {
|
||||
let value = plugins[i];
|
||||
if (filterType === PluginType.Plugin || rootClass.is_type_of(filterType, value.plugin_type)) {
|
||||
result.push(
|
||||
(
|
||||
<Grid key={value.uri} xs={12} sm={6} md={4} lg={3} xl={3} item
|
||||
<div key={value.uri} style={{width: this.state.grid_cell_width, height: 64}}
|
||||
onDoubleClick={(e) => { this.onDoubleClick(e, value.uri) }}
|
||||
onClick={(e) => { this.onClick(e, value.uri) }}
|
||||
onMouseEnter={(e) => { this.handleMouseEnter(e, value.uri) }}
|
||||
@@ -358,7 +451,7 @@ export const LoadPluginDialog =
|
||||
</div>
|
||||
</div>
|
||||
</ButtonBase>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
|
||||
@@ -366,6 +459,30 @@ export const LoadPluginDialog =
|
||||
}
|
||||
return result;
|
||||
}
|
||||
changedSearchString? : string = undefined;
|
||||
hSearchTimeout?: NodeJS.Timeout;
|
||||
|
||||
handleSearchStringReady() {
|
||||
if (this.changedSearchString !== undefined)
|
||||
{
|
||||
this.setState({search_string: this.changedSearchString});
|
||||
this.changedSearchString = undefined;
|
||||
}
|
||||
this.hSearchTimeout = undefined;
|
||||
}
|
||||
|
||||
|
||||
handleSearchStringChanged(text: string) : void {
|
||||
if (this.hSearchTimeout) {
|
||||
clearTimeout(this.hSearchTimeout);
|
||||
this.hSearchTimeout = undefined;
|
||||
}
|
||||
this.changedSearchString = text;
|
||||
this.hSearchTimeout = setTimeout(
|
||||
this.handleSearchStringReady,
|
||||
2000);
|
||||
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
@@ -381,10 +498,17 @@ export const LoadPluginDialog =
|
||||
if (t) selectedPlugin = t;
|
||||
}
|
||||
|
||||
let showSearchIcon = true;
|
||||
if (this.state.client_width < 500)
|
||||
{
|
||||
showSearchIcon = this.state.search_collapsed
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
<React.Fragment>
|
||||
<Dialog
|
||||
onKeyPress={(e)=> { this.handleKeyPress(e); }}
|
||||
fullScreen={true}
|
||||
TransitionComponent={Transition}
|
||||
maxWidth={false}
|
||||
@@ -393,16 +517,37 @@ export const LoadPluginDialog =
|
||||
onClose={this.handleCancel}
|
||||
style={{ overflowX: "hidden", overflowY: "hidden", display: "flex", flexDirection: "column", flexWrap: "nowrap" }}
|
||||
aria-labelledby="select-plugin-dialog-title">
|
||||
<DialogTitle id="select-plugin-dialog-title" style={{ flex: "0 0 auto" }}>
|
||||
<div style={{ display: "flex", flexDirection: "row", flexWrap: "nowrap", width: "100%", alignItems: "center" }}>
|
||||
<DialogTitle id="select-plugin-dialog-title" style={{ flex: "0 0 auto", padding: "0px",height: 54 }}>
|
||||
<div style={{ display: "flex", flexDirection: "row", paddingTop: 3, paddingBottom: 3,flexWrap: "nowrap", width: "100%", alignItems: "center" }}>
|
||||
<IconButton onClick={() => { this.cancel(); }} style={{ flex: "0 0 auto" }} >
|
||||
<ArrowBackIcon />
|
||||
</IconButton>
|
||||
|
||||
<Typography display="block" variant="h6" style={{ flex: "0 1 auto" }}>
|
||||
<Typography display="inline" noWrap variant="h6" style={{ flex: "0 0 auto", height: "auto", verticalAlign: "center",
|
||||
visibility: (this.state.search_collapsed? "visible": "collapse"),
|
||||
width: (this.state.search_collapsed? undefined: "0px")}}>
|
||||
{this.state.client_width > 520 ? "Select Plugin" : ""}
|
||||
</Typography>
|
||||
<div style={{ flex: "1 1 auto" }} />
|
||||
<div style={{ flex: "1 1 auto" }} >
|
||||
<SearchControl collapsed={this.state.search_collapsed} searchText={this.state.search_string}
|
||||
inputRef={this.searchInputRef}
|
||||
showSearchIcon={showSearchIcon}
|
||||
onTextChanged={(text)=> {
|
||||
this.handleSearchStringChanged(text);
|
||||
}}
|
||||
onClearFilterClick={()=> {
|
||||
this.setState({search_collapsed: true,search_string:""});
|
||||
}}
|
||||
onClick={()=> {
|
||||
if (this.state.search_collapsed) {
|
||||
this.setState({search_collapsed: false});
|
||||
} else {
|
||||
this.setState({ search_collapsed: true, search_string: ""});
|
||||
}
|
||||
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<Select defaultValue={this.state.filterType} key={this.state.filterType} onChange={(e) => { this.onFilterChange(e); }}
|
||||
style={{ flex: "0 0 160px" }}
|
||||
>
|
||||
@@ -420,13 +565,13 @@ export const LoadPluginDialog =
|
||||
padding: 8,
|
||||
display: "1 1 flex"
|
||||
}} >
|
||||
<Grid container justify="flex-start" >
|
||||
<div style={{display: "flex", width: this.state.client_width-30,flexDirection: "row",flexWrap: "wrap",
|
||||
justifyContent: "flex-start"}}
|
||||
>
|
||||
{
|
||||
this.filterPlugins(plugins)
|
||||
}
|
||||
|
||||
|
||||
</Grid>
|
||||
</div>
|
||||
</DialogContent>
|
||||
{(this.state.client_width >= NARROW_DISPLAY_THRESHOLD) ? (
|
||||
<DialogActions style={{ flex: "0 0 auto" }} >
|
||||
|
||||
@@ -61,8 +61,6 @@ export function SelectBaseIcon(plugin_type: PluginType): string {
|
||||
return "fx_distortion.svg";
|
||||
case PluginType.FlangerPlugin:
|
||||
return "fx_flanger.svg";
|
||||
case PluginType.HighpassPlugin:
|
||||
return "fx_highpass.svg";
|
||||
case PluginType.ParaEQPlugin:
|
||||
case PluginType.LowpassPlugin:
|
||||
case PluginType.HighpassPlugin:
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2021 Robin 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 { Theme, withStyles, WithStyles, createStyles } from '@material-ui/core/styles';
|
||||
import Input from '@material-ui/core/Input';
|
||||
import IconButton from '@material-ui/core/IconButton';
|
||||
import SearchIcon from '@material-ui/icons/Search';
|
||||
import ClearIcon from '@material-ui/icons/Clear';
|
||||
import InputAdornment from '@material-ui/core/InputAdornment';
|
||||
|
||||
const styles = (theme: Theme) => createStyles({
|
||||
});
|
||||
|
||||
|
||||
interface SearchControlProps extends WithStyles<typeof styles> {
|
||||
theme: Theme,
|
||||
collapsed?: boolean;
|
||||
searchText: string;
|
||||
onTextChanged: (searchString: string) => void;
|
||||
onClick: () => void;
|
||||
onClearFilterClick: () => void;
|
||||
inputRef?: React.RefObject<HTMLInputElement>;
|
||||
showSearchIcon?: boolean
|
||||
|
||||
};
|
||||
|
||||
interface SearchControlState {
|
||||
};
|
||||
|
||||
const SearchControl = withStyles(styles, { withTheme: true })(
|
||||
|
||||
class extends React.Component<SearchControlProps, SearchControlState> {
|
||||
|
||||
defaultInputRef: React.RefObject<HTMLInputElement>;
|
||||
|
||||
constructor(props: SearchControlProps) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
||||
};
|
||||
this.defaultInputRef = React.createRef<HTMLInputElement>();
|
||||
}
|
||||
getInputRef(): React.RefObject<HTMLInputElement> {
|
||||
if (this.props.inputRef) {
|
||||
return this.props.inputRef;
|
||||
}
|
||||
return this.defaultInputRef;
|
||||
}
|
||||
componentDidUpdate(oldProps: SearchControlProps) {
|
||||
if (oldProps.collapsed ?? false !== this.props.collapsed ?? false) {
|
||||
let inputRef = this.getInputRef();
|
||||
if (inputRef.current) {
|
||||
if (!(this.props.collapsed ?? false)) {
|
||||
setTimeout(() => {
|
||||
let inputRef = this.getInputRef();
|
||||
if (inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, 0);
|
||||
|
||||
} else {
|
||||
inputRef.current.value = "";
|
||||
this.props.onTextChanged("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div style={{ display: "flex", flexFlow: "row nowrap", justifyContent: "flex-end", alignItems: "center" }}>
|
||||
{(this.props.showSearchIcon ?? true) && (
|
||||
<IconButton onClick={() => { this.props.onClick(); }}
|
||||
>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
)
|
||||
|
||||
}
|
||||
<Input style={{
|
||||
flex: "1 1", visibility: (this.props.collapsed ?? false) ? "collapse" : "visible", marginRight: 12, height: 32,
|
||||
}}
|
||||
inputRef={this.getInputRef()}
|
||||
defaultValue={this.props.searchText}
|
||||
placeholder="Search"
|
||||
endAdornment={(
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
aria-label="clear search filter"
|
||||
onClick={() => {
|
||||
this.props.onClearFilterClick();
|
||||
let inputRef = this.getInputRef();
|
||||
if (inputRef.current) {
|
||||
inputRef.current.value = "";
|
||||
}
|
||||
}}
|
||||
edge="end"
|
||||
>
|
||||
<ClearIcon fontSize='small' style={{ opacity: 0.6 }} />
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
|
||||
)}
|
||||
onChange={
|
||||
(e) => {
|
||||
this.props.onTextChanged(e.target.value);
|
||||
}
|
||||
}
|
||||
|
||||
/>
|
||||
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export default SearchControl;
|
||||
@@ -0,0 +1,73 @@
|
||||
|
||||
|
||||
let escapedCharacters = /[^a-zA-Z0-9]/g;
|
||||
|
||||
function stringToRegex(text: string): RegExp
|
||||
{
|
||||
text = text.replace(escapedCharacters,"\\$&");
|
||||
|
||||
return new RegExp(text,"i");
|
||||
}
|
||||
|
||||
|
||||
class SearchFilter {
|
||||
regexes: RegExp[];
|
||||
|
||||
constructor(text: string)
|
||||
{
|
||||
let regexes: RegExp[] = [];
|
||||
|
||||
let bits = text.split(' ');
|
||||
for (let bit of bits)
|
||||
{
|
||||
if (bit.length !== 0)
|
||||
{
|
||||
regexes.push(stringToRegex(bit));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.regexes = regexes;
|
||||
}
|
||||
|
||||
score(...strings: string[]): number {
|
||||
let score = 0;
|
||||
for (let r = 0; r < this.regexes.length; ++r)
|
||||
{
|
||||
let regex = this.regexes[r];
|
||||
regex.lastIndex = 0;
|
||||
let matched = false;
|
||||
for (let i = 0; i < strings.length; ++i)
|
||||
{
|
||||
let s = strings[i];
|
||||
let m = regex.exec(s);
|
||||
if (m) {
|
||||
matched = true;
|
||||
if (m.index === 0)
|
||||
{
|
||||
if (r === 0 && i === 0) {
|
||||
score += 16;
|
||||
} else {
|
||||
score += 8;
|
||||
}
|
||||
}
|
||||
let end = m.index + m[0].length;
|
||||
if (end === s.length || (end < s.length && s[end] === ' ')) // word boundary
|
||||
{
|
||||
score += 2;
|
||||
} else {
|
||||
score += 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!matched) return 0;
|
||||
}
|
||||
|
||||
return score;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default SearchFilter;
|
||||
+17
-8
@@ -97,6 +97,7 @@ namespace pipedal {
|
||||
class BeastServerImpl : public BeastServer
|
||||
{
|
||||
private:
|
||||
int signalOnDone = -1;
|
||||
std::string address;
|
||||
int port = -1;
|
||||
std::filesystem::path rootPath;
|
||||
@@ -210,6 +211,9 @@ private:
|
||||
}
|
||||
void on_close(connection_hdl hdl)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
|
||||
#endif
|
||||
auto shThis = this->shared_from_this(); // we will destruct as we return.
|
||||
pServer->on_session_closed(shThis,hdl);
|
||||
}
|
||||
@@ -472,13 +476,19 @@ private:
|
||||
using websocketpp::lib::bind;
|
||||
using websocketpp::lib::placeholders::_1;
|
||||
using websocketpp::lib::placeholders::_2;
|
||||
|
||||
m_endpoint.set_open_handler(bind(&BeastServerImpl::on_open, this, _1));
|
||||
m_endpoint.set_close_handler(bind(&BeastServerImpl::on_close, this, _1));
|
||||
m_endpoint.set_http_handler(bind(&BeastServerImpl::on_http, this, _1));
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Listening on " << this->address << ":" << this->port;
|
||||
Lv2Log::info(ss.str());
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << port;
|
||||
|
||||
m_endpoint.listen(this->address, ss.str());
|
||||
m_endpoint.start_accept();
|
||||
|
||||
@@ -494,11 +504,6 @@ private:
|
||||
|
||||
// Start the ASIO io_service run loop
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Listening on " << this->address << ":" << this->port;
|
||||
Lv2Log::info(ss.str());
|
||||
}
|
||||
m_endpoint.run();
|
||||
//ioc.run();
|
||||
/****************** */
|
||||
@@ -514,8 +519,11 @@ private:
|
||||
catch (websocketpp::exception const &e)
|
||||
{
|
||||
std::cout << e.what() << std::endl;
|
||||
return;
|
||||
}
|
||||
if (this->signalOnDone != -1) {
|
||||
kill(getpid(),this->signalOnDone);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void ThreadProc(BeastServerImpl *server)
|
||||
@@ -565,12 +573,13 @@ public:
|
||||
this->pBgThread = nullptr;
|
||||
}
|
||||
|
||||
virtual void RunInBackground()
|
||||
virtual void RunInBackground(int signalOnDone)
|
||||
{
|
||||
if (this->pBgThread != nullptr)
|
||||
{
|
||||
throw std::runtime_error("Bad state.");
|
||||
}
|
||||
this->signalOnDone = signalOnDone;
|
||||
this->pBgThread = new std::thread(ThreadProc, this);
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -191,7 +191,8 @@ public:
|
||||
virtual void ShutDown(int timeoutMs) = 0;
|
||||
virtual void Join() = 0;
|
||||
|
||||
virtual void RunInBackground() = 0;
|
||||
// signalOnDone: fire the specified POSIX signal when the service thread terminates. -1 for no signal.
|
||||
virtual void RunInBackground(int signalOnDone = -1) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ endif()
|
||||
|
||||
message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" )
|
||||
|
||||
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
|
||||
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_BIND_GLOBAL_PLACEHOLDERS)
|
||||
|
||||
set (PIPEDAL_SOURCES
|
||||
SysExec.cpp SysExec.hpp
|
||||
|
||||
@@ -89,6 +89,13 @@ PiPedalModel::~PiPedalModel()
|
||||
|
||||
#include <fstream>
|
||||
|
||||
|
||||
void PiPedalModel::LoadLv2PluginInfo(const PiPedalConfiguration&configuration)
|
||||
{
|
||||
lv2Host.Load(configuration.GetLv2Path().c_str());
|
||||
|
||||
}
|
||||
|
||||
void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
||||
{
|
||||
|
||||
@@ -112,7 +119,7 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
|
||||
s << "Unable to load " << pluginClassesPath << ". " << e.what();
|
||||
throw PiPedalException(s.str().c_str());
|
||||
}
|
||||
lv2Host.Load(configuration.GetLv2Path().c_str());
|
||||
// lv2Host.Load(configuration.GetLv2Path().c_str());
|
||||
|
||||
this->pedalBoard = storage.GetCurrentPreset(); // the current *saved* preset.
|
||||
|
||||
|
||||
@@ -127,6 +127,7 @@ public:
|
||||
virtual ~PiPedalModel();
|
||||
|
||||
void Close();
|
||||
void LoadLv2PluginInfo(const PiPedalConfiguration&configuration);
|
||||
void Load(const PiPedalConfiguration&configuration);
|
||||
|
||||
const Lv2Host& getPlugins() const { return lv2Host; }
|
||||
|
||||
@@ -156,7 +156,7 @@ void pipedal::SetWifiConfig(const WifiConfigSettings&settings)
|
||||
dhcpcd.InsertLine(line++,"interface wlan0");
|
||||
dhcpcd.InsertLine(line++," static ip_address=" PIPEDAL_NETWORK);
|
||||
dhcpcd.InsertLine(line++," nohook wpa_supplicant");
|
||||
dhcpcd.Save(dhcpcdConfig);adding
|
||||
dhcpcd.Save(dhcpcdConfig);
|
||||
}
|
||||
|
||||
// ***** save the config files ***
|
||||
|
||||
+19
-7
@@ -66,8 +66,11 @@ public:
|
||||
static volatile bool g_SigBreak = false;
|
||||
void sig_handler(int signo)
|
||||
{
|
||||
g_SigBreak = true;
|
||||
sem_post(&signalSemaphore);
|
||||
if (!g_SigBreak)
|
||||
{
|
||||
g_SigBreak = true;
|
||||
sem_post(&signalSemaphore);
|
||||
}
|
||||
}
|
||||
using namespace boost::system;
|
||||
|
||||
@@ -465,6 +468,7 @@ int main(int argc, char *argv[])
|
||||
Lv2Log::set_logger(MakeLv2SystemdLogger());
|
||||
}
|
||||
signal(SIGTERM, sig_handler);
|
||||
signal(SIGUSR1, sig_handler);
|
||||
|
||||
std::filesystem::path doc_root = parser.Arguments()[0];
|
||||
std::filesystem::path web_root = doc_root;
|
||||
@@ -548,10 +552,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
}
|
||||
}
|
||||
Lv2Log::info("Waiting for jack service.");
|
||||
|
||||
// pre-cache device info.
|
||||
// pre-cache device info before we let audio services run.
|
||||
model.GetAlsaDevices();
|
||||
|
||||
// Get heavy IO out of the way before letting dependent (Jack) services run.
|
||||
model.LoadLv2PluginInfo(configuration);
|
||||
// Tell systemd we're done.
|
||||
sd_notify(0, "READY=1");
|
||||
|
||||
if (!isJackServiceRunning())
|
||||
@@ -570,9 +577,14 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
Lv2Log::info("Found Jack service.");
|
||||
}
|
||||
sleep(3); // jack needs a little time to get up to speed.
|
||||
if (isJackServiceRunning())
|
||||
{
|
||||
Lv2Log::info("Found Jack service.");
|
||||
sleep(3); // jack needs a little time to get up to speed.
|
||||
} else {
|
||||
Lv2Log::info("Jack service not started.");
|
||||
}
|
||||
}
|
||||
|
||||
model.Load(configuration);
|
||||
@@ -587,7 +599,7 @@ int main(int argc, char *argv[])
|
||||
std::shared_ptr<DownloadIntercept> downloadIntercept = std::make_shared<DownloadIntercept>(&model);
|
||||
server->AddRequestHandler(downloadIntercept);
|
||||
|
||||
server->RunInBackground();
|
||||
server->RunInBackground(SIGUSR1);
|
||||
|
||||
sem_wait(&signalSemaphore);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ ExecStart=${COMMAND}
|
||||
User=pipedal_d
|
||||
Group=pipedal_d
|
||||
Restart=always
|
||||
TimeoutStartSec=60
|
||||
RestartSec=25
|
||||
TimeoutStopSec=10
|
||||
WorkingDirectory=/var/pipedal
|
||||
|
||||
Reference in New Issue
Block a user