commit
This commit is contained in:
parent
1ced4381b8
commit
f44283160a
10 changed files with 181 additions and 138 deletions
|
@ -50,7 +50,6 @@ pub enum HandlerEvent {
|
||||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||||
#[serde(tag = "command", rename_all = "kebab-case")]
|
#[serde(tag = "command", rename_all = "kebab-case")]
|
||||||
pub enum HandlerCommand {
|
pub enum HandlerCommand {
|
||||||
SetActivePages { key_page_id: String, knob_page_id: String },
|
|
||||||
SetKeyStyle { path: KeyPath, value: Option<KeyStyle> },
|
SetKeyStyle { path: KeyPath, value: Option<KeyStyle> },
|
||||||
SetKnobStyle { path: KnobPath, value: Option<KnobStyle> },
|
SetKnobStyle { path: KnobPath, value: Option<KnobStyle> },
|
||||||
SetKnobValue { path: KnobPath, value: Option<f32> },
|
SetKnobValue { path: KnobPath, value: Option<f32> },
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::style::{KeyStyle, KnobStyle};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Key {
|
pub struct Key {
|
||||||
pub path: KeyPath,
|
pub path: KeyPath,
|
||||||
|
pub host_id: Box<str>,
|
||||||
pub base_style: KeyStyle,
|
pub base_style: KeyStyle,
|
||||||
pub style: Option<KeyStyle>,
|
pub style: Option<KeyStyle>,
|
||||||
}
|
}
|
||||||
|
@ -13,6 +14,7 @@ pub struct Key {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Knob {
|
pub struct Knob {
|
||||||
pub path: KnobPath,
|
pub path: KnobPath,
|
||||||
|
pub host_id: Box<str>,
|
||||||
pub base_style: KnobStyle,
|
pub base_style: KnobStyle,
|
||||||
pub style: Option<KnobStyle>,
|
pub style: Option<KnobStyle>,
|
||||||
pub value: Option<f32>,
|
pub value: Option<f32>,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use resvg::usvg::tiny_skia_path::PathBuilder;
|
use resvg::usvg::tiny_skia_path::PathBuilder;
|
||||||
|
@ -18,10 +19,11 @@ pub struct GraphicsContext {
|
||||||
pub icon_manager: IconManager,
|
pub icon_manager: IconManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_key(context: &GraphicsContext, key_size: IntSize, state: Option<&Key>) -> Bytes {
|
pub fn render_key(context: &GraphicsContext, key_size: IntSize, active_remote_handler_host_ids: &HashSet<Box<str>>, state: Option<&Key>) -> Bytes {
|
||||||
let mut pixmap = Pixmap::new(key_size.width(), key_size.height()).expect("constraints were already asserted by IntSize");
|
let mut pixmap = Pixmap::new(key_size.width(), key_size.height()).expect("constraints were already asserted by IntSize");
|
||||||
|
|
||||||
if let Some(state) = state {
|
if let Some(state) = state {
|
||||||
|
if active_remote_handler_host_ids.contains(&state.host_id) {
|
||||||
let style = state.style.as_ref().map(|s| s.merge_over(&state.base_style));
|
let style = state.style.as_ref().map(|s| s.merge_over(&state.base_style));
|
||||||
let style = style.as_ref().unwrap_or(&state.base_style);
|
let style = style.as_ref().unwrap_or(&state.base_style);
|
||||||
|
|
||||||
|
@ -38,8 +40,9 @@ pub fn render_key(context: &GraphicsContext, key_size: IntSize, state: Option<&K
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(color) = style.border {
|
if let Some(color) = style.border {
|
||||||
let path =
|
let path = PathBuilder::from_rect(
|
||||||
PathBuilder::from_rect(Rect::from_xywh(-1.0, -2.0, pixmap.width() as f32, pixmap.height() as f32).expect("width and height are not negative"));
|
Rect::from_xywh(-1.0, -2.0, pixmap.width() as f32, pixmap.height() as f32).expect("width and height are not negative"),
|
||||||
|
);
|
||||||
|
|
||||||
let paint = Paint {
|
let paint = Paint {
|
||||||
shader: Shader::SolidColor(Color::from_rgba8(color.r, color.g, color.b, color.a)),
|
shader: Shader::SolidColor(Color::from_rgba8(color.r, color.g, color.b, color.a)),
|
||||||
|
@ -57,14 +60,16 @@ pub fn render_key(context: &GraphicsContext, key_size: IntSize, state: Option<&K
|
||||||
pixmap.stroke_path(&path, &paint, &STROKE, Transform::identity(), None);
|
pixmap.stroke_path(&path, &paint, &STROKE, Transform::identity(), None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
convert_pixels_to_rgb565(pixmap.pixels(), context.buffer_endianness).freeze()
|
convert_pixels_to_rgb565(pixmap.pixels(), context.buffer_endianness).freeze()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_knob(context: &GraphicsContext, screen_size: IntSize, state: Option<&Knob>) -> Bytes {
|
pub fn render_knob(context: &GraphicsContext, screen_size: IntSize, active_remote_handler_host_ids: &HashSet<Box<str>>, state: Option<&Knob>) -> Bytes {
|
||||||
let mut pixmap = Pixmap::new(screen_size.width(), screen_size.height()).expect("constraints were already asserted by IntSize");
|
let mut pixmap = Pixmap::new(screen_size.width(), screen_size.height()).expect("constraints were already asserted by IntSize");
|
||||||
|
|
||||||
if let Some(state) = state {
|
if let Some(state) = state {
|
||||||
|
if active_remote_handler_host_ids.contains(&state.host_id) {
|
||||||
let style = state.style.as_ref().map(|s| s.merge_over(&state.base_style));
|
let style = state.style.as_ref().map(|s| s.merge_over(&state.base_style));
|
||||||
let style = style.as_ref().unwrap_or(&state.base_style);
|
let style = style.as_ref().unwrap_or(&state.base_style);
|
||||||
|
|
||||||
|
@ -115,6 +120,7 @@ pub fn render_knob(context: &GraphicsContext, screen_size: IntSize, state: Optio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
convert_pixels_to_rgb565(pixmap.pixels(), context.buffer_endianness).freeze()
|
convert_pixels_to_rgb565(pixmap.pixels(), context.buffer_endianness).freeze()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,17 +21,25 @@ use crate::icons::IconManager;
|
||||||
use crate::model::coordinator_config::Config;
|
use crate::model::coordinator_config::Config;
|
||||||
use crate::model::position::ButtonPosition;
|
use crate::model::position::ButtonPosition;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
pub enum CoordinatorCommand {
|
||||||
|
SetActivePages { key_page_id: String, knob_page_id: String },
|
||||||
|
SetRemoteHostIsActive { host_id: Box<str>, is_active: bool },
|
||||||
|
}
|
||||||
|
|
||||||
enum IoWork {
|
enum IoWork {
|
||||||
DeviceEvent(LoupedeckEvent),
|
DeviceEvent(LoupedeckEvent),
|
||||||
Command(HandlerCommand),
|
HandlerCommand(HandlerCommand),
|
||||||
|
CoordinatorCommand(CoordinatorCommand),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct IoWorkerContext {
|
pub struct IoWorkerContext {
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
device: LoupedeckDevice,
|
device: LoupedeckDevice,
|
||||||
commands_sender: flume::Sender<HandlerCommand>,
|
coordinator_commands_sender: flume::Sender<CoordinatorCommand>,
|
||||||
events_sender: broadcast::Sender<HandlerEvent>,
|
events_sender: broadcast::Sender<HandlerEvent>,
|
||||||
graphics: GraphicsContext,
|
graphics: GraphicsContext,
|
||||||
|
state: State,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IoWorkerContext {
|
impl IoWorkerContext {
|
||||||
|
@ -39,52 +47,59 @@ impl IoWorkerContext {
|
||||||
config_directory: &Path,
|
config_directory: &Path,
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
device: LoupedeckDevice,
|
device: LoupedeckDevice,
|
||||||
commands_sender: flume::Sender<HandlerCommand>,
|
coordinator_commands_sender: flume::Sender<CoordinatorCommand>,
|
||||||
events_sender: broadcast::Sender<HandlerEvent>,
|
events_sender: broadcast::Sender<HandlerEvent>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let buffer_endianness = device.characteristics().key_grid.display.endianness;
|
let buffer_endianness = device.characteristics().key_grid.display.endianness;
|
||||||
let label_renderer = RefCell::new(LabelRenderer::new(config.label_font_family.as_ref()));
|
let label_renderer = RefCell::new(LabelRenderer::new(config.label_font_family.as_ref()));
|
||||||
let dpi = device.characteristics().key_grid.display.dpi;
|
let dpi = device.characteristics().key_grid.display.dpi;
|
||||||
let icon_packs = Arc::clone(&config.icon_packs);
|
let icon_packs = Arc::clone(&config.icon_packs);
|
||||||
|
let state = State::create(&config);
|
||||||
|
|
||||||
IoWorkerContext {
|
IoWorkerContext {
|
||||||
config,
|
config,
|
||||||
device,
|
device,
|
||||||
commands_sender,
|
coordinator_commands_sender,
|
||||||
events_sender,
|
events_sender,
|
||||||
graphics: GraphicsContext {
|
graphics: GraphicsContext {
|
||||||
buffer_endianness,
|
buffer_endianness,
|
||||||
label_renderer,
|
label_renderer,
|
||||||
icon_manager: IconManager::new(config_directory.to_path_buf(), icon_packs, dpi),
|
icon_manager: IconManager::new(config_directory.to_path_buf(), icon_packs, dpi),
|
||||||
},
|
},
|
||||||
|
state,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn do_io_work(context: IoWorkerContext, commands_receiver: flume::Receiver<HandlerCommand>) {
|
pub fn do_io_work(
|
||||||
let mut state = State::create(&context.config);
|
mut context: IoWorkerContext,
|
||||||
|
coordinator_commands_receiver: &flume::Receiver<CoordinatorCommand>,
|
||||||
|
handler_commands_receiver: &flume::Receiver<HandlerCommand>,
|
||||||
|
) {
|
||||||
let device_events_receiver = context.device.events();
|
let device_events_receiver = context.device.events();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let a = flume::Selector::new()
|
let work = flume::Selector::new()
|
||||||
.recv(&device_events_receiver, |e| {
|
.recv(&device_events_receiver, |e| {
|
||||||
IoWork::DeviceEvent(e.expect("the device events channel is not closed by the other side"))
|
IoWork::DeviceEvent(e.expect("the device events channel is not closed by the other side"))
|
||||||
})
|
})
|
||||||
.recv(&commands_receiver, |c| IoWork::Command(c.unwrap()))
|
.recv(coordinator_commands_receiver, |c| IoWork::CoordinatorCommand(c.unwrap()))
|
||||||
|
.recv(handler_commands_receiver, |c| IoWork::HandlerCommand(c.unwrap()))
|
||||||
.wait();
|
.wait();
|
||||||
|
|
||||||
match a {
|
match work {
|
||||||
IoWork::DeviceEvent(event) => {
|
IoWork::DeviceEvent(event) => {
|
||||||
if !handle_event(&context, &mut state, event) {
|
if !handle_event(&mut context, event) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IoWork::Command(command) => handle_command(&context, &mut state, command),
|
IoWork::CoordinatorCommand(command) => handle_coordinator_command(&mut context, command),
|
||||||
|
IoWork::HandlerCommand(command) => handle_handler_command(&mut context, command),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_event(context: &IoWorkerContext, state: &mut State, event: LoupedeckEvent) -> bool {
|
fn handle_event(context: &mut IoWorkerContext, event: LoupedeckEvent) -> bool {
|
||||||
log::trace!("Handling event: {:?}", &event);
|
log::trace!("Handling event: {:?}", &event);
|
||||||
|
|
||||||
let send_key_event = |path: KeyPath, event: KeyEvent| {
|
let send_key_event = |path: KeyPath, event: KeyEvent| {
|
||||||
|
@ -104,10 +119,10 @@ fn handle_event(context: &IoWorkerContext, state: &mut State, event: LoupedeckEv
|
||||||
let button_config = &context.config.buttons[position];
|
let button_config = &context.config.buttons[position];
|
||||||
|
|
||||||
context
|
context
|
||||||
.commands_sender
|
.coordinator_commands_sender
|
||||||
.send(HandlerCommand::SetActivePages {
|
.send(CoordinatorCommand::SetActivePages {
|
||||||
key_page_id: button_config.key_page.as_ref().unwrap_or(&state.active_key_page_id).clone(),
|
key_page_id: button_config.key_page.as_ref().unwrap_or(&context.state.active_key_page_id).clone(),
|
||||||
knob_page_id: button_config.knob_page.as_ref().unwrap_or(&state.active_knob_page_id).clone(),
|
knob_page_id: button_config.knob_page.as_ref().unwrap_or(&context.state.active_knob_page_id).clone(),
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -125,7 +140,7 @@ fn handle_event(context: &IoWorkerContext, state: &mut State, event: LoupedeckEv
|
||||||
};
|
};
|
||||||
|
|
||||||
let path = KeyPath {
|
let path = KeyPath {
|
||||||
page_id: state.active_key_page_id.clone(),
|
page_id: context.state.active_key_page_id.clone(),
|
||||||
position,
|
position,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -137,10 +152,10 @@ fn handle_event(context: &IoWorkerContext, state: &mut State, event: LoupedeckEv
|
||||||
.expect("the key index is valid because is was returned by get_key_at_global_coordinates");
|
.expect("the key index is valid because is was returned by get_key_at_global_coordinates");
|
||||||
|
|
||||||
let kind = if is_end {
|
let kind = if is_end {
|
||||||
state.active_touch_ids.remove(&touch_id);
|
context.state.active_touch_ids.remove(&touch_id);
|
||||||
KeyTouchEventKind::End
|
KeyTouchEventKind::End
|
||||||
} else {
|
} else {
|
||||||
let is_new = state.active_touch_ids.insert(touch_id);
|
let is_new = context.state.active_touch_ids.insert(touch_id);
|
||||||
if is_new {
|
if is_new {
|
||||||
KeyTouchEventKind::Start
|
KeyTouchEventKind::Start
|
||||||
} else {
|
} else {
|
||||||
|
@ -170,7 +185,7 @@ fn handle_event(context: &IoWorkerContext, state: &mut State, event: LoupedeckEv
|
||||||
|
|
||||||
send_knob_event(
|
send_knob_event(
|
||||||
KnobPath {
|
KnobPath {
|
||||||
page_id: state.active_knob_page_id.clone(),
|
page_id: context.state.active_knob_page_id.clone(),
|
||||||
position,
|
position,
|
||||||
},
|
},
|
||||||
KnobEvent::Rotate {
|
KnobEvent::Rotate {
|
||||||
|
@ -186,7 +201,7 @@ fn handle_event(context: &IoWorkerContext, state: &mut State, event: LoupedeckEv
|
||||||
|
|
||||||
send_knob_event(
|
send_knob_event(
|
||||||
KnobPath {
|
KnobPath {
|
||||||
page_id: state.active_knob_page_id.clone(),
|
page_id: context.state.active_knob_page_id.clone(),
|
||||||
position,
|
position,
|
||||||
},
|
},
|
||||||
KnobEvent::Press,
|
KnobEvent::Press,
|
||||||
|
@ -198,46 +213,53 @@ fn handle_event(context: &IoWorkerContext, state: &mut State, event: LoupedeckEv
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_command(context: &IoWorkerContext, state: &mut State, command: HandlerCommand) {
|
fn handle_coordinator_command(context: &mut IoWorkerContext, command: CoordinatorCommand) {
|
||||||
log::trace!("Handling command: {:?}", &command);
|
log::trace!("Handling coordinator command: {:?}", &command);
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
HandlerCommand::SetActivePages { key_page_id, knob_page_id } => {
|
CoordinatorCommand::SetActivePages { key_page_id, knob_page_id } => {
|
||||||
state.active_key_page_id = key_page_id;
|
context.state.active_key_page_id = key_page_id;
|
||||||
state.active_knob_page_id = knob_page_id;
|
context.state.active_knob_page_id = knob_page_id;
|
||||||
|
|
||||||
for button in context.device.characteristics().available_buttons {
|
for button in context.device.characteristics().available_buttons {
|
||||||
context
|
context
|
||||||
.device
|
.device
|
||||||
.set_button_color(button, get_correct_button_color(context, state, ButtonPosition::of(&button)))
|
.set_button_color(button, get_correct_button_color(context, &context.state, ButtonPosition::of(&button)))
|
||||||
.expect("the button is available for this device because that is literally what we are iterating over");
|
.expect("the button is available for this device because that is literally what we are iterating over");
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_grid = &context.device.characteristics().key_grid;
|
redraw_visible_page(&context);
|
||||||
for index in 0..(key_grid.rows * key_grid.columns) {
|
}
|
||||||
draw_key_at_index(context, state, index);
|
CoordinatorCommand::SetRemoteHostIsActive { host_id, is_active } => {
|
||||||
|
if is_active {
|
||||||
|
context.state.active_remote_handler_host_ids.insert(host_id);
|
||||||
|
} else {
|
||||||
|
context.state.active_remote_handler_host_ids.remove(&host_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
for position in KnobPosition::VARIANTS {
|
redraw_visible_page(&context);
|
||||||
draw_knob_at_position(context, state, *position);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.device.refresh_display(&key_grid.display).unwrap();
|
fn handle_handler_command(context: &mut IoWorkerContext, command: HandlerCommand) {
|
||||||
}
|
log::trace!("Handling handler command: {:?}", &command);
|
||||||
|
|
||||||
|
match command {
|
||||||
HandlerCommand::SetKeyStyle { path, value } => {
|
HandlerCommand::SetKeyStyle { path, value } => {
|
||||||
state.mutate_key_for_command("SetKeyStyle", &path, |k| {
|
context.state.mutate_key_for_command("SetKeyStyle", &path, |k| {
|
||||||
k.style = value;
|
k.style = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
draw_key_at_path_if_visible(context, state, path);
|
draw_key_at_path_if_visible(context, path);
|
||||||
context.device.refresh_display(&context.device.characteristics().key_grid.display).unwrap();
|
context.device.refresh_display(&context.device.characteristics().key_grid.display).unwrap();
|
||||||
}
|
}
|
||||||
HandlerCommand::SetKnobStyle { path, value } => {
|
HandlerCommand::SetKnobStyle { path, value } => {
|
||||||
state.mutate_knob_for_command("SetKnobStyle", &path, |k| {
|
context.state.mutate_knob_for_command("SetKnobStyle", &path, |k| {
|
||||||
k.style = value;
|
k.style = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
draw_knob_at_path_if_visible(context, state, path);
|
draw_knob_at_path_if_visible(context, path);
|
||||||
context.device.refresh_display(&context.device.characteristics().key_grid.display).unwrap();
|
context.device.refresh_display(&context.device.characteristics().key_grid.display).unwrap();
|
||||||
}
|
}
|
||||||
HandlerCommand::SetKnobValue { path, value } => {
|
HandlerCommand::SetKnobValue { path, value } => {
|
||||||
|
@ -248,15 +270,28 @@ fn handle_command(context: &IoWorkerContext, state: &mut State, command: Handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.mutate_knob_for_command("SetKnobValue", &path, |k| {
|
context.state.mutate_knob_for_command("SetKnobValue", &path, |k| {
|
||||||
k.value = value;
|
k.value = value;
|
||||||
});
|
});
|
||||||
|
|
||||||
draw_knob_at_path_if_visible(context, state, path);
|
draw_knob_at_path_if_visible(context, path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn redraw_visible_page(context: &IoWorkerContext) {
|
||||||
|
let key_grid = &context.device.characteristics().key_grid;
|
||||||
|
for index in 0..(key_grid.rows * key_grid.columns) {
|
||||||
|
draw_key_at_index(context, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
for position in KnobPosition::VARIANTS {
|
||||||
|
draw_knob_at_position(context, *position);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.device.refresh_display(&key_grid.display).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// active -> config.active_button_color
|
// active -> config.active_button_color
|
||||||
// no actions defined -> #000000
|
// no actions defined -> #000000
|
||||||
// inactive -> config.inactive_button_color
|
// inactive -> config.inactive_button_color
|
||||||
|
@ -306,6 +341,7 @@ fn draw_key(context: &IoWorkerContext, index: u8, key: Option<&Key>) {
|
||||||
let buffer = render_key(
|
let buffer = render_key(
|
||||||
&context.graphics,
|
&context.graphics,
|
||||||
IntSize::from_wh(rect.w as u32, rect.h as u32).expect("rect.w and rect.h are not zero"),
|
IntSize::from_wh(rect.w as u32, rect.h as u32).expect("rect.w and rect.h are not zero"),
|
||||||
|
&context.state.active_remote_handler_host_ids,
|
||||||
key,
|
key,
|
||||||
);
|
);
|
||||||
context
|
context
|
||||||
|
@ -314,23 +350,23 @@ fn draw_key(context: &IoWorkerContext, index: u8, key: Option<&Key>) {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_key_at_index(context: &IoWorkerContext, state: &State, index: u8) {
|
fn draw_key_at_index(context: &IoWorkerContext, index: u8) {
|
||||||
let position = get_key_position_for_index(&context.device.characteristics().key_grid, index);
|
let position = get_key_position_for_index(&context.device.characteristics().key_grid, index);
|
||||||
|
|
||||||
draw_key(context, index, state.active_key_page().keys_by_position.get(&position));
|
draw_key(context, index, context.state.active_key_page().keys_by_position.get(&position));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_key_at_position_if_visible(context: &IoWorkerContext, state: &State, position: KeyPosition) {
|
fn draw_key_at_position_if_visible(context: &IoWorkerContext, position: KeyPosition) {
|
||||||
let index = get_key_index_for_position(&context.device.characteristics().key_grid, position);
|
let index = get_key_index_for_position(&context.device.characteristics().key_grid, position);
|
||||||
|
|
||||||
if let Some(index) = index {
|
if let Some(index) = index {
|
||||||
draw_key(context, index, state.active_key_page().keys_by_position.get(&position));
|
draw_key(context, index, context.state.active_key_page().keys_by_position.get(&position));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_key_at_path_if_visible(context: &IoWorkerContext, state: &State, path: KeyPath) {
|
fn draw_key_at_path_if_visible(context: &IoWorkerContext, path: KeyPath) {
|
||||||
if state.active_key_page_id == path.page_id {
|
if context.state.active_key_page_id == path.page_id {
|
||||||
draw_key_at_position_if_visible(context, state, path.position);
|
draw_key_at_position_if_visible(context, path.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,6 +382,7 @@ fn draw_knob(context: &IoWorkerContext, position: KnobPosition, knob: Option<&Kn
|
||||||
let buffer = render_knob(
|
let buffer = render_knob(
|
||||||
&context.graphics,
|
&context.graphics,
|
||||||
IntSize::from_wh(rect.w as u32, rect.h as u32).expect("rect.w and rect.h are not zero."),
|
IntSize::from_wh(rect.w as u32, rect.h as u32).expect("rect.w and rect.h are not zero."),
|
||||||
|
&context.state.active_remote_handler_host_ids,
|
||||||
knob,
|
knob,
|
||||||
);
|
);
|
||||||
context
|
context
|
||||||
|
@ -355,13 +392,13 @@ fn draw_knob(context: &IoWorkerContext, position: KnobPosition, knob: Option<&Kn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_knob_at_position(context: &IoWorkerContext, state: &State, position: KnobPosition) {
|
fn draw_knob_at_position(context: &IoWorkerContext, position: KnobPosition) {
|
||||||
draw_knob(context, position, Some(&state.active_knob_page().knobs_by_position[position]));
|
draw_knob(context, position, Some(&context.state.active_knob_page().knobs_by_position[position]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_knob_at_path_if_visible(context: &IoWorkerContext, state: &State, path: KnobPath) {
|
fn draw_knob_at_path_if_visible(context: &IoWorkerContext, path: KnobPath) {
|
||||||
if state.active_knob_page_id == path.page_id {
|
if context.state.active_knob_page_id == path.page_id {
|
||||||
draw_knob_at_position(context, state, path.position);
|
draw_knob_at_position(context, path.position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,11 @@ use deckster_shared::path::{KeyPath, KnobPath};
|
||||||
use loupedeck_serial::commands::VibrationPattern;
|
use loupedeck_serial::commands::VibrationPattern;
|
||||||
use loupedeck_serial::device::LoupedeckDevice;
|
use loupedeck_serial::device::LoupedeckDevice;
|
||||||
|
|
||||||
use crate::coordinator::io_worker::{do_io_work, IoWorkerContext};
|
use crate::coordinator::io_worker::{do_io_work, CoordinatorCommand, IoWorkerContext};
|
||||||
use crate::coordinator::mqtt::start_mqtt_client;
|
use crate::coordinator::mqtt::start_mqtt_client;
|
||||||
use crate::handler_runner;
|
use crate::handler_runner;
|
||||||
use crate::handler_runner::KeyOrKnobHandlerConfig;
|
use crate::handler_runner::KeyOrKnobHandlerConfig;
|
||||||
use crate::model::coordinator_config::Config;
|
use crate::model::coordinator_config::Config;
|
||||||
use crate::model::get_default_host_id;
|
|
||||||
use crate::model::mqtt::HandlerHostsConfig;
|
use crate::model::mqtt::HandlerHostsConfig;
|
||||||
|
|
||||||
mod graphics;
|
mod graphics;
|
||||||
|
@ -33,11 +32,12 @@ pub async fn start(config_directory: &Path, config: Config) -> Result<()> {
|
||||||
let available_device = available_devices.first().wrap_err("No device connected.")?;
|
let available_device = available_devices.first().wrap_err("No device connected.")?;
|
||||||
log::info!("Found {} device(s).", available_devices.len());
|
log::info!("Found {} device(s).", available_devices.len());
|
||||||
|
|
||||||
let (commands_sender, commands_receiver) = flume::bounded::<HandlerCommand>(5);
|
let (coordinator_commands_sender, coordinator_commands_receiver) = flume::bounded::<CoordinatorCommand>(5);
|
||||||
|
let (handler_commands_sender, handler_commands_receiver) = flume::bounded::<HandlerCommand>(5);
|
||||||
let events_sender = broadcast::Sender::<HandlerEvent>::new(5);
|
let events_sender = broadcast::Sender::<HandlerEvent>::new(5);
|
||||||
|
|
||||||
commands_sender
|
coordinator_commands_sender
|
||||||
.send(HandlerCommand::SetActivePages {
|
.send(CoordinatorCommand::SetActivePages {
|
||||||
knob_page_id: config.initial.knob_page.clone(),
|
knob_page_id: config.initial.knob_page.clone(),
|
||||||
key_page_id: config.initial.key_page.clone(),
|
key_page_id: config.initial.key_page.clone(),
|
||||||
})
|
})
|
||||||
|
@ -91,16 +91,16 @@ pub async fn start(config_directory: &Path, config: Config) -> Result<()> {
|
||||||
|
|
||||||
if let Some(mqtt_config) = &config.mqtt {
|
if let Some(mqtt_config) = &config.mqtt {
|
||||||
log::info!("Initializing MQTT client…");
|
log::info!("Initializing MQTT client…");
|
||||||
start_mqtt_client(mqtt_config, &handler_hosts_config, commands_sender.clone(), events_sender.subscribe()).await;
|
start_mqtt_client(mqtt_config, &handler_hosts_config, handler_commands_sender.clone(), events_sender.subscribe()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Initializing handler processes…");
|
log::info!("Initializing handler processes…");
|
||||||
|
|
||||||
handler_runner::start(
|
handler_runner::start(
|
||||||
get_default_host_id(),
|
String::default().into_boxed_str(),
|
||||||
&config_directory.join("handlers"),
|
&config_directory.join("handlers"),
|
||||||
handler_hosts_config,
|
handler_hosts_config,
|
||||||
commands_sender.clone(),
|
handler_commands_sender.clone(),
|
||||||
events_sender.subscribe(),
|
events_sender.subscribe(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -112,12 +112,12 @@ pub async fn start(config_directory: &Path, config: Config) -> Result<()> {
|
||||||
device.set_brightness(0.5);
|
device.set_brightness(0.5);
|
||||||
device.vibrate(VibrationPattern::RiseFall);
|
device.vibrate(VibrationPattern::RiseFall);
|
||||||
|
|
||||||
let io_worker_context = IoWorkerContext::create(config_directory, Arc::clone(&config), device, commands_sender.clone(), events_sender);
|
let io_worker_context = IoWorkerContext::create(config_directory, Arc::clone(&config), device, coordinator_commands_sender, events_sender);
|
||||||
|
|
||||||
let io_worker_thread = thread::Builder::new()
|
let io_worker_thread = thread::Builder::new()
|
||||||
.name("deckster IO worker".to_owned())
|
.name("deckster IO worker".to_owned())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
do_io_work(io_worker_context, commands_receiver);
|
do_io_work(io_worker_context, &coordinator_commands_receiver, &handler_commands_receiver);
|
||||||
})
|
})
|
||||||
.wrap_err("Could not spawn the worker thread")?;
|
.wrap_err("Could not spawn the worker thread")?;
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub struct State {
|
||||||
pub active_key_page_id: String,
|
pub active_key_page_id: String,
|
||||||
pub active_knob_page_id: String,
|
pub active_knob_page_id: String,
|
||||||
pub active_touch_ids: HashSet<u8>,
|
pub active_touch_ids: HashSet<u8>,
|
||||||
|
pub active_remote_handler_host_ids: HashSet<Box<str>>,
|
||||||
pub key_pages_by_id: HashMap<String, KeyPage>,
|
pub key_pages_by_id: HashMap<String, KeyPage>,
|
||||||
pub knob_pages_by_id: HashMap<String, KnobPage>,
|
pub knob_pages_by_id: HashMap<String, KnobPage>,
|
||||||
}
|
}
|
||||||
|
@ -32,6 +33,7 @@ impl State {
|
||||||
page_id: p.id.clone(),
|
page_id: p.id.clone(),
|
||||||
position: *position,
|
position: *position,
|
||||||
},
|
},
|
||||||
|
host_id: k.host.clone(),
|
||||||
base_style: k.base_style.clone(),
|
base_style: k.base_style.clone(),
|
||||||
style: None,
|
style: None,
|
||||||
})
|
})
|
||||||
|
@ -54,6 +56,7 @@ impl State {
|
||||||
page_id: p.id.clone(),
|
page_id: p.id.clone(),
|
||||||
position,
|
position,
|
||||||
},
|
},
|
||||||
|
host_id: knob_config.host.clone(),
|
||||||
base_style: knob_config.base_style.clone(),
|
base_style: knob_config.base_style.clone(),
|
||||||
style: None,
|
style: None,
|
||||||
value: None,
|
value: None,
|
||||||
|
@ -67,6 +70,7 @@ impl State {
|
||||||
active_key_page_id: config.initial.key_page.clone(),
|
active_key_page_id: config.initial.key_page.clone(),
|
||||||
active_knob_page_id: config.initial.knob_page.clone(),
|
active_knob_page_id: config.initial.knob_page.clone(),
|
||||||
active_touch_ids: HashSet::new(),
|
active_touch_ids: HashSet::new(),
|
||||||
|
active_remote_handler_host_ids: HashSet::new(),
|
||||||
key_pages_by_id,
|
key_pages_by_id,
|
||||||
knob_pages_by_id,
|
knob_pages_by_id,
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,7 +135,6 @@ pub async fn start_mqtt_client(
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(command) = commands_receiver.recv_async().await {
|
while let Ok(command) = commands_receiver.recv_async().await {
|
||||||
match command {
|
match command {
|
||||||
HandlerCommand::SetActivePages { .. } => log::warn!("HandlerCommand::SetActivePages is not supported for remote handlers."),
|
|
||||||
HandlerCommand::SetKeyStyle { path, value } => client
|
HandlerCommand::SetKeyStyle { path, value } => client
|
||||||
.publish(
|
.publish(
|
||||||
format!("{topic_prefix}/keys/{path}/style"),
|
format!("{topic_prefix}/keys/{path}/style"),
|
||||||
|
|
|
@ -50,7 +50,7 @@ pub struct Key {
|
||||||
#[serde(default, flatten)]
|
#[serde(default, flatten)]
|
||||||
pub base_style: KeyStyle,
|
pub base_style: KeyStyle,
|
||||||
|
|
||||||
#[serde(default = "super::get_default_host_id")]
|
#[serde(default)]
|
||||||
pub host: Box<str>,
|
pub host: Box<str>,
|
||||||
pub handler: Box<str>,
|
pub handler: Box<str>,
|
||||||
pub config: Arc<toml::Table>,
|
pub config: Arc<toml::Table>,
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub struct Knob {
|
||||||
#[serde(default, flatten)]
|
#[serde(default, flatten)]
|
||||||
pub base_style: KnobStyle,
|
pub base_style: KnobStyle,
|
||||||
|
|
||||||
#[serde(default = "super::get_default_host_id")]
|
#[serde(default)]
|
||||||
pub host: Box<str>,
|
pub host: Box<str>,
|
||||||
pub handler: Box<str>,
|
pub handler: Box<str>,
|
||||||
pub config: Arc<toml::Table>,
|
pub config: Arc<toml::Table>,
|
||||||
|
|
|
@ -5,7 +5,3 @@ pub mod key_page;
|
||||||
pub mod knob_page;
|
pub mod knob_page;
|
||||||
pub mod mqtt;
|
pub mod mqtt;
|
||||||
pub mod position;
|
pub mod position;
|
||||||
|
|
||||||
pub fn get_default_host_id() -> Box<str> {
|
|
||||||
"local".to_owned().into_boxed_str()
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue