|
20 | 20 |
|
21 | 21 | import lvgl as lv |
22 | 22 | import mpos.ui.theme |
| 23 | +import time |
23 | 24 |
|
24 | 25 |
|
25 | 26 | class MposKeyboard: |
@@ -60,6 +61,14 @@ def __init__(self, parent): |
60 | 61 | # Store textarea reference (we DON'T pass it to LVGL to avoid double-typing) |
61 | 62 | self._textarea = None |
62 | 63 |
|
| 64 | + # Track last mode switch time to prevent race conditions |
| 65 | + # When user rapidly clicks mode buttons, button indices can get confused |
| 66 | + # because index 29 is "abc" in numbers mode but "," in lowercase mode |
| 67 | + self._last_mode_switch_time = 0 |
| 68 | + |
| 69 | + # Re-entrancy guard to prevent recursive event processing during mode switches |
| 70 | + self._in_mode_switch = False |
| 71 | + |
63 | 72 | # Configure layouts |
64 | 73 | self._setup_layouts() |
65 | 74 |
|
@@ -148,11 +157,20 @@ def _handle_events(self, event): |
148 | 157 | # This prevents LVGL's default handler from interfering |
149 | 158 | event.stop_processing() |
150 | 159 |
|
| 160 | + # Re-entrancy guard: Skip processing if we're currently switching modes |
| 161 | + # This prevents set_mode() from triggering recursive event processing |
| 162 | + if self._in_mode_switch: |
| 163 | + return |
| 164 | + |
151 | 165 | # Get the pressed button and its text |
152 | 166 | button = self._keyboard.get_selected_button() |
| 167 | + current_mode = self._keyboard.get_mode() |
153 | 168 | text = self._keyboard.get_button_text(button) |
154 | 169 |
|
155 | | - # Ignore if no valid button text (can happen during initialization) |
| 170 | + # DEBUG |
| 171 | + print(f"[KBD] btn={button}, mode={current_mode}, text='{text}'") |
| 172 | + |
| 173 | + # Ignore if no valid button text (can happen during mode switching) |
156 | 174 | if text is None: |
157 | 175 | return |
158 | 176 |
|
@@ -245,29 +263,29 @@ def set_mode(self, mode): |
245 | 263 | mode: One of MODE_LOWERCASE, MODE_UPPERCASE, MODE_NUMBERS, MODE_SPECIALS |
246 | 264 | (can also accept standard LVGL modes) |
247 | 265 | """ |
248 | | - # Determine which layout we're switching to |
249 | | - # We need to set the map for BOTH the USER mode and the corresponding standard mode |
250 | | - # to prevent crashes if LVGL internally switches between them |
| 266 | + # Map modes to their layouts |
251 | 267 | mode_info = { |
252 | | - self.MODE_LOWERCASE: (self._lowercase_map, self._lowercase_ctrl, [self.MODE_LOWERCASE, lv.keyboard.MODE.TEXT_LOWER]), |
253 | | - self.MODE_UPPERCASE: (self._uppercase_map, self._uppercase_ctrl, [self.MODE_UPPERCASE, lv.keyboard.MODE.TEXT_UPPER]), |
254 | | - self.MODE_NUMBERS: (self._numbers_map, self._numbers_ctrl, [self.MODE_NUMBERS, lv.keyboard.MODE.NUMBER]), |
255 | | - self.MODE_SPECIALS: (self._specials_map, self._specials_ctrl, [self.MODE_SPECIALS, lv.keyboard.MODE.SPECIAL]), |
256 | | - # Also support standard LVGL modes |
257 | | - lv.keyboard.MODE.TEXT_LOWER: (self._lowercase_map, self._lowercase_ctrl, [self.MODE_LOWERCASE, lv.keyboard.MODE.TEXT_LOWER]), |
258 | | - lv.keyboard.MODE.TEXT_UPPER: (self._uppercase_map, self._uppercase_ctrl, [self.MODE_UPPERCASE, lv.keyboard.MODE.TEXT_UPPER]), |
259 | | - lv.keyboard.MODE.NUMBER: (self._numbers_map, self._numbers_ctrl, [self.MODE_NUMBERS, lv.keyboard.MODE.NUMBER]), |
260 | | - lv.keyboard.MODE.SPECIAL: (self._specials_map, self._specials_ctrl, [self.MODE_SPECIALS, lv.keyboard.MODE.SPECIAL]), |
| 268 | + self.MODE_LOWERCASE: (self._lowercase_map, self._lowercase_ctrl), |
| 269 | + self.MODE_UPPERCASE: (self._uppercase_map, self._uppercase_ctrl), |
| 270 | + self.MODE_NUMBERS: (self._numbers_map, self._numbers_ctrl), |
| 271 | + self.MODE_SPECIALS: (self._specials_map, self._specials_ctrl), |
261 | 272 | } |
262 | 273 |
|
263 | | - if mode in mode_info: |
264 | | - key_map, ctrl_map, mode_list = mode_info[mode] |
265 | | - # CRITICAL: Set the map for BOTH modes to prevent NULL pointer crashes |
266 | | - # This ensures the map is set regardless of which mode LVGL uses internally |
267 | | - for m in mode_list: |
268 | | - self._keyboard.set_map(m, key_map, ctrl_map) |
269 | | - |
270 | | - self._keyboard.set_mode(mode) |
| 274 | + # Set re-entrancy guard to block any events triggered during mode switch |
| 275 | + self._in_mode_switch = True |
| 276 | + |
| 277 | + try: |
| 278 | + # Set the map for the new mode BEFORE calling set_mode() |
| 279 | + # This prevents crashes from set_mode() being called with no map set |
| 280 | + if mode in mode_info: |
| 281 | + key_map, ctrl_map = mode_info[mode] |
| 282 | + self._keyboard.set_map(mode, key_map, ctrl_map) |
| 283 | + |
| 284 | + # Now switch to the new mode |
| 285 | + self._keyboard.set_mode(mode) |
| 286 | + finally: |
| 287 | + # Always clear the guard, even if an exception occurs |
| 288 | + self._in_mode_switch = False |
271 | 289 |
|
272 | 290 | # ======================================================================== |
273 | 291 | # Python magic method for automatic method forwarding |
|
0 commit comments