connect/
connect.rs

1extern crate easyjack as jack;
2extern crate getopts;
3
4use getopts::Options;
5use std::env;
6use std::io::Write;
7use std::io::stderr;
8use std::process::exit;
9use std::sync::mpsc::{Sender, Receiver};
10use std::sync::mpsc;
11
12// bring the trait for all common port operations into scope
13use jack::Port;
14
15enum Mode {
16    Connect(String, String),
17    Disconnect(String, String),
18}
19
20/// An example of a struct wrapping a jack client
21/// This pattern is overkill for this example program, but it serves to
22/// demonstrate the pattern which the easyjack wrapper is designed to
23/// accommodate.
24/// The callback code communicates back to the main thread via a channel and
25/// have the main thread performs actions
26struct Connector<'a> {
27    client: jack::Client<'a>,
28
29    /// the incoming end of the channel running on the other thread
30    /// The channel can receive messages composed of an Option of a pair of port ids
31    incoming: Receiver<(jack::PortId, jack::PortId, jack::PortConnectStatus)>,
32}
33
34impl<'a> Connector<'a> {
35    fn new(servername: Option<String>) -> Result<Self, jack::status::Status> {
36        // we don't want to start a server if none is already started
37        let opts   = jack::options::NO_START_SERVER;
38        let myname = "connector";
39        let client = match servername {
40            None             => jack::Client::open(myname, opts),
41            Some(servername) => jack::Client::open_connection_to(myname, &*servername, opts),
42        };
43
44        let client = match client {
45            Ok((cl, _)) => cl,
46            Err(code)   => return Err(code)
47        };
48
49        // create our channel to communicate with the handler
50        let (tx, rx) = mpsc::channel();
51
52        // create the handler and give it the transmission end of the channel
53        let handler = ConnectorHandler { outgoing: tx };
54
55        // create an instance of the Connector, then set up the handler
56        let mut conn = Connector { client: client, incoming: rx };
57        conn.client.set_metadata_handler(handler).unwrap();
58
59        Ok(conn)
60    }
61
62    fn activate(&mut self) -> Result<(), jack::status::Status> {
63        self.client.activate()
64    }
65
66    fn connect(&mut self, port1: &str, port2: &str) -> Result<(), jack::status::Status> {
67        self.client.connect_ports(port1, port2)
68    }
69
70    fn disconnect(&mut self, port1: &str, port2: &str) -> Result<(), jack::status::Status> {
71        self.client.disconnect_ports(port1, port2)
72    }
73
74    fn wait_and_shutdown(self) {
75        let (a, b, stat) = self.incoming.recv().unwrap();
76        let n1 = self.client.get_port_by_id(a).unwrap().get_name();
77        let n2 = self.client.get_port_by_id(b).unwrap().get_name();
78
79        match stat {
80            jack::PortConnectStatus::PortsConnected =>
81                println!("ports connected: {} and {}", n1, n2),
82
83            jack::PortConnectStatus::PortsDisconnected =>
84                println!("ports disconnected: {} and {}", n1, n2)
85        }
86    }
87}
88
89
90/// the struct which will actually handle the jack callback
91struct ConnectorHandler {
92    outgoing: Sender<(jack::PortId, jack::PortId, jack::PortConnectStatus)>
93}
94
95impl jack::MetadataHandler for ConnectorHandler {
96    fn on_port_connect(&mut self, a: jack::PortId, b: jack::PortId, status: jack::PortConnectStatus) {
97        // a connection happened, this is probably the connection we made
98        // if not, that's a shame, we don't handle this case
99        // send a message to the channel so that the main thread knows it is
100        // safe to shutdown
101        self.outgoing.send( (a, b, status) ).unwrap();
102    }
103
104    fn callbacks_of_interest(&self) -> Vec<jack::MetadataHandlers> {
105        vec![jack::MetadataHandlers::PortConnect]
106    }
107}
108
109fn do_connect(server: Option<String>, mode: Mode) {
110    // create a connector
111    let mut connector = match Connector::new(server) {
112        Ok(conn) => conn,
113        Err(code) => {
114            println!("could not create connector: {:?}", code);
115            return
116        }
117    };
118
119    // activate the connector
120    match connector.activate() {
121        Ok(()) => (),
122        Err(code) => {
123            println!("could not activate client: {:?}", code);
124            return
125        }
126    }
127
128
129    // make the connection (or disconnect some ports)
130    match mode {
131        Mode::Connect(p1, p2) => {
132            match connector.connect(p1.as_str(), p2.as_str()) {
133                Ok(())    => (),
134                Err(code) => {
135                    println!("Connect failed because: {:?}", code);
136                    return
137                }
138            }
139        },
140
141        Mode::Disconnect(p1, p2) => {
142            match connector.disconnect(p1.as_str(), p2.as_str()) {
143                Ok(())    => (),
144                Err(code) => {
145                    println!("Disconnect failed because: {:?}", code);
146                    return
147                }
148            }
149        }
150    }
151
152    // wait for a bit, and shutdown
153    connector.wait_and_shutdown();
154}
155
156fn usage_with_error(exe: String, err: &str, opts: Options) -> ! {
157    let brief = format!("Error! {}\n{} port1 port2", err, opts.short_usage(&exe));
158    let use_me = opts.usage(&brief);
159    writeln!(&mut stderr(), "{}", use_me).unwrap();
160    exit(1);
161}
162
163fn main() {
164    // the main method contains a bunch of argument handling, don't worry very much about it!
165    // setup the arg parser
166    let mut opts = Options::new();
167    opts.optopt("s", "servername", "name of the server to connect to", "NAME");
168
169    // one of these is required, will check manually
170    opts.optflag("c", "connect", "run in connect mode");
171    opts.optflag("d", "disconnect", "run in disconnect mode");
172
173    let args: Vec<String> = env::args().collect();
174    let exe = args[0].clone();
175    let matches = match opts.parse(&args[1..]) {
176        Ok(m)  => m,
177        Err(f) => panic!(f.to_string())
178    };
179
180    // do additional validation
181    if !matches.opt_present("c") && !matches.opt_present("d") {
182        usage_with_error(exe, "Did not specify either -c or -d", opts);
183    }
184
185    if matches.opt_present("c") && matches.opt_present("d") {
186        usage_with_error(exe, "Cannot specify both -c and -d", opts);
187    }
188
189    if matches.free.len() != 2 {
190        usage_with_error(exe, "Did not specify exactly 2 ports!", opts);
191    }
192
193    // set up some variables
194    let port1 = matches.free[0].clone();
195    let port2 = matches.free[1].clone();
196    let server_name = matches.opt_str("s");
197
198    let mode = if matches.opt_present("c") {
199        Mode::Connect(port1, port2)
200    } else {
201        Mode::Disconnect(port1, port2)
202    };
203
204    do_connect(server_name, mode)
205}