From 572606437f9eebe9640acc3396a0e8eaf74869c7 Mon Sep 17 00:00:00 2001 From: Jiri Konecny Date: Mon, 14 Mar 2022 13:31:55 +0100 Subject: [PATCH 4/6] Don't configure the keyboard in Live environments with XWayland Call the can_configure_keyboard function to check if we can configure the keyboard. The function doesn't allow the configuration in Live environments that run Anaconda on XWayland. Use the xisxwayland tool for detection. Related: rhbz#2016613 (cherry picked from commit e24a1ba6d97481ac81fbfa492f58d9d7e74355b9) --- anaconda.spec.in | 1 + pyanaconda/core/configuration/system.py | 5 ++ pyanaconda/keyboard.py | 48 +++++++++++++++- pyanaconda/ui/gui/__init__.py | 1 + pyanaconda/ui/gui/spokes/keyboard.py | 13 ++--- .../pyanaconda_tests/test_keyboard.py | 56 ++++++++++++++++++- 6 files changed, 114 insertions(+), 10 deletions(-) diff --git a/anaconda.spec.in b/anaconda.spec.in index 57f216f14c..fadc83d858 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -157,6 +157,7 @@ BuildRequires: desktop-file-utils Requires: anaconda-gui = %{version}-%{release} Requires: usermode Requires: zenity +Requires: xisxwayland Recommends: xhost %description live diff --git a/pyanaconda/core/configuration/system.py b/pyanaconda/core/configuration/system.py index 9ff8b77ba4..37fd50ce91 100644 --- a/pyanaconda/core/configuration/system.py +++ b/pyanaconda/core/configuration/system.py @@ -127,6 +127,11 @@ class SystemSection(Section): """Can we configure the keyboard?""" return self._is_boot_iso or self._is_live_os or self._is_booted_os + @property + def can_run_on_xwayland(self): + """Could we run on XWayland?""" + return self._is_live_os + @property def can_modify_syslog(self): """Can we modify syslog?""" diff --git a/pyanaconda/keyboard.py b/pyanaconda/keyboard.py index 3ab47bc420..10b7e56920 100644 --- a/pyanaconda/keyboard.py +++ b/pyanaconda/keyboard.py @@ -26,6 +26,7 @@ import langtable from pyanaconda.core.configuration.anaconda import conf from pyanaconda import localization from pyanaconda.core.constants import DEFAULT_KEYBOARD +from pyanaconda.core.util import execWithRedirect from pyanaconda.modules.common.task import sync_run_task from pyanaconda.modules.common.constants.services import LOCALIZATION @@ -55,6 +56,49 @@ class InvalidLayoutVariantSpec(Exception): pass +def _is_xwayland(): + """Is Anaconda running in XWayland environment? + + This can't be easily detected from the Anaconda because Anaconda + is running as XWayland app. Use xisxwayland tool for the detection. + """ + try: + rc = execWithRedirect('xisxwayland', []) + + if rc == 0: + return True + + log.debug( + "Anaconda doesn't run on XWayland. " + "See xisxwayland --help for more info." + ) + except FileNotFoundError: + log.warning( + "The xisxwayland tool is not available! " + "Taking the environment as not Wayland." + ) + + return False + + +def can_configure_keyboard(): + """Can we configure the keyboard? + + FIXME: This is a temporary solution. + + The is_wayland logic is not part of the configuration so we would + have to add it to the configuration otherwise it won't be accessible + in the Anaconda modules. + """ + if not conf.system.can_configure_keyboard: + return False + + if conf.system.can_run_on_xwayland and _is_xwayland(): + return False + + return True + + def parse_layout_variant(layout_variant_str): """ Parse layout and variant from the string that may look like 'layout' or @@ -180,7 +224,7 @@ def set_x_keyboard_defaults(localization_proxy, xkl_wrapper): new_layouts = [DEFAULT_KEYBOARD] localization_proxy.SetXLayouts(new_layouts) - if conf.system.can_configure_keyboard: + if can_configure_keyboard(): xkl_wrapper.replace_layouts(new_layouts) # the console layout configured should be "native" by default, @@ -193,7 +237,7 @@ def set_x_keyboard_defaults(localization_proxy, xkl_wrapper): # initialize layout switching if needed localization_proxy.SetLayoutSwitchOptions(["grp:alt_shift_toggle"]) - if conf.system.can_configure_keyboard: + if can_configure_keyboard(): xkl_wrapper.set_switching_options(["grp:alt_shift_toggle"]) # activate the language-default layout instead of the additional # one diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index fc95721f97..5b95ee99ac 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -41,6 +41,7 @@ from pyanaconda.core.path import make_directories from pyanaconda import threading as anaconda_threading from pyanaconda.core.glib import Bytes, GError +from pyanaconda.keyboard import can_configure_keyboard from pyanaconda.ui import UserInterface, common from pyanaconda.ui.gui.utils import unbusyCursor from pyanaconda.core.async_utils import async_action_wait diff --git a/pyanaconda/ui/gui/spokes/keyboard.py b/pyanaconda/ui/gui/spokes/keyboard.py index fe7c225719..a32dab0696 100644 --- a/pyanaconda/ui/gui/spokes/keyboard.py +++ b/pyanaconda/ui/gui/spokes/keyboard.py @@ -33,7 +33,6 @@ from pyanaconda.ui.gui.xkl_wrapper import XklWrapper, XklWrapperError from pyanaconda import keyboard from pyanaconda import flags from pyanaconda.core.i18n import _, N_, CN_ -from pyanaconda.core.configuration.anaconda import conf from pyanaconda.core.constants import DEFAULT_KEYBOARD, THREAD_KEYBOARD_INIT, THREAD_ADD_LAYOUTS_INIT from pyanaconda.ui.communication import hubQ from pyanaconda.core.string import strip_accents, have_word_match @@ -381,7 +380,7 @@ class KeyboardSpoke(NormalSpoke): self._add_dialog = AddLayoutDialog(self.data) self._add_dialog.initialize() - if conf.system.can_configure_keyboard: + if keyboard.can_configure_keyboard(): self.builder.get_object("warningBox").hide() # We want to store layouts' names but show layouts as @@ -401,7 +400,7 @@ class KeyboardSpoke(NormalSpoke): self._layoutSwitchLabel = self.builder.get_object("layoutSwitchLabel") - if not conf.system.can_configure_keyboard: + if not keyboard.can_configure_keyboard(): # Disable area for testing layouts as we cannot make # it work without modifying runtime system @@ -453,7 +452,7 @@ class KeyboardSpoke(NormalSpoke): def _addLayout(self, store, name): # first try to add the layout - if conf.system.can_configure_keyboard: + if keyboard.can_configure_keyboard(): self._xkl_wrapper.add_layout(name) # valid layout, append it to the store @@ -466,7 +465,7 @@ class KeyboardSpoke(NormalSpoke): """ - if conf.system.can_configure_keyboard: + if keyboard.can_configure_keyboard(): self._xkl_wrapper.remove_layout(store[itr][0]) store.remove(itr) @@ -558,7 +557,7 @@ class KeyboardSpoke(NormalSpoke): return store.swap(cur, prev) - if conf.system.can_configure_keyboard: + if keyboard.can_configure_keyboard(): self._flush_layouts_to_X() if not store.iter_previous(cur): @@ -581,7 +580,7 @@ class KeyboardSpoke(NormalSpoke): return store.swap(cur, nxt) - if conf.system.can_configure_keyboard: + if keyboard.can_configure_keyboard(): self._flush_layouts_to_X() if activate_default: diff --git a/tests/unit_tests/pyanaconda_tests/test_keyboard.py b/tests/unit_tests/pyanaconda_tests/test_keyboard.py index 5f632ceb5c..b97aef0664 100644 --- a/tests/unit_tests/pyanaconda_tests/test_keyboard.py +++ b/tests/unit_tests/pyanaconda_tests/test_keyboard.py @@ -15,12 +15,66 @@ # License and may only be used or replicated with the express permission of # Red Hat, Inc. # - from pyanaconda import keyboard import unittest import pytest +from unittest.mock import patch + + +class KeyboardUtilsTestCase(unittest.TestCase): + """Test the keyboard utils.""" + + @patch("pyanaconda.keyboard.conf") + @patch("pyanaconda.keyboard.execWithRedirect") + def test_can_configure_keyboard(self, exec_mock, conf_mock): + """Check if the keyboard configuration is enabled or disabled.""" + # It's a dir installation. + conf_mock.system.can_configure_keyboard = False + conf_mock.system.can_run_on_xwayland = False + assert keyboard.can_configure_keyboard() is False + exec_mock.assert_not_called() + + # It's a boot.iso. + conf_mock.system.can_configure_keyboard = True + conf_mock.system.can_run_on_xwayland = False + assert keyboard.can_configure_keyboard() is True + exec_mock.assert_not_called() + + # It's a Live installation on Wayland. + conf_mock.system.can_configure_keyboard = True + conf_mock.system.can_run_on_xwayland = True + exec_mock.return_value = 0 + assert keyboard.can_configure_keyboard() is False + exec_mock.assert_called_once_with('xisxwayland', []) + exec_mock.reset_mock() + + # It's a Live installation and not on Wayland. + conf_mock.system.can_configure_keyboard = True + conf_mock.system.can_run_on_xwayland = True + exec_mock.return_value = 1 # xisxwayland returns 1 if it is not XWayland + assert keyboard.can_configure_keyboard() is True + exec_mock.assert_called_once_with('xisxwayland', []) + exec_mock.reset_mock() + + # It's a Live installation and probably not on Wayland, + # because the xisxwayland tooling is not present. + conf_mock.system.can_configure_keyboard = True + conf_mock.system.can_run_on_xwayland = True + exec_mock.side_effect = FileNotFoundError() + + with self.assertLogs(level="WARNING") as cm: + keyboard.can_configure_keyboard() + + msg = "The xisxwayland tool is not available!" + assert any(map(lambda x: msg in x, cm.output)) + + exec_mock.assert_called_once_with('xisxwayland', []) + exec_mock.reset_mock() + + class ParsingAndJoiningTests(unittest.TestCase): + def test_layout_variant_parsing(self): """Should correctly parse keyboard layout and variant string specs.""" -- 2.35.1