submission.rs 5.88 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 ::logging;
25
use ::vkeyboard::VirtualKeyboard;
26
27
28
29
30
31
32
33

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

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

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

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

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

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

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

106
107
pub struct Submission {
    imservice: Option<Box<IMService>>,
108
109
110
111
112
113
114
115
116
117
118
119
    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,
    ) {
120
121
122
123
        match &key.action {
            Action::Submit { text: _, keys: _ }
                | Action::Erase
            => (),
124
            _ => {
125
126
127
128
                log_print!(
                    logging::Level::Bug,
                    "Submitted key with action other than Submit or Erase",
                );
129
130
131
132
                return;
            },
        };

133
134
        let was_committed_as_text = match (&mut self.imservice, &key.action) {
            (Some(imservice), Action::Submit { text: Some(text), keys: _ }) => {
135
136
137
138
139
140
141
                let submit_result = imservice.commit_string(text)
                    .and_then(|_| imservice.commit());
                match submit_result {
                    Ok(()) => true,
                    Err(imservice::SubmitError::NotActive) => false,
                }
            },
142
143
144
            /* Delete_surrounding_text takes byte offsets,
             * so cannot work without get_surrounding_text.
             * This is a bug in the protocol.
145
146
147
148
149
150
151
            (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,
                }
152
            }*/
153
154
155
            (_, _) => false,
        };
        
156
        let submit_action = match was_committed_as_text {
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
183
184
185
186
187
188
189
            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,
                    )
                },
            }
        };
    }
190
}