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