submission.rs 5.63 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
21
 
use ::action::Action;
use ::imservice;
22
use ::imservice::IMService;
23
use ::keyboard::{ KeyCode, KeyState, KeyStateId, PressType };
24
use ::vkeyboard::VirtualKeyboard;
25
26
27
28
29
30
31
32

/// Gathers stuff defined in C or called by C
pub mod c {
    use super::*;
    
    use std::os::raw::c_void;

    use ::imservice::c::InputMethod;
33
34
    use ::layout::c::LevelKeyboard;
    use ::vkeyboard::c::ZwpVirtualKeyboardV1;
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

    // 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,
50
        vk: ZwpVirtualKeyboardV1,
51
52
53
54
55
56
57
58
59
60
61
        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,
62
                virtual_keyboard: VirtualKeyboard(vk),
63
                pressed: Vec::new(),
64
65
66
67
            }
        ))
    }

68
    /// Use to initialize the UI reference
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
    #[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)
            }
        };
    }
84
85
86
87
88
89
90
91
92
93

    #[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);
    }
94
95
}

96
97
98
#[derive(Clone, Copy)]
pub struct Timestamp(pub u32);

99
100
101
102
103
104
enum SubmittedAction {
    /// A collection of keycodes that were pressed
    VirtualKeyboard(Vec<KeyCode>),
    IMService,
}

105
106
pub struct Submission {
    imservice: Option<Box<IMService>>,
107
108
109
110
111
112
113
114
115
116
117
118
    virtual_keyboard: VirtualKeyboard,
    pressed: Vec<(KeyStateId, SubmittedAction)>,
}

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,
        key: &KeyState, key_id: KeyStateId,
        time: Timestamp,
    ) {
119
120
121
122
        match &key.action {
            Action::Submit { text: _, keys: _ }
                | Action::Erase
            => (),
123
            _ => {
124
                eprintln!("BUG: Submitted key with action other than Submit or Erase");
125
126
127
128
                return;
            },
        };

129
130
        let was_committed_as_text = match (&mut self.imservice, &key.action) {
            (Some(imservice), Action::Submit { text: Some(text), keys: _ }) => {
131
132
133
134
135
136
137
                let submit_result = imservice.commit_string(text)
                    .and_then(|_| imservice.commit());
                match submit_result {
                    Ok(()) => true,
                    Err(imservice::SubmitError::NotActive) => false,
                }
            },
138
139
140
141
142
143
144
145
            (Some(imservice), Action::Erase) => {
                let submit_result = imservice.delete_surrounding_text(1, 0)
                    .and_then(|_| imservice.commit());
                match submit_result {
                    Ok(()) => true,
                    Err(imservice::SubmitError::NotActive) => false,
                }
            }
146
147
148
            (_, _) => false,
        };
        
149
        let submit_action = match was_committed_as_text {
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
            true => SubmittedAction::IMService,
            false => {
                self.virtual_keyboard.switch(
                    &key.keycodes,
                    PressType::Pressed,
                    time,
                );
                SubmittedAction::VirtualKeyboard(key.keycodes.clone())
            },
        };
        
        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,
                    )
                },
            }
        };
    }
183
}