submission.rs 7.45 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*! Managing the state of text input in the application.
 * 
 * This is a library module.
 * 
 * It needs to combine text-input and virtual-keyboard protocols
 * to achieve a consistent view of the text-input state,
 * and to submit exactly what the user wanted.
 * 
 * It must also not get tripped up by sudden disappearances of interfaces.
 * 
 * The virtual-keyboard interface is always present.
 * 
 * The text-input interface may not be presented,
 * and, for simplicity, no further attempt to claim it is made.
 * 
 * The text-input interface may be enabled and disabled at arbitrary times,
 * and those events SHOULD NOT cause any lost events.
 * */
19
20

use std::collections::HashSet;
21
use std::ffi::CString;
22
use ::action::Modifier;
23
use ::imservice;
24
use ::imservice::IMService;
25
26
use ::keyboard::{ KeyCode, KeyStateId, Modifiers, PressType };
use ::util::vec_remove;
27
use ::vkeyboard::VirtualKeyboard;
28

29
30
31
// traits
use std::iter::FromIterator;

32
33
34
35
36
37
38
/// Gathers stuff defined in C or called by C
pub mod c {
    use super::*;
    
    use std::os::raw::c_void;

    use ::imservice::c::InputMethod;
39
40
    use ::layout::c::LevelKeyboard;
    use ::vkeyboard::c::ZwpVirtualKeyboardV1;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

    // The following defined in C

    /// ServerContextService*
    #[repr(transparent)]
    pub struct UIManager(*const c_void);

    /// EekboardContextService*
    #[repr(transparent)]
    pub struct StateManager(*const c_void);

    #[no_mangle]
    pub extern "C"
    fn submission_new(
        im: *mut InputMethod,
56
        vk: ZwpVirtualKeyboardV1,
57
58
59
60
61
62
63
64
65
66
67
        state_manager: *const StateManager
    ) -> *mut Submission {
        let imservice = if im.is_null() {
            None
        } else {
            Some(IMService::new(im, state_manager))
        };
        // TODO: add vkeyboard too
        Box::<Submission>::into_raw(Box::new(
            Submission {
                imservice,
68
                modifiers_active: Vec::new(),
69
                virtual_keyboard: VirtualKeyboard(vk),
70
                pressed: Vec::new(),
71
72
73
74
            }
        ))
    }

75
    /// Use to initialize the UI reference
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
    #[no_mangle]
    pub extern "C"
    fn submission_set_ui(submission: *mut Submission, ui_manager: *const UIManager) {
        if submission.is_null() {
            panic!("Null submission pointer");
        }
        let submission: &mut Submission = unsafe { &mut *submission };
        if let Some(ref mut imservice) = &mut submission.imservice {
            imservice.ui_manager = if ui_manager.is_null() {
                None
            } else {
                Some(ui_manager)
            }
        };
    }
91
92
93
94
95
96
97
98
99
100

    #[no_mangle]
    pub extern "C"
    fn submission_set_keyboard(submission: *mut Submission, keyboard: LevelKeyboard) {
        if submission.is_null() {
            panic!("Null submission pointer");
        }
        let submission: &mut Submission = unsafe { &mut *submission };
        submission.virtual_keyboard.update_keymap(keyboard);
    }
101
102
}

103
104
105
#[derive(Clone, Copy)]
pub struct Timestamp(pub u32);

106
107
108
109
110
111
enum SubmittedAction {
    /// A collection of keycodes that were pressed
    VirtualKeyboard(Vec<KeyCode>),
    IMService,
}

112
113
pub struct Submission {
    imservice: Option<Box<IMService>>,
114
    virtual_keyboard: VirtualKeyboard,
115
    modifiers_active: Vec<(KeyStateId, Modifier)>,
116
117
118
    pressed: Vec<(KeyStateId, SubmittedAction)>,
}

119
120
121
122
123
124
pub enum SubmitData<'a> {
    Text(&'a CString),
    Erase,
    Keycodes,
}

125
126
127
128
129
impl Submission {
    /// Sends a submit text event if possible;
    /// otherwise sends key press and makes a note of it
    pub fn handle_press(
        &mut self,
130
131
132
        key_id: KeyStateId,
        data: SubmitData,
        keycodes: &Vec<KeyCode>,
133
134
        time: Timestamp,
    ) {
135
136
137
138
        let mods_are_on = !self.modifiers_active.is_empty();

        let was_committed_as_text = match (&mut self.imservice, mods_are_on) {
            (Some(imservice), false) => {
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
                enum Outcome {
                    Submitted(Result<(), imservice::SubmitError>),
                    NotSubmitted,
                };

                let submit_outcome = match data {
                    SubmitData::Text(text) => {
                        Outcome::Submitted(imservice.commit_string(text))
                    },
                    SubmitData::Erase => {
                        /* Delete_surrounding_text takes byte offsets,
                         * so cannot work without get_surrounding_text.
                         * This is a bug in the protocol.
                         */
                        // imservice.delete_surrounding_text(1, 0),
                        Outcome::NotSubmitted
                    },
                    SubmitData::Keycodes => Outcome::NotSubmitted,
                };
158

159
160
161
162
163
164
165
166
                match submit_outcome {
                    Outcome::Submitted(result) => {
                        match result.and_then(|()| imservice.commit()) {
                            Ok(()) => true,
                            Err(imservice::SubmitError::NotActive) => false,
                        }
                    },
                    Outcome::NotSubmitted => false,
167
168
                }
            },
169
            (_, _) => false,
170
        };
171

172
        let submit_action = match was_committed_as_text {
173
174
175
            true => SubmittedAction::IMService,
            false => {
                self.virtual_keyboard.switch(
176
                    keycodes,
177
178
179
                    PressType::Pressed,
                    time,
                );
180
                SubmittedAction::VirtualKeyboard(keycodes.clone())
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
            },
        };
        
        self.pressed.push((key_id, submit_action));
    }
    
    pub fn handle_release(&mut self, key_id: KeyStateId, time: Timestamp) {
        let index = self.pressed.iter().position(|(id, _)| *id == key_id);
        if let Some(index) = index {
            let (_id, action) = self.pressed.remove(index);
            match action {
                // string already sent, nothing to do
                SubmittedAction::IMService => {},
                // no matter if the imservice got activated,
                // keys must be released
                SubmittedAction::VirtualKeyboard(keycodes) => {
                    self.virtual_keyboard.switch(
                        &keycodes,
                        PressType::Released,
                        time,
                    )
                },
            }
        };
    }
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
    
    pub fn handle_add_modifier(
        &mut self,
        key_id: KeyStateId,
        modifier: Modifier, _time: Timestamp,
    ) {
        self.modifiers_active.push((key_id, modifier));
        self.update_modifiers();
    }

    pub fn handle_drop_modifier(
        &mut self,
        key_id: KeyStateId,
        _time: Timestamp,
    ) {
        vec_remove(&mut self.modifiers_active, |(id, _)| *id == key_id);
        self.update_modifiers();
    }

    fn update_modifiers(&mut self) {
        let raw_modifiers = self.modifiers_active.iter()
            .map(|(_id, m)| match m {
                Modifier::Control => Modifiers::CONTROL,
                Modifier::Alt => Modifiers::MOD1,
            })
            .fold(Modifiers::empty(), |m, n| m | n);
        self.virtual_keyboard.set_modifiers_state(raw_modifiers);
    }

    pub fn is_modifier_active(&self, modifier: Modifier) -> bool {
        self.modifiers_active.iter()
            .position(|(_id, m)| *m == modifier)
            .is_some()
    }

    pub fn get_active_modifiers(&self) -> HashSet<Modifier> {
        HashSet::from_iter(
            self.modifiers_active.iter().map(|(_id, m)| m.clone())
        )
    }
246
}