mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 19:56:42 +01:00
port connections; DevicePorts -> PortList
This commit is contained in:
parent
b1df7bf4e6
commit
22b44f562b
8 changed files with 117 additions and 77 deletions
|
|
@ -21,7 +21,7 @@ pub use self::launcher::Launcher;
|
||||||
use crossterm::event;
|
use crossterm::event;
|
||||||
use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client};
|
use ::jack::{AudioIn, AudioOut, MidiIn, MidiOut, Port, PortSpec, Client};
|
||||||
|
|
||||||
pub trait Device: Render + Handle + DevicePorts + Send + Sync {}
|
pub trait Device: Render + Handle + PortList + Send + Sync {}
|
||||||
|
|
||||||
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
|
pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn Error>> {
|
||||||
let device = Arc::new(Mutex::new(device));
|
let device = Arc::new(Mutex::new(device));
|
||||||
|
|
@ -92,7 +92,7 @@ pub fn run (device: impl Device + Send + Sync + 'static) -> Result<(), Box<dyn E
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Render + Handle + DevicePorts + Send + Sync> Device for T {}
|
impl<T: Render + Handle + PortList + Send + Sync> Device for T {}
|
||||||
|
|
||||||
pub trait Handle {
|
pub trait Handle {
|
||||||
// Handle an input event.
|
// Handle an input event.
|
||||||
|
|
@ -125,7 +125,7 @@ impl<T: AsRef<str>> Blit for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait DevicePorts {
|
pub trait PortList {
|
||||||
fn audio_ins (&self) -> Usually<Vec<String>> {
|
fn audio_ins (&self) -> Usually<Vec<String>> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
@ -196,7 +196,7 @@ pub struct DynamicDevice<T> {
|
||||||
pub render: Mutex<Box<dyn FnMut(&T, &mut Buffer, Rect)->Usually<Rect> + Send>>,
|
pub render: Mutex<Box<dyn FnMut(&T, &mut Buffer, Rect)->Usually<Rect> + Send>>,
|
||||||
pub handle: Arc<Mutex<Box<dyn FnMut(&mut T, &AppEvent)->Usually<bool> + Send>>>,
|
pub handle: Arc<Mutex<Box<dyn FnMut(&mut T, &AppEvent)->Usually<bool> + Send>>>,
|
||||||
pub process: Arc<Mutex<Box<dyn FnMut(&mut T, &Client, &ProcessScope)->Control + Send>>>,
|
pub process: Arc<Mutex<Box<dyn FnMut(&mut T, &Client, &ProcessScope)->Control + Send>>>,
|
||||||
client: Option<DynamicAsyncClient>
|
pub client: Option<DynamicAsyncClient>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Handle for DynamicDevice<T> {
|
impl<T> Handle for DynamicDevice<T> {
|
||||||
|
|
@ -211,7 +211,7 @@ impl<T> Render for DynamicDevice<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: DevicePorts + Send + Sync + 'static> DevicePorts for DynamicDevice<T> {
|
impl<T: PortList + Send + Sync + 'static> PortList for DynamicDevice<T> {
|
||||||
fn audio_ins (&self) -> Usually<Vec<String>> {
|
fn audio_ins (&self) -> Usually<Vec<String>> {
|
||||||
self.state().audio_ins()
|
self.state().audio_ins()
|
||||||
}
|
}
|
||||||
|
|
@ -244,7 +244,7 @@ impl<T: Send + Sync + 'static> DynamicDevice<T> {
|
||||||
client: None,
|
client: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn state (&self) -> std::sync::MutexGuard<'_, T> {
|
pub fn state (&self) -> std::sync::MutexGuard<'_, T> {
|
||||||
self.state.lock().unwrap()
|
self.state.lock().unwrap()
|
||||||
}
|
}
|
||||||
fn activate (mut self, client: Client) -> Usually<Self> {
|
fn activate (mut self, client: Client) -> Usually<Self> {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,23 @@ impl Chain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PortList for Chain {
|
||||||
|
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||||
|
if let Some(device) = self.items.get(0) {
|
||||||
|
device.midi_ins()
|
||||||
|
} else {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn audio_outs (&self) -> Usually<Vec<String>> {
|
||||||
|
if let Some(device) = self.items.get(self.items.len().saturating_sub(1)) {
|
||||||
|
device.audio_outs()
|
||||||
|
} else {
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn process (_: &mut Chain, _: &Client, _: &ProcessScope) -> Control {
|
pub fn process (_: &mut Chain, _: &Client, _: &ProcessScope) -> Control {
|
||||||
Control::Continue
|
Control::Continue
|
||||||
}
|
}
|
||||||
|
|
@ -238,5 +255,3 @@ pub fn handle (state: &mut Chain, event: &AppEvent) -> Usually<bool> {
|
||||||
|s: &mut Chain|s.handle_focus(&FocusEvent::Outward)]
|
|s: &mut Chain|s.handle_focus(&FocusEvent::Outward)]
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DevicePorts for Chain {}
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@ pub struct Launcher {
|
||||||
overdub: bool,
|
overdub: bool,
|
||||||
position: usize,
|
position: usize,
|
||||||
cursor: (usize, usize),
|
cursor: (usize, usize),
|
||||||
tracks: Vec<DynamicDevice<Sequencer>>,
|
pub tracks: Vec<DynamicDevice<Sequencer>>,
|
||||||
chains: Vec<DynamicDevice<Chain>>,
|
pub chains: Vec<DynamicDevice<Chain>>,
|
||||||
scenes: Vec<Scene>,
|
scenes: Vec<Scene>,
|
||||||
show_help: bool,
|
show_help: bool,
|
||||||
view: LauncherView,
|
view: LauncherView,
|
||||||
|
|
@ -34,6 +34,44 @@ impl Scene {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Launcher {
|
impl Launcher {
|
||||||
|
pub fn new_with_controller (name: &str, pattern: &str)
|
||||||
|
-> Result<DynamicDevice<Self>, Box<dyn Error>>
|
||||||
|
{
|
||||||
|
let launcher = Self::new(name)?;
|
||||||
|
{
|
||||||
|
let state = &launcher.state();
|
||||||
|
let (client, _status) = Client::new(&format!("{name}-init"), ClientOptions::NO_START_SERVER)?;
|
||||||
|
let controllers = client.ports(Some(pattern), None, ::jack::PortFlags::IS_OUTPUT);
|
||||||
|
for (i, sequencer) in state.tracks.iter().enumerate() {
|
||||||
|
for sequencer_midi_in in sequencer.midi_ins()?.iter() {
|
||||||
|
for controller in controllers.iter() {
|
||||||
|
client.connect_ports_by_name(
|
||||||
|
&controller,
|
||||||
|
&sequencer_midi_in
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let chain: &DynamicDevice<Chain> = &state.chains[i];
|
||||||
|
for sequencer_midi_out in sequencer.midi_outs()?.iter() {
|
||||||
|
for chain_midi_in in chain.midi_ins()?.iter() {
|
||||||
|
client.connect_ports_by_name(
|
||||||
|
&sequencer_midi_out,
|
||||||
|
&chain_midi_in
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client.connect_ports_by_name(
|
||||||
|
&chain.audio_outs()?[0],
|
||||||
|
"Komplete Audio 6 Analog Stereo 1/2:playback_FL"
|
||||||
|
);
|
||||||
|
client.connect_ports_by_name(
|
||||||
|
&chain.audio_outs()?[1],
|
||||||
|
"Komplete Audio 6 Analog Stereo 1/2:playback_FR"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(launcher)
|
||||||
|
}
|
||||||
pub fn new (name: &str,) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
pub fn new (name: &str,) -> Result<DynamicDevice<Self>, Box<dyn Error>> {
|
||||||
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
let (client, _) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
let transport = client.transport();
|
let transport = client.transport();
|
||||||
|
|
@ -127,7 +165,7 @@ impl Launcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl DevicePorts for Launcher {}
|
impl PortList for Launcher {}
|
||||||
pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
pub fn process (state: &mut Launcher, _: &Client, _: &ProcessScope) -> Control {
|
||||||
let transport = state.transport.query().unwrap();
|
let transport = state.transport.query().unwrap();
|
||||||
state.playing = transport.state;
|
state.playing = transport.state;
|
||||||
|
|
|
||||||
|
|
@ -5,24 +5,22 @@ mod vst2;
|
||||||
mod vst3;
|
mod vst3;
|
||||||
|
|
||||||
pub struct Plugin {
|
pub struct Plugin {
|
||||||
name: String,
|
name: String,
|
||||||
input: ::jack::Port<::jack::MidiIn>,
|
path: String,
|
||||||
outputs: [::jack::Port<::jack::AudioOut>;2],
|
plugin: Option<PluginKind>,
|
||||||
path: String,
|
offset: usize,
|
||||||
plugin: Option<PluginKind>,
|
selected: usize,
|
||||||
offset: usize,
|
mapping: bool,
|
||||||
selected: usize,
|
midi_in: ::jack::Port<::jack::MidiIn>,
|
||||||
midi_map_enable: bool,
|
audio_out: [::jack::Port<::jack::AudioOut>;2],
|
||||||
}
|
}
|
||||||
|
|
||||||
enum PluginKind {
|
enum PluginKind {
|
||||||
LV2 {
|
LV2 {
|
||||||
world: ::livi::World,
|
world: ::livi::World,
|
||||||
features: Arc<::livi::Features>,
|
features: Arc<::livi::Features>,
|
||||||
input: ::livi::event::LV2AtomSequence,
|
portList: Vec<::livi::Port>,
|
||||||
portList: Vec<::livi::Port>,
|
instance: ::livi::Instance,
|
||||||
instance: ::livi::Instance,
|
|
||||||
outputs: [Vec<f32>;2],
|
|
||||||
},
|
},
|
||||||
VST2 {
|
VST2 {
|
||||||
instance: ::vst::host::PluginInstance
|
instance: ::vst::host::PluginInstance
|
||||||
|
|
@ -37,29 +35,27 @@ impl Plugin {
|
||||||
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
let (client, _status) = Client::new(name, ClientOptions::NO_START_SERVER)?;
|
||||||
DynamicDevice::new(render, handle, Self::process, Self {
|
DynamicDevice::new(render, handle, Self::process, Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
input: client.register_port("in", MidiIn::default())?,
|
path: HELM.into(),
|
||||||
outputs: [
|
plugin: Some(self::lv2::plug(HELM)?),
|
||||||
|
offset: 0,
|
||||||
|
selected: 0,
|
||||||
|
mapping: false,
|
||||||
|
midi_in: client.register_port("in", MidiIn::default())?,
|
||||||
|
audio_out: [
|
||||||
client.register_port("outL", AudioOut::default())?,
|
client.register_port("outL", AudioOut::default())?,
|
||||||
client.register_port("outR", AudioOut::default())?,
|
client.register_port("outR", AudioOut::default())?,
|
||||||
],
|
],
|
||||||
path: HELM.into(),
|
|
||||||
plugin: Some(self::lv2::plug_in(HELM)?),
|
|
||||||
offset: 0,
|
|
||||||
selected: 0,
|
|
||||||
midi_map_enable: false
|
|
||||||
}).activate(client)
|
}).activate(client)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
pub fn process (&mut self, _: &Client, scope: &ProcessScope) -> Control {
|
||||||
match self.plugin.as_mut() {
|
match self.plugin.as_mut() {
|
||||||
Some(PluginKind::LV2 {
|
Some(PluginKind::LV2 { features, ref mut instance, .. }) => {
|
||||||
features, input, ref mut instance, ..
|
|
||||||
}) => {
|
|
||||||
let mut input = ::livi::event::LV2AtomSequence::new(
|
let mut input = ::livi::event::LV2AtomSequence::new(
|
||||||
&features, scope.n_frames() as usize
|
&features, scope.n_frames() as usize
|
||||||
);
|
);
|
||||||
let urid = features.midi_urid();
|
let urid = features.midi_urid();
|
||||||
for event in self.input.iter(scope) {
|
for event in self.midi_in.iter(scope) {
|
||||||
match event.bytes.len() {
|
match event.bytes.len() {
|
||||||
3 => input.push_midi_event::<3>(
|
3 => input.push_midi_event::<3>(
|
||||||
event.time as i64,
|
event.time as i64,
|
||||||
|
|
@ -70,18 +66,9 @@ impl Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ports = ::livi::EmptyPortConnections::new()
|
let ports = ::livi::EmptyPortConnections::new()
|
||||||
.with_atom_sequence_inputs(
|
.with_atom_sequence_inputs(std::iter::once(&input))
|
||||||
std::iter::once(&input)
|
.with_audio_outputs(self.audio_out.iter_mut().map(|o|o.as_mut_slice(scope)));
|
||||||
)
|
unsafe { instance.run(scope.n_frames() as usize, ports).unwrap() };
|
||||||
.with_audio_outputs(
|
|
||||||
self.outputs.iter_mut().map(|o|o.as_mut_slice(scope)),
|
|
||||||
);
|
|
||||||
unsafe {
|
|
||||||
instance.run(
|
|
||||||
scope.n_frames() as usize,
|
|
||||||
ports
|
|
||||||
).unwrap()
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +76,19 @@ impl Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::vst::host::Host for Plugin {}
|
impl PortList for Plugin {
|
||||||
|
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||||
|
Ok(vec![
|
||||||
|
self.midi_in.name()?
|
||||||
|
])
|
||||||
|
}
|
||||||
|
fn audio_outs (&self) -> Usually<Vec<String>> {
|
||||||
|
Ok(vec![
|
||||||
|
self.audio_out[0].name()?,
|
||||||
|
self.audio_out[1].name()?
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect)
|
pub fn render (state: &Plugin, buf: &mut Buffer, area: Rect)
|
||||||
-> Usually<Rect>
|
-> Usually<Rect>
|
||||||
|
|
@ -150,17 +149,8 @@ pub fn handle (s: &mut Plugin, event: &AppEvent) -> Usually<bool> {
|
||||||
}],
|
}],
|
||||||
[Char('m'), NONE, "toggle_midi_map", "toggle midi map mode",
|
[Char('m'), NONE, "toggle_midi_map", "toggle midi map mode",
|
||||||
|s: &mut Plugin|{
|
|s: &mut Plugin|{
|
||||||
s.midi_map_enable = !s.midi_map_enable;
|
s.mapping = !s.mapping;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}]
|
}]
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DevicePorts for Plugin {
|
|
||||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
|
||||||
Ok(vec!["in".into()])
|
|
||||||
}
|
|
||||||
fn audio_outs (&self) -> Usually<Vec<String>> {
|
|
||||||
Ok(vec!["outL".into(), "outR".into()])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn plug_in (uri: &str) -> Usually<PluginKind> {
|
pub fn plug (uri: &str) -> Usually<PluginKind> {
|
||||||
let world = ::livi::World::with_load_bundle(&uri);
|
let world = ::livi::World::with_load_bundle(&uri);
|
||||||
let features = world.build_features(::livi::FeaturesBuilder {
|
let features = world.build_features(::livi::FeaturesBuilder {
|
||||||
min_block_length: 1,
|
min_block_length: 1,
|
||||||
|
|
@ -18,11 +18,6 @@ pub fn plug_in (uri: &str) -> Usually<PluginKind> {
|
||||||
portList.push(port);
|
portList.push(port);
|
||||||
}
|
}
|
||||||
Ok(PluginKind::LV2 {
|
Ok(PluginKind::LV2 {
|
||||||
input: ::livi::event::LV2AtomSequence::new(&features, 1024),
|
|
||||||
outputs: [
|
|
||||||
vec![0.0; features.max_block_length()],
|
|
||||||
vec![0.0; features.max_block_length()],
|
|
||||||
],
|
|
||||||
instance: unsafe {
|
instance: unsafe {
|
||||||
plugin.instantiate(features.clone(), 48000.0).expect("boop")
|
plugin.instantiate(features.clone(), 48000.0).expect("boop")
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
impl ::vst::host::Host for Plugin {}
|
||||||
|
|
||||||
fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, path: &str) -> Usually<PluginKind> {
|
fn set_vst_plugin (host: &Arc<Mutex<Plugin>>, path: &str) -> Usually<PluginKind> {
|
||||||
let mut loader = ::vst::host::PluginLoader::load(
|
let mut loader = ::vst::host::PluginLoader::load(
|
||||||
&std::path::Path::new("/nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lxvst/helm.so"),
|
&std::path::Path::new("/nix/store/ij3sz7nqg5l7v2dygdvzy3w6cj62bd6r-helm-0.9.0/lib/lxvst/helm.so"),
|
||||||
|
|
|
||||||
|
|
@ -20,13 +20,13 @@ impl Sequence {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Sequencer {
|
pub struct Sequencer {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// JACK transport handle.
|
/// JACK transport handle.
|
||||||
transport: ::jack::Transport,
|
transport: ::jack::Transport,
|
||||||
/// JACK MIDI input port that will be created.
|
/// JACK MIDI input port that will be created.
|
||||||
input_port: Port<MidiIn>,
|
pub midi_in: Port<MidiIn>,
|
||||||
/// JACK MIDI output port that will be created.
|
/// JACK MIDI output port that will be created.
|
||||||
output_port: Port<MidiOut>,
|
pub midi_out: Port<MidiOut>,
|
||||||
|
|
||||||
/// Holds info about tempo
|
/// Holds info about tempo
|
||||||
timebase: Arc<Timebase>,
|
timebase: Arc<Timebase>,
|
||||||
|
|
@ -79,8 +79,8 @@ impl Sequencer {
|
||||||
let state = transport.query_state()?;
|
let state = transport.query_state()?;
|
||||||
DynamicDevice::new(render, handle, Self::process, Self {
|
DynamicDevice::new(render, handle, Self::process, Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
input_port: client.register_port("in", MidiIn::default())?,
|
midi_in: client.register_port("in", MidiIn::default())?,
|
||||||
output_port: client.register_port("out", MidiOut::default())?,
|
midi_out: client.register_port("out", MidiOut::default())?,
|
||||||
|
|
||||||
timebase: timebase.clone(),
|
timebase: timebase.clone(),
|
||||||
steps: 64,
|
steps: 64,
|
||||||
|
|
@ -155,7 +155,7 @@ impl Sequencer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read from input, write inputs to sequence and/or output buffer
|
// Read from input, write inputs to sequence and/or output buffer
|
||||||
for event in self.input_port.iter(scope) {
|
for event in self.midi_in.iter(scope) {
|
||||||
let tick = tick as u32;
|
let tick = tick as u32;
|
||||||
let msg = midly::live::LiveEvent::parse(event.bytes).unwrap();
|
let msg = midly::live::LiveEvent::parse(event.bytes).unwrap();
|
||||||
match msg {
|
match msg {
|
||||||
|
|
@ -208,7 +208,7 @@ impl Sequencer {
|
||||||
|
|
||||||
// Write to port from output buffer
|
// Write to port from output buffer
|
||||||
// (containing notes from sequence and/or monitor)
|
// (containing notes from sequence and/or monitor)
|
||||||
let mut writer = self.output_port.writer(scope);
|
let mut writer = self.midi_out.writer(scope);
|
||||||
for time in 0..scope.n_frames() {
|
for time in 0..scope.n_frames() {
|
||||||
if let Some(Some(frame)) = output.get_mut(time as usize) {
|
if let Some(Some(frame)) = output.get_mut(time as usize) {
|
||||||
for event in frame.iter() {
|
for event in frame.iter() {
|
||||||
|
|
@ -222,12 +222,12 @@ impl Sequencer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DevicePorts for Sequencer {
|
impl PortList for Sequencer {
|
||||||
fn midi_ins (&self) -> Usually<Vec<String>> {
|
fn midi_ins (&self) -> Usually<Vec<String>> {
|
||||||
Ok(vec!["in".into()])
|
Ok(vec![self.midi_in.name()?])
|
||||||
}
|
}
|
||||||
fn midi_outs (&self) -> Usually<Vec<String>> {
|
fn midi_outs (&self) -> Usually<Vec<String>> {
|
||||||
Ok(vec!["out".into()])
|
Ok(vec![self.midi_out.name()?])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,5 +19,5 @@ fn main () -> Result<(), Box<dyn Error>> {
|
||||||
let _cli = cli::Cli::parse();
|
let _cli = cli::Cli::parse();
|
||||||
let xdg = microxdg::XdgApp::new("dawdle")?;
|
let xdg = microxdg::XdgApp::new("dawdle")?;
|
||||||
crate::config::create_dirs(&xdg)?;
|
crate::config::create_dirs(&xdg)?;
|
||||||
run(Launcher::new("Launcher#0")?)
|
run(Launcher::new_with_controller("Launcher#0", ".*nanoKEY.*")?)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue