aethermap-gui 1.5.0

GUI client for aethermap input remapper
Documentation
use crate::gui::{Message, State};
use aethermap_common::{LayerConfigInfo, LayerMode};
use iced::Command;

pub fn layer_state_changed(
    state: &mut State,
    device_id: String,
    layer_id: usize,
) -> Command<Message> {
    state.active_layers.insert(device_id, layer_id);
    Command::none()
}

pub fn layer_config_requested(state: &State, device_id: String) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    let id = device_id.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            (id.clone(), client.list_layers(&id).await)
        },
        |(device_id, result)| match result {
            Ok(layers) => {
                if let Some(active_layer) = layers.first() {
                    Message::LayerStateChanged(device_id, active_layer.layer_id)
                } else {
                    Message::TickAnimations
                }
            }
            Err(e) => Message::ProfileError(format!("Failed to load layers: {}", e)),
        },
    )
}

pub fn layer_activate_requested(
    state: &State,
    device_id: String,
    layer_id: usize,
    mode: LayerMode,
) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    let id = device_id.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client.activate_layer(&id, layer_id, mode).await
        },
        move |result| match result {
            Ok(()) => Message::LayerStateChanged(device_id, layer_id),
            Err(e) => Message::ProfileError(format!("Failed to activate layer: {}", e)),
        },
    )
}

pub fn layer_config_updated(
    state: &State,
    device_id: String,
    config: LayerConfigInfo,
) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    let id = device_id.clone();
    let layer_id = config.layer_id;
    let name = config.name.clone();
    let mode = config.mode;
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client.set_layer_config(&id, layer_id, name, mode).await
        },
        move |result| match result {
            Ok(()) => Message::LayerConfigRequested(device_id),
            Err(e) => Message::ProfileError(format!("Failed to update layer config: {}", e)),
        },
    )
}

pub fn open_layer_config_dialog(
    state: &mut State,
    device_id: String,
    layer_id: usize,
) -> Command<Message> {
    let current_name = state
        .layer_configs
        .get(&device_id)
        .and_then(|layers| layers.iter().find(|l| l.layer_id == layer_id))
        .map(|l| l.name.clone())
        .unwrap_or_else(|| format!("Layer {}", layer_id));

    let current_mode = state
        .layer_configs
        .get(&device_id)
        .and_then(|layers| layers.iter().find(|l| l.layer_id == layer_id))
        .map(|l| l.mode)
        .unwrap_or(LayerMode::Hold);

    state.layer_config_dialog = Some((device_id, layer_id, current_name, current_mode));
    Command::none()
}

pub fn layer_config_name_changed(state: &mut State, name: String) -> Command<Message> {
    if let Some((device_id, layer_id, _, mode)) = state.layer_config_dialog.take() {
        state.layer_config_dialog = Some((device_id, layer_id, name, mode));
    }
    Command::none()
}

pub fn layer_config_mode_changed(state: &mut State, mode: LayerMode) -> Command<Message> {
    if let Some((device_id, layer_id, name, _)) = state.layer_config_dialog.take() {
        state.layer_config_dialog = Some((device_id, layer_id, name, mode));
    }
    Command::none()
}

pub fn save_layer_config(state: &mut State) -> Command<Message> {
    if let Some((device_id, layer_id, name, mode)) = state.layer_config_dialog.take() {
        let config = LayerConfigInfo {
            layer_id,
            name: name.clone(),
            mode,
            remap_count: 0,
            led_color: (0, 0, 255),
            led_zone: None,
        };
        Command::perform(async move { (device_id, config) }, |(device_id, config)| {
            Message::LayerConfigUpdated(device_id, config)
        })
    } else {
        Command::none()
    }
}

pub fn cancel_layer_config(state: &mut State) -> Command<Message> {
    state.layer_config_dialog = None;
    Command::none()
}

pub fn refresh_layers(state: &State) -> Command<Message> {
    let mut commands = Vec::new();

    for device_id in state.device_profiles.keys() {
        let device_id = device_id.clone();
        let socket_path = state.socket_path.clone();
        commands.push(Command::perform(
            async move {
                let client = crate::ipc::IpcClient::new(socket_path);
                (device_id.clone(), client.list_layers(&device_id).await)
            },
            |(device_id, result)| match result {
                Ok(layers) => Message::LayerListLoaded(device_id, layers),
                Err(_) => Message::TickAnimations,
            },
        ));
    }

    for device_id in state.active_layers.keys().cloned().collect::<Vec<_>>() {
        let device_id = device_id.clone();
        let socket_path = state.socket_path.clone();
        commands.push(Command::perform(
            async move {
                let client = crate::ipc::IpcClient::new(socket_path);
                (device_id.clone(), client.get_active_layer(&device_id).await)
            },
            |(device_id, result)| match result {
                Ok(Some(layer_id)) => Message::LayerStateChanged(device_id, layer_id),
                _ => Message::TickAnimations,
            },
        ));
    }

    Command::batch(commands)
}

pub fn layer_list_loaded(
    state: &mut State,
    device_id: String,
    layers: Vec<LayerConfigInfo>,
) -> Command<Message> {
    state.layer_configs.insert(device_id, layers);
    Command::none()
}

pub fn analog_dpad_mode_requested(state: &State, device_id: String) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    let device_id_clone = device_id.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client.get_analog_dpad_mode(&device_id_clone).await
        },
        move |result| match result {
            Ok(mode) => Message::AnalogDpadModeLoaded(device_id, mode),
            Err(e) => {
                eprintln!("Failed to get D-pad mode: {}", e);
                Message::TickAnimations
            }
        },
    )
}

pub fn analog_dpad_mode_loaded(
    state: &mut State,
    device_id: String,
    mode: String,
) -> Command<Message> {
    state.analog_dpad_modes.insert(device_id, mode);
    Command::none()
}

pub fn set_analog_dpad_mode(state: &State, device_id: String, mode: String) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    let device_id_clone = device_id.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client.set_analog_dpad_mode(&device_id_clone, &mode).await
        },
        |result| match result {
            Ok(_) => Message::AnalogDpadModeSet(Ok(())),
            Err(e) => Message::AnalogDpadModeSet(Err(e)),
        },
    )
}

pub fn analog_dpad_mode_set(state: &mut State, result: Result<(), String>) -> Command<Message> {
    if let Err(e) = result {
        eprintln!("Failed to set D-pad mode: {}", e);
    }
    Command::none()
}

pub fn analog_deadzone_xy_requested(state: &State, device_id: String) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    let device_id_clone = device_id.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client.get_analog_deadzone_xy(&device_id_clone).await
        },
        move |result| match result {
            Ok((x_pct, y_pct)) => Message::AnalogDeadzoneXYLoaded(device_id, (x_pct, y_pct)),
            Err(e) => {
                eprintln!("Failed to get per-axis deadzone: {}", e);
                Message::TickAnimations
            }
        },
    )
}

pub fn analog_deadzone_xy_loaded(
    state: &mut State,
    device_id: String,
    xy: (u8, u8),
) -> Command<Message> {
    state.analog_deadzones_xy.insert(device_id, xy);
    Command::none()
}

pub fn set_analog_deadzone_xy(
    state: &State,
    device_id: String,
    x_pct: u8,
    y_pct: u8,
) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client
                .set_analog_deadzone_xy(&device_id, x_pct, y_pct)
                .await
        },
        |result| match result {
            Ok(_) => Message::AnalogDeadzoneXYSet(Ok(())),
            Err(e) => Message::AnalogDeadzoneXYSet(Err(e)),
        },
    )
}

pub fn analog_deadzone_xy_set(state: &mut State, result: Result<(), String>) -> Command<Message> {
    if let Err(e) = result {
        eprintln!("Failed to set per-axis deadzone: {}", e);
        state.add_notification(&format!("Failed to set deadzone: {}", e), true);
    }
    Command::none()
}

pub fn analog_outer_deadzone_xy_requested(state: &State, device_id: String) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    let device_id_clone = device_id.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client.get_analog_outer_deadzone_xy(&device_id_clone).await
        },
        move |result| match result {
            Ok((x_pct, y_pct)) => Message::AnalogOuterDeadzoneXYLoaded(device_id, (x_pct, y_pct)),
            Err(e) => {
                eprintln!("Failed to get per-axis outer deadzone: {}", e);
                Message::TickAnimations
            }
        },
    )
}

pub fn analog_outer_deadzone_xy_loaded(
    state: &mut State,
    device_id: String,
    xy: (u8, u8),
) -> Command<Message> {
    state.analog_outer_deadzones_xy.insert(device_id, xy);
    Command::none()
}

pub fn set_analog_outer_deadzone_xy(
    state: &State,
    device_id: String,
    x_pct: u8,
    y_pct: u8,
) -> Command<Message> {
    let socket_path = state.socket_path.clone();
    Command::perform(
        async move {
            let client = crate::ipc::IpcClient::new(socket_path);
            client
                .set_analog_outer_deadzone_xy(&device_id, x_pct, y_pct)
                .await
        },
        |result| match result {
            Ok(_) => Message::AnalogOuterDeadzoneXYSet(Ok(())),
            Err(e) => Message::AnalogOuterDeadzoneXYSet(Err(e)),
        },
    )
}

pub fn analog_outer_deadzone_xy_set(
    state: &mut State,
    result: Result<(), String>,
) -> Command<Message> {
    if let Err(e) = result {
        eprintln!("Failed to set per-axis outer deadzone: {}", e);
        state.add_notification(&format!("Failed to set outer deadzone: {}", e), true);
    }
    Command::none()
}