Being a multi thread newbie, I want to create a solution that makes it possible to use my fancy macro keyboard (Blackmagic Speed Entry, originally created for controlling Davinci Resolve Video NLE) to control my music notation software (Steinberg Dorico) via its websocket Remote API.
The solution below does the basics:
- it sets up a messaging channel for app-internal communication
- it sets up the websocket connection to the software, and performs necessary handshaking
- it sets up the macro keyboard connection, and uses its listener callbacks to broadcast messages with info about what keys are pressed.
- it sets up an message reciever loop to deal with the messages, and uses the websocket to send commands back to the software
It listens to messages FROM the macro keyboard) and sends the corresponding commands TO the websocket/software.
// set up a messaging channel using the following AppMessage enum
#[derive(Debug)]
enum AppMessage {
SpeedKey(Key, bool),
}
// create a channel for sending messages to the websocket
let (app_message_sender, app_message_reciever) = mpsc::channel::<AppMessage>();
//--------------------------------------------------------------------
// setup websocket connection to Dorico notation software
let mut dorico_web_socket = connect("ws://127.0.0.1:4560").unwrap().0;
// ... and do some handshaking with the software
// <-- insert thread with listener loop for messages from the dorico_web_socket here?
//--------------------------------------------------------------------
// set up the Blackmagic Speed Editor macro keyboard HidDevice connection
let mut speed_editor_kbd = bmd_speededitor::new().unwrap();
// clone the app_message_sender for use in the on_key callback
let sender_kbd = app_message_sender.clone();
// add a onkey listener to the macro keyboard
speed_editor_kbd.on_key(move |key, down| {
// use the cloned app_message_sender to send a message with key and down state
sender_kbd.send(AppMessage::SpeedKey(key, down)).expect("On key error");
Ok(())
});
// kickoff the speed editor keyboard
let handle_speed_editor = thread::spawn(move || {
speed_editor_kbd.run().expect("Expected Speed Editor to run");
});
//--------------------------------------------------------------------
// listen to messages from the app_message_reciever
for app_message in app_message_reciever {
match app_message {
AppMessage::SpeedKey(key, down) => {
// match the current pressed key state
match key {
Key::Cam1 => {
// send a message via the websocket to the software
&dorico_web_socket.send(Message::Text("Dorico-command-123").into()));
}
Key::Cam2 => {
//
}
}
}
}
}
However, I also want to listen to messages FROM the websocket/sofware and deal with these.
My problem is that I havent found a way to set up a listener loop for websocket messages without consuming away the websocket itself - thus making it unavailable when it's needed later on in the program. If I plug in the following snippet at the <-- insert thread with listener loop
above, the &dorico_web_socket
is consumed and can not be used later.
let handle_dorico_socket = thread::spawn(move || loop {
let msg = &dorico_web_socket.read().unwrap(); // websocket is consumed here
match msg {
Message::Text(message_from_dorico) => {
// broadcast an AppMessage or something...
}
_ => {}
}
});
What is the best way to deal with this scenario?