winit/platform_impl/linux/common/xkb/
compose.rs
1use 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 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}