diff --git a/config/keyboard/desktop.conf b/config/keyboard/desktop.conf index 23fb312b..8d9e9be0 100644 --- a/config/keyboard/desktop.conf +++ b/config/keyboard/desktop.conf @@ -126,3 +126,4 @@ KEY_FENRIR,KEY_F8=export_clipboard_to_x KEY_FENRIR,KEY_CTRL,KEY_UP=inc_alsa_volume KEY_FENRIR,KEY_CTRL,KEY_DOWN=dec_alsa_volume KEY_FENRIR,KEY_SHIFT,KEY_V=announce_fenrir_version +KEY_CTRL,KEY_ALT,KEY_ESC=cycle_keyboard_layout diff --git a/config/keyboard/laptop.conf b/config/keyboard/laptop.conf index 8affb134..7a5d8e62 100644 --- a/config/keyboard/laptop.conf +++ b/config/keyboard/laptop.conf @@ -126,3 +126,4 @@ KEY_FENRIR,KEY_F8=export_clipboard_to_x KEY_FENRIR,KEY_CTRL,KEY_UP=inc_alsa_volume KEY_FENRIR,KEY_CTRL,KEY_DOWN=dec_alsa_volume KEY_FENRIR,KEY_SHIFT,KEY_V=announce_fenrir_version +KEY_CTRL,KEY_ALT,KEY_ESC=cycle_keyboard_layout diff --git a/config/keyboard/nvda-desktop.conf b/config/keyboard/nvda-desktop.conf index 9a0dba35..7c754382 100644 --- a/config/keyboard/nvda-desktop.conf +++ b/config/keyboard/nvda-desktop.conf @@ -126,3 +126,4 @@ KEY_FENRIR,KEY_CTRL,KEY_C=save_settings KEY_FENRIR,KEY_F8=export_clipboard_to_x KEY_FENRIR,KEY_ALT,KEY_UP=inc_alsa_volume KEY_FENRIR,KEY_ALT,KEY_DOWN=dec_alsa_volume +KEY_CTRL,KEY_ALT,KEY_ESC=cycle_keyboard_layout diff --git a/config/keyboard/nvda-laptop.conf b/config/keyboard/nvda-laptop.conf index dab8d124..54a78745 100644 --- a/config/keyboard/nvda-laptop.conf +++ b/config/keyboard/nvda-laptop.conf @@ -126,3 +126,4 @@ KEY_FENRIR,KEY_CTRL,KEY_C=save_settings KEY_FENRIR,KEY_F8=export_clipboard_to_x KEY_FENRIR,KEY_ALT,KEY_UP=inc_alsa_volume KEY_FENRIR,KEY_ALT,KEY_DOWN=dec_alsa_volume +KEY_CTRL,KEY_ALT,KEY_ESC=cycle_keyboard_layout diff --git a/config/keyboard/pty.conf b/config/keyboard/pty.conf index 871bfd4b..76fa95e8 100644 --- a/config/keyboard/pty.conf +++ b/config/keyboard/pty.conf @@ -85,3 +85,5 @@ alt+f12 - quit fenrir ^[[1;3F=temp_disable_speech # control+end - toggle auto read ^[[1;5F=toggle_auto_read +# control+alt+escape - cycle keyboard layout +^[[27;7;27~=cycle_keyboard_layout diff --git a/config/keyboard/speakup.conf b/config/keyboard/speakup.conf index 17d4aca0..cce1b982 100644 --- a/config/keyboard/speakup.conf +++ b/config/keyboard/speakup.conf @@ -216,3 +216,4 @@ KEY_FENRIR,KEY_F7=import_clipboard_from_x KEY_FENRIR,KEY_F8=export_clipboard_to_x KEY_FENRIR,KEY_CTRL,KEY_UP=inc_alsa_volume KEY_FENRIR,KEY_CTRL,KEY_DOWN=dec_alsa_volume +KEY_CTRL,KEY_ALT,KEY_ESC=cycle_keyboard_layout diff --git a/src/fenrirscreenreader/commands/commands/cycle_keyboard_layout.py b/src/fenrirscreenreader/commands/commands/cycle_keyboard_layout.py new file mode 100644 index 00000000..1e38f343 --- /dev/null +++ b/src/fenrirscreenreader/commands/commands/cycle_keyboard_layout.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +# Fenrir TTY screen reader +# By Chrys, Storm Dragon, and contributers. + +from fenrirscreenreader.core import debug +import os + +class command(): + def __init__(self): + pass + def initialize(self, environment): + self.env = environment + def shutdown(self): + pass + def getDescription(self): + return _('cycles between available keyboard layouts') + + def getAvailableLayouts(self): + """Get list of available keyboard layout files""" + layouts = [] + + # Check standard locations for keyboard layouts + settingsRoot = '/etc/fenrirscreenreader/' + if not os.path.exists(settingsRoot): + # Fallback to source directory + import fenrirscreenreader + fenrirPath = os.path.dirname(fenrirscreenreader.__file__) + settingsRoot = fenrirPath + '/../../config/' + + keyboardPath = settingsRoot + 'keyboard/' + + if os.path.exists(keyboardPath): + for file in os.listdir(keyboardPath): + if file.endswith('.conf') and not file.startswith('__'): + layout_name = file.replace('.conf', '') + if layout_name not in layouts: + layouts.append(layout_name) + + # Ensure we have at least basic layouts + if not layouts: + layouts = ['desktop', 'laptop'] + else: + layouts.sort() + + return layouts + + def run(self): + current_layout = self.env['runtime']['settingsManager'].getSetting('keyboard', 'keyboardLayout') + + # Extract layout name from full path if needed + if '/' in current_layout: + current_layout = os.path.basename(current_layout).replace('.conf', '') + + # Get available layouts + available_layouts = self.getAvailableLayouts() + + # Find next layout in cycle + try: + current_index = available_layouts.index(current_layout) + next_index = (current_index + 1) % len(available_layouts) + except ValueError: + # If current layout not found, start from beginning + next_index = 0 + + next_layout = available_layouts[next_index] + + # Update setting and reload shortcuts + self.env['runtime']['settingsManager'].setSetting('keyboard', 'keyboardLayout', next_layout) + + # Reload shortcuts with new layout + try: + self.env['runtime']['inputManager'].reloadShortcuts() + self.env['runtime']['outputManager'].presentText( + _('Switched to {} keyboard layout').format(next_layout), + interrupt=True + ) + except Exception as e: + self.env['runtime']['debug'].writeDebugOut( + "Error reloading shortcuts: " + str(e), + debug.debugLevel.ERROR + ) + self.env['runtime']['outputManager'].presentText( + _('Error switching keyboard layout'), + interrupt=True + ) + + def setCallback(self, callback): + pass \ No newline at end of file diff --git a/src/fenrirscreenreader/core/inputManager.py b/src/fenrirscreenreader/core/inputManager.py index ac993f11..ee7d6e67 100644 --- a/src/fenrirscreenreader/core/inputManager.py +++ b/src/fenrirscreenreader/core/inputManager.py @@ -389,3 +389,35 @@ class inputManager(): self.lastDetectedDevices =devices def getLastDetectedDevices(self): return self.lastDetectedDevices + def reloadShortcuts(self): + """Reload keyboard shortcuts from current layout setting""" + # Clear existing bindings + self.env['bindings'].clear() + self.env['rawBindings'].clear() + + # Get current layout path + layout_setting = self.env['runtime']['settingsManager'].getSetting('keyboard', 'keyboardLayout') + + # Resolve full path if needed + if not os.path.exists(layout_setting): + settingsRoot = '/etc/fenrirscreenreader/' + if not os.path.exists(settingsRoot): + import fenrirscreenreader + fenrirPath = os.path.dirname(fenrirscreenreader.__file__) + settingsRoot = fenrirPath + '/../../config/' + + layout_path = settingsRoot + 'keyboard/' + layout_setting + '.conf' + if not os.path.exists(layout_path): + # Fallback to default if layout not found + layout_path = settingsRoot + 'keyboard/desktop.conf' + else: + layout_path = layout_setting + + # Reload shortcuts + self.loadShortcuts(layout_path) + + self.env['runtime']['debug'].writeDebugOut( + "Reloaded shortcuts from: " + layout_path, + debug.debugLevel.INFO, + onAnyLevel=True + )