1mod im_vec;
2
3use crate::AHashMap;
4use alloc::string::String;
5use alloc::vec;
6use alloc::vec::Vec;
7use core::num::{NonZeroU16, NonZeroU32};
8use xim_parser::{
9 attrs, Attribute, AttributeName, ErrorCode, ForwardEventFlag, InputStyle, InputStyleList,
10 Point, Request, XimWrite,
11};
12
13use self::im_vec::ImVec;
14use crate::server::{Server, ServerCore, ServerError, ServerHandler};
15
16pub struct InputContext {
17 client_win: u32,
18 app_win: Option<NonZeroU32>,
19 app_focus_win: Option<NonZeroU32>,
20 input_method_id: NonZeroU16,
21 input_context_id: NonZeroU16,
22 input_style: InputStyle,
23 preedit_spot: Point,
24 pub(super) preedit_started: bool,
25 pub(super) prev_preedit_length: usize,
26 locale: String,
27}
28
29impl InputContext {
30 pub fn new(
31 client_win: u32,
32 input_method_id: NonZeroU16,
33 input_context_id: NonZeroU16,
34 locale: String,
35 ) -> Self {
36 Self {
37 client_win,
38 app_win: None,
39 app_focus_win: None,
40 input_method_id,
41 input_context_id,
42 input_style: InputStyle::empty(),
43 preedit_spot: Point { x: 0, y: 0 },
44 preedit_started: false,
45 prev_preedit_length: 0,
46 locale,
47 }
48 }
49
50 pub fn client_win(&self) -> u32 {
51 self.client_win
52 }
53
54 pub fn app_win(&self) -> Option<NonZeroU32> {
55 self.app_win
56 }
57
58 pub fn app_focus_win(&self) -> Option<NonZeroU32> {
59 self.app_focus_win
60 }
61
62 pub fn preedit_spot(&self) -> Point {
63 self.preedit_spot.clone()
64 }
65
66 pub fn input_method_id(&self) -> NonZeroU16 {
67 self.input_method_id
68 }
69
70 pub fn input_context_id(&self) -> NonZeroU16 {
71 self.input_context_id
72 }
73
74 pub fn input_style(&self) -> InputStyle {
75 self.input_style
76 }
77
78 pub fn locale(&self) -> &str {
79 self.locale.as_str()
80 }
81}
82
83pub struct UserInputContext<T> {
84 pub ic: InputContext,
85 pub user_data: T,
86}
87
88impl<T> UserInputContext<T> {
89 pub fn new(ic: InputContext, user_data: T) -> Self {
90 Self { ic, user_data }
91 }
92}
93
94fn set_ic_attrs(ic: &mut InputContext, ic_attributes: Vec<Attribute>) {
95 for attr in ic_attributes {
96 let name = if let Some(name) = attrs::get_name(attr.id) {
97 name
98 } else {
99 log::warn!("Unknown attr id: {}", attr.id);
100 continue;
101 };
102
103 match name {
104 AttributeName::InputStyle => {
105 if let Ok(style) = xim_parser::read(&attr.value) {
106 log::debug!("Style: {:?}", style);
107 ic.input_style = style;
108 }
109 }
110 AttributeName::ClientWindow => {
111 ic.app_win = xim_parser::read(&attr.value).ok().and_then(NonZeroU32::new);
112 }
113 AttributeName::FocusWindow => {
114 ic.app_focus_win = xim_parser::read(&attr.value).ok().and_then(NonZeroU32::new);
115 }
116 AttributeName::PreeditAttributes => {
117 let mut b = &attr.value[..];
118 while !b.is_empty() {
119 match xim_parser::read::<Attribute>(b) {
120 Ok(attr) => {
121 b = &b[attr.size()..];
122 match attrs::get_name(attr.id) {
123 Some(AttributeName::SpotLocation) => {
124 if let Ok(spot) = xim_parser::read(&attr.value) {
125 log::debug!("Spot: {:?}", spot);
126 ic.preedit_spot = spot;
127 }
128 }
129 name => {
130 log::warn!("Ignore unhandled preedit attr: {:?}", name);
131 }
132 }
133 }
134 Err(_) => {
135 break;
136 }
137 }
138 }
139 }
140 name => {
141 log::warn!("Ignore unhandled attr: {:?}", name);
142 }
143 }
144 }
145}
146
147pub struct InputMethod<T> {
148 pub(crate) locale: String,
149 pub(crate) input_contexts: ImVec<UserInputContext<T>>,
150}
151
152impl<T> InputMethod<T> {
153 pub fn new(locale: String) -> Self {
154 Self {
155 locale,
156 input_contexts: ImVec::new(),
157 }
158 }
159
160 pub fn clone_locale(&self) -> String {
161 self.locale.clone()
162 }
163
164 pub fn new_ic(&mut self, ic: UserInputContext<T>) -> (NonZeroU16, &mut UserInputContext<T>) {
165 self.input_contexts.new_item(ic)
166 }
167
168 pub fn remove_input_context(&mut self, ic_id: u16) -> Result<UserInputContext<T>, ServerError> {
169 self.input_contexts
170 .remove_item(ic_id)
171 .ok_or(ServerError::ClientNotExists)
172 }
173
174 pub fn get_input_context(
175 &mut self,
176 ic_id: u16,
177 ) -> Result<&mut UserInputContext<T>, ServerError> {
178 self.input_contexts
179 .get_item(ic_id)
180 .ok_or(ServerError::ClientNotExists)
181 }
182}
183
184pub struct XimConnection<T> {
185 pub(crate) client_win: u32,
186 pub(crate) disconnected: bool,
187 pub(crate) input_methods: ImVec<InputMethod<T>>,
188}
189
190impl<T> XimConnection<T> {
191 pub fn new(client_win: u32) -> Self {
192 Self {
193 client_win,
194 disconnected: false,
195 input_methods: ImVec::new(),
196 }
197 }
198
199 pub fn disconnect<S: ServerCore + Server, H: ServerHandler<S, InputContextData = T>>(
200 &mut self,
201 server: &mut S,
202 handler: &mut H,
203 ) -> Result<(), ServerError> {
204 for (_id, im) in self.input_methods.drain() {
205 for (_id, ic) in im.input_contexts {
206 handler.handle_destroy_ic(server, ic)?;
207 }
208 }
209
210 self.disconnected = true;
211
212 Ok(())
213 }
214
215 fn get_input_method(&mut self, id: u16) -> Result<&mut InputMethod<T>, ServerError> {
216 self.input_methods
217 .get_item(id)
218 .ok_or(ServerError::ClientNotExists)
219 }
220
221 fn remove_input_method(&mut self, id: u16) -> Result<InputMethod<T>, ServerError> {
222 self.input_methods
223 .remove_item(id)
224 .ok_or(ServerError::ClientNotExists)
225 }
226
227 pub(crate) fn handle_request<S: ServerCore, H: ServerHandler<S, InputContextData = T>>(
228 &mut self,
229 server: &mut S,
230 req: Request,
231 handler: &mut H,
232 ) -> Result<(), ServerError> {
233 if log::log_enabled!(log::Level::Trace) {
234 log::trace!("<-: {:?}", req);
235 } else {
236 log::debug!("<-: {}", req.name());
237 }
238
239 match req {
240 Request::Error {
241 code,
242 detail,
243 flag: _,
244 input_method_id: _,
245 input_context_id: _,
246 } => {
247 log::error!("XIM ERROR! code: {:?}, detail: {}", code, detail);
250 }
251
252 Request::Connect { .. } => {
253 server.send_req(
254 self.client_win,
255 Request::ConnectReply {
256 server_major_protocol_version: 1,
257 server_minor_protocol_version: 0,
258 },
259 )?;
260 handler.handle_connect(server)?;
261 }
262
263 Request::Disconnect {} => {
264 self.disconnect(server, handler)?;
265 server.send_req(self.client_win, Request::DisconnectReply {})?;
266 }
267
268 Request::Open { locale } => {
269 let (input_method_id, _im) = self.input_methods.new_item(InputMethod::new(locale));
270
271 server.send_req(
272 self.client_win,
273 Request::OpenReply {
274 input_method_id: input_method_id.get(),
275 im_attrs: vec![attrs::QUERY_INPUT_STYLE],
276 ic_attrs: vec![
277 attrs::INPUT_STYLE,
278 attrs::CLIENTWIN,
279 attrs::FOCUSWIN,
280 attrs::FILTER_EVENTS,
281 attrs::PREEDIT_ATTRIBUTES,
282 attrs::STATUS_ATTRIBUTES,
283 attrs::FONT_SET,
284 attrs::AREA,
285 attrs::AREA_NEEDED,
286 attrs::COLOR_MAP,
287 attrs::STD_COLOR_MAP,
288 attrs::FOREGROUND,
289 attrs::BACKGROUND,
290 attrs::BACKGROUND_PIXMAP,
291 attrs::SPOT_LOCATION,
292 attrs::LINE_SPACE,
293 attrs::SEPARATOR_OF_NESTED_LIST,
294 ],
295 },
296 )?;
297 }
298
299 Request::CreateIc {
300 input_method_id,
301 ic_attributes,
302 } => {
303 let client_win = self.client_win;
304 let im = self.get_input_method(input_method_id)?;
305 let mut ic = InputContext::new(
306 client_win,
307 NonZeroU16::new(input_method_id).unwrap(),
308 NonZeroU16::new(1).unwrap(),
309 im.clone_locale(),
310 );
311 set_ic_attrs(&mut ic, ic_attributes);
312 let input_style = ic.input_style;
313 let ic = UserInputContext::new(ic, handler.new_ic_data(server, input_style)?);
314 let (input_context_id, ic) = im.new_ic(ic);
315 ic.ic.input_context_id = input_context_id;
316
317 server.send_req(
318 ic.ic.client_win(),
319 Request::CreateIcReply {
320 input_method_id,
321 input_context_id: input_context_id.get(),
322 },
323 )?;
324
325 handler.handle_create_ic(server, ic)?;
326 }
327
328 Request::DestroyIc {
329 input_context_id,
330 input_method_id,
331 } => {
332 handler.handle_destroy_ic(
333 server,
334 self.get_input_method(input_method_id)?
335 .remove_input_context(input_context_id)?,
336 )?;
337 server.send_req(
338 self.client_win,
339 Request::DestroyIcReply {
340 input_method_id,
341 input_context_id,
342 },
343 )?;
344 }
345
346 Request::Close { input_method_id } => {
347 for (_id, ic) in self.remove_input_method(input_method_id)?.input_contexts {
348 handler.handle_destroy_ic(server, ic)?;
349 }
350
351 server.send_req(self.client_win, Request::CloseReply { input_method_id })?;
352 }
353
354 Request::QueryExtension {
355 input_method_id, ..
356 } => {
357 server.send_req(
359 self.client_win,
360 Request::QueryExtensionReply {
361 input_method_id,
362 extensions: Vec::new(),
363 },
364 )?;
365 }
366 Request::EncodingNegotiation {
367 input_method_id,
368 encodings,
369 ..
370 } => {
371 log::debug!("Encodings: {:?}", encodings);
372
373 match encodings
374 .iter()
375 .position(|e| e.starts_with("COMPOUND_TEXT"))
376 {
377 Some(pos) => {
378 server.send_req(
379 self.client_win,
380 Request::EncodingNegotiationReply {
381 input_method_id,
382 category: 0,
383 index: pos as i16,
384 },
385 )?;
386 }
387 None => {
388 server.send_req(
389 self.client_win,
390 Request::EncodingNegotiationReply {
391 input_method_id,
392 category: 0,
393 index: -1,
394 },
395 )?;
396 }
397 }
398 }
399 Request::ResetIc {
400 input_method_id,
401 input_context_id,
402 } => {
403 let ic = self
404 .get_input_method(input_method_id)?
405 .get_input_context(input_context_id)?;
406 let ret = handler.handle_reset_ic(server, ic)?;
407 server.send_req(
408 ic.ic.client_win(),
409 Request::ResetIcReply {
410 input_method_id,
411 input_context_id,
412 preedit_string: xim_ctext::utf8_to_compound_text(&ret),
413 },
414 )?;
415 }
416 Request::GetImValues {
417 input_method_id,
418 im_attributes,
419 } => {
420 let mut out = Vec::with_capacity(im_attributes.len());
421
422 for name in im_attributes.into_iter().filter_map(attrs::get_name) {
423 match name {
424 AttributeName::QueryInputStyle => {
425 out.push(Attribute {
426 id: attrs::get_id(name),
427 value: xim_parser::write_to_vec(InputStyleList {
428 styles: handler.input_styles().as_ref().to_vec(),
429 }),
430 });
431 }
432 _ => {
433 return server.error(
434 self.client_win,
435 ErrorCode::BadName,
436 "Unknown im attribute name".into(),
437 NonZeroU16::new(input_method_id),
438 None,
439 );
440 }
441 }
442 }
443
444 server.send_req(
445 self.client_win,
446 Request::GetImValuesReply {
447 input_method_id,
448 im_attributes: out,
449 },
450 )?;
451 }
452
453 Request::GetIcValues {
454 input_method_id,
455 input_context_id,
456 ic_attributes,
457 } => {
458 let ic = &self
459 .get_input_method(input_method_id)?
460 .get_input_context(input_context_id)?
461 .ic;
462 let mut out = Vec::with_capacity(ic_attributes.len());
463
464 for name in ic_attributes.into_iter().filter_map(attrs::get_name) {
465 match name {
466 AttributeName::InputStyle => out.push(Attribute {
467 id: attrs::get_id(name),
468 value: xim_parser::write_to_vec(ic.input_style()),
469 }),
470 AttributeName::ClientWindow => out.push(Attribute {
471 id: attrs::get_id(name),
472 value: xim_parser::write_to_vec(
473 ic.app_win().map_or(0, NonZeroU32::get),
474 ),
475 }),
476 AttributeName::FocusWindow => out.push(Attribute {
477 id: attrs::get_id(name),
478 value: xim_parser::write_to_vec(
479 ic.app_focus_win().map_or(0, NonZeroU32::get),
480 ),
481 }),
482 AttributeName::FilterEvents => out.push(Attribute {
483 id: attrs::get_id(name),
484 value: xim_parser::write_to_vec(handler.filter_events()),
485 }),
486 AttributeName::QueryInputStyle => {
487 return server.error(
488 self.client_win,
489 ErrorCode::BadName,
490 "Unknown ic attribute name".into(),
491 NonZeroU16::new(input_method_id),
492 None,
493 );
494 }
495 name => {
496 log::warn!("Unimplemented attribute {:?}", name);
497 }
498 }
499 }
500
501 server.send_req(
502 self.client_win,
503 Request::GetIcValuesReply {
504 ic_attributes: out,
505 input_method_id,
506 input_context_id,
507 },
508 )?;
509 }
510
511 Request::SetIcValues {
512 input_context_id,
513 input_method_id,
514 ic_attributes,
515 } => {
516 let ic = self
517 .get_input_method(input_method_id)?
518 .get_input_context(input_context_id)?;
519
520 set_ic_attrs(&mut ic.ic, ic_attributes);
521
522 server.send_req(
523 ic.ic.client_win(),
524 Request::SetIcValuesReply {
525 input_method_id,
526 input_context_id,
527 },
528 )?;
529
530 handler.handle_set_ic_values(server, ic)?;
531 }
532
533 Request::SetIcFocus {
534 input_method_id,
535 input_context_id,
536 } => {
537 let ic = self
538 .get_input_method(input_method_id)?
539 .get_input_context(input_context_id)?;
540 handler.handle_set_focus(server, ic)?;
541 }
542
543 Request::UnsetIcFocus {
544 input_method_id,
545 input_context_id,
546 } => {
547 let ic = self
548 .get_input_method(input_method_id)?
549 .get_input_context(input_context_id)?;
550 handler.handle_unset_focus(server, ic)?;
551 }
552
553 Request::PreeditStartReply { .. } => {}
555
556 Request::ForwardEvent {
557 input_method_id,
558 input_context_id,
559 serial_number: _,
560 flag,
561 xev,
562 } => {
563 let ev = server.deserialize_event(&xev);
564 let input_context = self
565 .get_input_method(input_method_id)?
566 .get_input_context(input_context_id)?;
567 let consumed = handler.handle_forward_event(server, input_context, &ev)?;
568
569 if !consumed {
570 server.send_req(
571 self.client_win,
572 Request::ForwardEvent {
573 input_method_id,
574 input_context_id,
575 serial_number: 0,
576 flag: ForwardEventFlag::empty(),
577 xev,
578 },
579 )?;
580 }
581
582 if flag.contains(ForwardEventFlag::SYNCHRONOUS) {
583 server.send_req(
584 self.client_win,
585 Request::SyncReply {
586 input_method_id,
587 input_context_id,
588 },
589 )?;
590 }
591 }
592
593 Request::Sync {
594 input_method_id,
595 input_context_id,
596 } => {
597 server.send_req(
598 self.client_win,
599 Request::SyncReply {
600 input_method_id,
601 input_context_id,
602 },
603 )?;
604 }
605
606 Request::SyncReply { .. } => {}
607
608 _ => {
609 log::warn!("Unknown request: {:?}", req);
610 }
611 }
612
613 Ok(())
614 }
615}
616
617pub struct XimConnections<T> {
618 pub(crate) connections: AHashMap<u32, XimConnection<T>>,
619}
620
621impl<T> Default for XimConnections<T> {
622 fn default() -> Self {
623 Self::new()
624 }
625}
626
627impl<T> XimConnections<T> {
628 pub fn new() -> Self {
629 Self {
630 connections: AHashMap::with_hasher(Default::default()),
631 }
632 }
633
634 pub fn new_connection(&mut self, com_win: u32, client_win: u32) {
635 self.connections
636 .insert(com_win, XimConnection::new(client_win));
637 }
638
639 pub fn get_connection(&mut self, com_win: u32) -> Option<&mut XimConnection<T>> {
640 self.connections.get_mut(&com_win)
641 }
642
643 pub fn remove_connection(&mut self, com_win: u32) -> Option<XimConnection<T>> {
644 self.connections.remove(&com_win)
645 }
646}