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, XKBCH}; |
10 | use smol_str::SmolStr; |
11 | use 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)] |
17 | pub struct XkbComposeTable { |
18 | table: NonNull<xkb_compose_table>, |
19 | } |
20 | |
21 | impl 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 | |
58 | impl Deref for XkbComposeTable { |
59 | type Target = NonNull<xkb_compose_table>; |
60 | |
61 | fn deref(&self) -> &Self::Target { |
62 | &self.table |
63 | } |
64 | } |
65 | |
66 | impl 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)] |
75 | pub struct XkbComposeState { |
76 | state: NonNull<xkb_compose_state>, |
77 | } |
78 | |
79 | impl 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 | |
110 | impl 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)] |
119 | pub enum ComposeStatus { |
120 | Accepted(xkb_compose_status), |
121 | Ignored, |
122 | None, |
123 | } |
124 | |