diff --git a/pyanaconda/display.py b/pyanaconda/display.py index 114fdfed65b4..57f3e988c5c0 100644 --- a/pyanaconda/display.py +++ b/pyanaconda/display.py @@ -260,6 +260,9 @@ def start_wayland_compositor(): FIXME: Will XDG_DATA_DIRS work with Wayland compositor ? """ + # mark Wayland is in use + flags.wayland = True + datadir = os.environ.get('ANACONDA_DATADIR', '/usr/share/anaconda') if 'XDG_DATA_DIRS' in os.environ: xdg_data_dirs = datadir + '/window-manager:' + os.environ['XDG_DATA_DIRS'] diff --git a/pyanaconda/flags.py b/pyanaconda/flags.py index 000fd5df876a..47ffbce0077c 100644 --- a/pyanaconda/flags.py +++ b/pyanaconda/flags.py @@ -36,6 +36,7 @@ def __init__(self): self.usevnc = False self.vncquestion = True self.preexisting_x11 = False + self.wayland = False self.automatedInstall = False self.eject = True # ksprompt is whether or not to prompt for missing ksdata diff --git a/pyanaconda/ui/gui/kbd_wrapper.py b/pyanaconda/ui/gui/kbd_wrapper.py new file mode 100644 index 000000000000..977f7a1ebd7d --- /dev/null +++ b/pyanaconda/ui/gui/kbd_wrapper.py @@ -0,0 +1,208 @@ +# +# Copyright (C) 2023 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# + +""" +This module provides an API compatible dummy +replacement for the XklWrapper, for temporary +use when Anaconda runs on a Wayland compositor. + +Eventually the class should be "filled in" and use +a real modern API that works on Wayland compositors +and possible even on X if we still support it +at that point. +""" + +import threading + +from pyanaconda.core.async_utils import async_action_wait + +from pyanaconda.anaconda_loggers import get_module_logger +log = get_module_logger(__name__) + +class KbdWrapper(object): + """ + Class wrapping a keyboard access API. + + Right now this is just a dummy class + that can be used in place of libXklavier + when running on a Wayland compositor. + + But eventually this class should wrap + a modern keyboard handling API that + works on Wayland (and ideally X if we + still support it at that point). + """ + + _instance = None + _instance_lock = threading.Lock() + + @staticmethod + def get_instance(): + with KbdWrapper._instance_lock: + if not KbdWrapper._instance: + KbdWrapper._instance = KbdWrapper() + + return KbdWrapper._instance + + def __init__(self): + self._layout_infos = dict() + self._layout_infos_lock = threading.RLock() + self._switch_opt_infos = dict() + self._switch_opt_infos_lock = threading.RLock() + log.debug("KbdWrapper: initialized") + + def get_current_layout(self): + """ + Get current activated X layout and variant + + :return: current activated X layout and variant (e.g. "cz (qwerty)") + + """ + + log.warning("KbdWrapper: get_current_layout() - returning dummy data") + return "cz (qwerty)" + + def get_available_layouts(self): + """A list of layouts""" + + log.warning("KbdWrapper: get_available_layouts() - returning dummy data") + with self._layout_infos_lock: + return list(self._layout_infos.keys()) + + def get_common_layouts(self): + """A list of common layouts""" + + log.warning("KbdWrapper: get_common_layouts() - returning dummy data") + return [] + + def get_switching_options(self): + """Method returning list of available layout switching options""" + + log.warning("KbdWrapper: get_switching_options() - returning dummy data") + with self._switch_opt_infos_lock: + return list(self._switch_opt_infos.keys()) + + def get_layout_variant_description(self, layout_variant, with_lang=True, xlated=True): + """ + Get description of the given layout-variant. + + :param layout_variant: layout-variant specification (e.g. 'cz (qwerty)') + :type layout_variant: str + :param with_lang: whether to include language of the layout-variant (if defined) + in the description or not + :type with_lang: bool + :param xlated: whethe to return translated or english version of the description + :type xlated: bool + :return: description of the layout-variant specification (e.g. 'Czech (qwerty)') + :rtype: str + + """ + log.warning("KbdWrapper: get_layout_variant_description() - returning dummy data") + return "cz (qwerty)" + + + def get_switch_opt_description(self, switch_opt): + """ + Get description of the given layout switching option. + + :param switch_opt: switching option name/ID (e.g. 'grp:alt_shift_toggle') + :type switch_opt: str + :return: description of the layout switching option (e.g. 'Alt + Shift') + :rtype: str + + """ + + log.warning("KbdWrapper: get_switch_opt_description() - returning dummy data") + return "" + + @async_action_wait + def activate_default_layout(self): + """ + Activates default layout (the first one in the list of configured + layouts). + + """ + + log.warning("KbdWrapper: activate_default_layout() - not implemented") + + def is_valid_layout(self, layout): + """Return if given layout is valid layout or not""" + + log.warning("KbdWrapper: is_valid_layout() - not implemented (always False)") + return False + + @async_action_wait + def add_layout(self, layout): + """ + Method that tries to add a given layout to the current X configuration. + + The X layouts configuration is handled by two lists. A list of layouts + and a list of variants. Index-matching items in these lists (as if they + were zipped) are used for the construction of real layouts (e.g. + 'cz (qwerty)'). + + :param layout: either 'layout' or 'layout (variant)' + :raise XklWrapperError: if the given layout is invalid or cannot be added + + """ + + log.warning("KbdWrapper: add_layout() - not implemented") + + @async_action_wait + def remove_layout(self, layout): + """ + Method that tries to remove a given layout from the current X + configuration. + + See also the documentation for the add_layout method. + + :param layout: either 'layout' or 'layout (variant)' + :raise XklWrapperError: if the given layout cannot be removed + + """ + + log.warning("KbdWrapper: remove_layout() - not implemented") + + @async_action_wait + def replace_layouts(self, layouts_list): + """ + Method that replaces the layouts defined in the current X configuration + with the new ones given. + + :param layouts_list: list of layouts defined as either 'layout' or + 'layout (variant)' + :raise XklWrapperError: if layouts cannot be replaced with the new ones + + """ + + log.warning("KbdWrapper: replace_layouts() - not implemented") + + @async_action_wait + def set_switching_options(self, options): + """ + Method that sets options for layout switching. It replaces the old + options with the new ones. + + :param options: layout switching options to be set + :type options: list or generator + :raise XklWrapperError: if the old options cannot be replaced with the + new ones + + """ + + log.warning("KbdWrapper: set_switching_options() - not implemented") diff --git a/pyanaconda/ui/gui/spokes/keyboard.py b/pyanaconda/ui/gui/spokes/keyboard.py index d2e8981dafb6..6e7765c9e891 100644 --- a/pyanaconda/ui/gui/spokes/keyboard.py +++ b/pyanaconda/ui/gui/spokes/keyboard.py @@ -18,10 +18,11 @@ # import gi -gi.require_version("Gkbd", "3.0") +#gi.require_version("Gkbd", "3.0") gi.require_version("Gtk", "3.0") -from gi.repository import Gkbd, Gtk +#from gi.repository import Gkbd, Gtk +from gi.repository import Gtk import locale as locale_mod from pyanaconda.ui.gui import GUIObject @@ -29,7 +30,9 @@ from pyanaconda.ui.categories.localization import LocalizationCategory from pyanaconda.ui.gui.utils import gtk_call_once, escape_markup, gtk_batch_map, timed_action from pyanaconda.ui.gui.utils import override_cell_property +# FIXME: can we even import this ? from pyanaconda.ui.gui.xkl_wrapper import XklWrapper, XklWrapperError +from pyanaconda.ui.gui.kbd_wrapper import KbdWrapper from pyanaconda import keyboard from pyanaconda import flags from pyanaconda.core.i18n import _, N_, CN_ @@ -67,6 +70,15 @@ def _show_description(column, renderer, model, itr, wrapper): return value +def _get_keyboard_handling_wrapper(): + if flags.flags.wayland: + log.debug("keyboard - layout: running with Xorg, using XklWrapper") + return XklWrapper.get_instance() + else: + log.debug("keyboard: running with Wayland compositor, using KbdWrapper") + return KbdWrapper.get_instance() + + class AddLayoutDialog(GUIObject): builderObjects = ["addLayoutDialog", "newLayoutStore", "newLayoutStoreFilter"] @@ -75,7 +87,8 @@ class AddLayoutDialog(GUIObject): def __init__(self, data): super().__init__(data) - self._xkl_wrapper = XklWrapper.get_instance() + # FIXME: rename the variable ? + self._xkl_wrapper = _get_keyboard_handling_wrapper() self._chosen_layouts = [] def matches_entry(self, model, itr, user_data=None): @@ -214,7 +227,8 @@ class ConfigureSwitchingDialog(GUIObject): def __init__(self, data, l12_module): super().__init__(data) - self._xkl_wrapper = XklWrapper.get_instance() + # FIXME: rename the variable ? + self._xkl_wrapper = _get_keyboard_handling_wrapper() self._switchingOptsStore = self.builder.get_object("switchingOptsStore") @@ -320,7 +334,8 @@ def __init__(self, *args): super().__init__(*args) self._remove_last_attempt = False self._confirmed = False - self._xkl_wrapper = XklWrapper.get_instance() + # FIXME: rename the variable ? + self._xkl_wrapper = _get_keyboard_handling_wrapper() self._add_dialog = None self._ready = False