winit/platform_impl/linux/common/xkb/
compose.rs

1//! XKB compose handling.
2
3use std::env;
4use std::ffi::CString;
5use std::ops::Deref;
6use std::os::unix::ffi::OsStringExt;
7use std::ptr::NonNull;
8
9use smol_str::SmolStr;
10use xkbcommon_dl::{
11    xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, xkb_compose_state_flags,
12    xkb_compose_status, xkb_compose_table, xkb_keysym_t,
13};
14
15use super::{XkbContext, XKBCH};
16
17#[derive(Debug)]
18pub struct XkbComposeTable {
19    table: NonNull<xkb_compose_table>,
20}
21
22impl XkbComposeTable {
23    pub fn new(context: &XkbContext) -> Option<Self> {
24        let locale = env::var_os("LC_ALL")
25            .and_then(|v| if v.is_empty() { None } else { Some(v) })
26            .or_else(|| env::var_os("LC_CTYPE"))
27            .and_then(|v| if v.is_empty() { None } else { Some(v) })
28            .or_else(|| env::var_os("LANG"))
29            .and_then(|v| if v.is_empty() { None } else { Some(v) })
30            .unwrap_or_else(|| "C".into());
31        let locale = CString::new(locale.into_vec()).unwrap();
32
33        let table = unsafe {
34            (XKBCH.xkb_compose_table_new_from_locale)(
35                context.as_ptr(),
36                locale.as_ptr(),
37                xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS,
38            )
39        };
40
41        let table = NonNull::new(table)?;
42        Some(Self { table })
43    }
44
45    /// Create new state with the given compose table.
46    pub fn new_state(&self) -> Option<XkbComposeState> {
47        let state = unsafe {
48            (XKBCH.xkb_compose_state_new)(
49                self.table.as_ptr(),
50                xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS,
51            )
52        };
53
54        let state = NonNull::new(state)?;
55        Some(XkbComposeState { state })
56    }
57}
58
59impl Deref for XkbComposeTable {
60    type Target = NonNull<xkb_compose_table>;
61
62    fn deref(&self) -> &Self::Target {
63        &self.table
64    }
65}
66
67impl Drop for XkbComposeTable {
68    fn drop(&mut self) {
69        unsafe {
70            (XKBCH.xkb_compose_table_unref)(self.table.as_ptr());
71        }
72    }
73}
74
75#[derive(Debug)]
76pub struct XkbComposeState {
77    state: NonNull<xkb_compose_state>,
78}
79
80impl XkbComposeState {
81    pub fn get_string(&mut self, scratch_buffer: &mut Vec<u8>) -> Option<SmolStr> {
82        super::make_string_with(scratch_buffer, |ptr, len| unsafe {
83            (XKBCH.xkb_compose_state_get_utf8)(self.state.as_ptr(), ptr, len)
84        })
85    }
86
87    #[inline]
88    pub fn feed(&mut self, keysym: xkb_keysym_t) -> ComposeStatus {
89        let feed_result = unsafe { (XKBCH.xkb_compose_state_feed)(self.state.as_ptr(), keysym) };
90        match feed_result {
91            xkb_compose_feed_result::XKB_COMPOSE_FEED_IGNORED => ComposeStatus::Ignored,
92            xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED => {
93                ComposeStatus::Accepted(self.status())
94            },
95        }
96    }
97
98    #[inline]
99    pub fn reset(&mut self) {
100        unsafe {
101            (XKBCH.xkb_compose_state_reset)(self.state.as_ptr());
102        }
103    }
104
105    #[inline]
106    pub fn status(&mut self) -> xkb_compose_status {
107        unsafe { (XKBCH.xkb_compose_state_get_status)(self.state.as_ptr()) }
108    }
109}
110
111impl Drop for XkbComposeState {
112    fn drop(&mut self) {
113        unsafe {
114            (XKBCH.xkb_compose_state_unref)(self.state.as_ptr());
115        };
116    }
117}
118
119#[derive(Copy, Clone, Debug)]
120pub enum ComposeStatus {
121    Accepted(xkb_compose_status),
122    Ignored,
123    None,
124}