diff --git a/See b/See deleted file mode 100644 index e69de29bb..000000000 diff --git a/[36 b/[36 deleted file mode 100644 index e69de29bb..000000000 diff --git a/linux/appimage/apprun.sh b/linux/appimage/apprun.sh index f31068c2f..1af393555 100755 --- a/linux/appimage/apprun.sh +++ b/linux/appimage/apprun.sh @@ -109,7 +109,6 @@ APPDIR="$(dirname "$(readlink -e "$0")")" appimage_setenv - # Handle AppImage specific options. [ -n "$APPIMAGE" ] && case "$1" in --install|--uninstall) diff --git a/plover/config.py b/plover/config.py index 7ae6c3330..e9861ae5f 100644 --- a/plover/config.py +++ b/plover/config.py @@ -122,6 +122,20 @@ def validate(config, key, value): return value return ConfigOption(name, lambda c, k: default, getter, setter, validate, None) +def str_option(name, default, section, option=None): + option = option or name + def getter(config, key): + return config._config[section][option] + def setter(config, key, value): + config._set(section, option, str(value)) + def validate(config, key, value): + try: + value = str(value) + except ValueError as e: + raise InvalidConfigOption(value, default) from e + return value + return ConfigOption(name, lambda c, k: default, getter, setter, validate, None) + def boolean_option(name, default, section, option=None): option = option or name def getter(config, key): @@ -338,6 +352,7 @@ def _set(self, section, option, value): boolean_option('start_capitalized', False, OUTPUT_CONFIG_SECTION), int_option('undo_levels', DEFAULT_UNDO_LEVELS, MINIMUM_UNDO_LEVELS, None, OUTPUT_CONFIG_SECTION), int_option('time_between_key_presses', DEFAULT_TIME_BETWEEN_KEY_PRESSES, MINIMUM_TIME_BETWEEN_KEY_PRESSES, None, OUTPUT_CONFIG_SECTION), + str_option('xkb_layout', "us", OUTPUT_CONFIG_SECTION), # Logging. path_option('log_file_name', expand_path('strokes.log'), LOGGING_CONFIG_SECTION, 'log_file'), boolean_option('enable_stroke_logging', False, LOGGING_CONFIG_SECTION), diff --git a/plover/engine.py b/plover/engine.py index cc83c5259..c1c5b8336 100644 --- a/plover/engine.py +++ b/plover/engine.py @@ -212,6 +212,9 @@ def _update(self, config_update=None, full=False, reset_machine=False): self._formatter.start_capitalized = config['start_capitalized'] self._translator.set_min_undo_length(config['undo_levels']) self._keyboard_emulation.set_key_press_delay(config['time_between_key_presses']) + # This only applies to UInput, because it emulates a physical keyboard and follows the layout set in software. Because there is no standard of defining it, the user has to do so manually if not using an US keyboard if not using an US keyboard. + if hasattr(self._keyboard_emulation, '_update_layout'): + self._keyboard_emulation._update_layout(config['xkb_layout']) # Update system. system_name = config['system_name'] if system.NAME != system_name: diff --git a/plover/gui_qt/config_window.py b/plover/gui_qt/config_window.py index a200a10d4..c24818f75 100644 --- a/plover/gui_qt/config_window.py +++ b/plover/gui_qt/config_window.py @@ -20,6 +20,7 @@ QLabel, QScrollArea, QSpinBox, + QLineEdit, QStyledItemDelegate, QTableWidget, QTableWidgetItem, @@ -71,6 +72,17 @@ def __init__(self, maximum=None, minimum=None): self.setMinimum(minimum) +class StrOption(QLineEdit): + + valueChanged = pyqtSignal(str) + + def __init__(self): + super().__init__() + self.textChanged.connect(self.valueChanged.emit) + + def setValue(self, value): + self.setText(value) + class ChoiceOption(QComboBox): valueChanged = pyqtSignal(str) @@ -392,6 +404,16 @@ def __init__(self, engine): 'programs time to process each key press.\n' 'Setting the delay too high will negatively impact the\n' 'performance of key stroke output.')), + ConfigOption(_('Keyboard Layout:'), 'xkb_layout', StrOption, + _('Set the keyboard layout configured in your system.\n' + 'Examples: "us", "gb", "fr", "no"\n' + '\n' + 'This only applies when using Linux/BSD and not using X11.\n' + 'If you\'re unsure, you probably don\'t need to change it.\n' + 'If you need to configure more options about your layout,\n' + 'such as setting the variant to a different layout like colemak,\n' + 'you can set environment variables starting with XKB_DEFAULT_\n' + 'for the RULES, MODEL, VARIANT and OPTIONS')), )), # i18n: Widget: “ConfigWindow”. (_('Plugins'), ( diff --git a/plover/oslayer/linux/keyboardcontrol_uinput.py b/plover/oslayer/linux/keyboardcontrol_uinput.py index 60f054177..071138131 100644 --- a/plover/oslayer/linux/keyboardcontrol_uinput.py +++ b/plover/oslayer/linux/keyboardcontrol_uinput.py @@ -1,8 +1,3 @@ -# TODO: get a stop() function to run when KeyboardEmulation stops, calling `self._ui.close()` -# TODO: figure out a better way to do this: -import os # To read the layout variable - -# TODO: somehow get the requires udev rule installed from evdev import UInput, ecodes as e, util, InputDevice, list_devices import threading from select import select @@ -163,7 +158,7 @@ "eject": e.KEY_EJECTCD, "audiopause": e.KEY_PAUSE, "audioplay": e.KEY_PLAY, - "audionext": e.KEY_NEXT, # Maybe VIDEO_NEXT or NEXTSONG + "audionext": e.KEY_NEXT, "audiorewind": e.KEY_REWIND, "kbdbrightnessup": e.KEY_KBDILLUMUP, "kbdbrightnessdown": e.KEY_KBDILLUMDOWN, @@ -177,18 +172,11 @@ def __init__(self): # Initialize UInput with all keys available self._res = util.find_ecodes_by_regex(r"KEY_.*") self._ui = UInput(self._res) - # Set the keyboard layout from an environment variable - kb_env = "PLOVER_UINPUT_LAYOUT" - kb_layout = os.environ[kb_env] if kb_env in os.environ else "us" - self._set_layout(kb_layout) - def _set_layout(self, layout): - # Get the system keyboard layout so that emulation works on different layouts + def _update_layout(self, layout): log.info("Using keyboard layout " + layout + " for keyboard emulation.") symbols = generate_symbols(layout) - # Remove unwanted symbols from the table - # Includes symbols such as numpad-star - use unicode instead - # There has to be a cleaner way to do this + # Remove symbols not in KEY_TO_KEYCODE syms_to_remove = [] for sym in symbols: (base, _) = symbols[sym]