zed_xim/server/
connection.rs

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                // TODO: handle error
248
249                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                // Extension not supported now
358                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            // Ignore start reply
554            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}