1 | //! XKB compose handling. |
2 | |
3 | use std::env; |
4 | use std::ffi::CString; |
5 | use std::ops::Deref; |
6 | use std::os::unix::ffi::OsStringExt; |
7 | use std::ptr::NonNull; |
8 | |
9 | use super::XkbContext; |
10 | use super::XKBCH; |
11 | use smol_str::SmolStr; |
12 | use xkbcommon_dl::{ |
13 | xkb_compose_compile_flags, xkb_compose_feed_result, xkb_compose_state, xkb_compose_state_flags, |
14 | xkb_compose_status, xkb_compose_table, xkb_keysym_t, |
15 | }; |
16 | |
17 | #[derive (Debug)] |
18 | pub struct XkbComposeTable { |
19 | table: NonNull<xkb_compose_table>, |
20 | } |
21 | |
22 | impl 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 | |
59 | impl Deref for XkbComposeTable { |
60 | type Target = NonNull<xkb_compose_table>; |
61 | |
62 | fn deref(&self) -> &Self::Target { |
63 | &self.table |
64 | } |
65 | } |
66 | |
67 | impl 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)] |
76 | pub struct XkbComposeState { |
77 | state: NonNull<xkb_compose_state>, |
78 | } |
79 | |
80 | impl 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 | |
111 | impl 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)] |
120 | pub enum ComposeStatus { |
121 | Accepted(xkb_compose_status), |
122 | Ignored, |
123 | None, |
124 | } |
125 | |