1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
extern crate swayipc;

use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use swayipc::{Connection, Input};

#[derive(Debug)]
enum Error {
    InconsistentLayouts,
    NoKeyboards,
}

#[no_mangle]
pub extern "C" fn Xkb_Switch_getXkbLayout() -> *const c_char {
    match Connection::new() {
        Ok(mut conn) => CString::new(get_cur_layout(&mut conn).unwrap())
            .unwrap()
            .into_raw() as *const c_char,
        Err(_) => 0 as *const c_char,
    }
}

fn get_cur_layout(conn: &mut Connection) -> Result<String, Error> {
    let mut layouts: Vec<String> = get_keyboards(conn)
        .drain(..)
        .filter_map(|kb| kb.xkb_active_layout_name)
        .collect();
    layouts.dedup();
    match layouts.leak() {
        [] => Err(Error::NoKeyboards),
        [layout] => Ok(layout.to_string()),
        _ => Err(Error::InconsistentLayouts),
    }
}

fn get_keyboards(conn: &mut Connection) -> Vec<Input> {
    let mut all_inputs = conn.get_inputs().unwrap();
    all_inputs.retain(|input_device| input_device.input_type == "keyboard");
    all_inputs
}

#[no_mangle]
pub extern "C" fn Xkb_Switch_setXkbLayout(layout_ptr: *const c_char) {
    match Connection::new() {
        Ok(mut conn) => {
            let layout = unsafe { CStr::from_ptr(layout_ptr).to_string_lossy().to_string() };
            switch_layout(&mut conn, &layout);
        }
        Err(_) => (),
    };
}

fn switch_layout(conn: &mut Connection, layout: &String) {
    get_keyboards(conn).iter().for_each(|kb| {
        let layout_index = kb
            .xkb_layout_names
            .iter()
            .position(|x| x == layout)
            .unwrap();

        conn.run_command(format!(
            "input {} xkb_switch_layout {}",
            kb.identifier, layout_index
        ))
        .unwrap();
    });
}