Skip to content

Commit

Permalink
Creating a dbus interface to get local keyboard layouts
Browse files Browse the repository at this point in the history
Adding dbus interface for loading locale keyboards

Remove duplicate function and migrate to localization one
  • Loading branch information
adamkankovsky committed Jan 17, 2025
1 parent 15ff849 commit e590a17
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 22 deletions.
31 changes: 30 additions & 1 deletion pyanaconda/localization.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
import re
from collections import namedtuple

import iso639
import langtable
from xkbregistry import rxkb

from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.core import constants
Expand All @@ -36,7 +38,7 @@
log = get_module_logger(__name__)

SCRIPTS_SUPPORTED_BY_CONSOLE = {'Latn', 'Cyrl', 'Grek'}

LayoutInfo = namedtuple("LayoutInfo", ["langs", "desc"])

class LocalizationConfigError(Exception):
"""Exception class for localization configuration related problems"""
Expand Down Expand Up @@ -390,6 +392,33 @@ def get_territory_locales(territory):
return langtable.list_locales(territoryId=territory)


def _build_layout_infos():
"""Build localized information for keyboard layouts.
:param rxkb_context: RXKB context (e.g., rxkb.Context())
:return: Dictionary with layouts and their descriptions
"""
rxkb_context = rxkb.Context()
layout_infos = {}

for layout in rxkb_context.layouts.values():
name = layout.name
if layout.variant:
name += f" ({layout.variant})"

langs = []
for lang in layout.iso639_codes:
if iso639.find(iso639_2=lang):
langs.append(iso639.to_name(lang))

if name not in layout_infos:
layout_infos[name] = LayoutInfo(langs, layout.description)
else:
layout_infos[name].langs.extend(langs)

return layout_infos


def get_locale_keyboards(locale):
"""Function returning preferred keyboard layouts for the given locale.
Expand Down
59 changes: 59 additions & 0 deletions pyanaconda/modules/common/structures/keyboard_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#
# DBus structure for keyboard layout in localization module.
#
# Copyright (C) 2025 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.
#
from dasbus.structure import DBusData
from dasbus.typing import List, Str # Pylint: disable=wildcard-import

__all__ = ["KeyboardLayout"]


class KeyboardLayout(DBusData):
"""Structure representing a keyboard layout."""

def __init__(self, layout_id="", description="", langs=""):
self._layout_id = layout_id
self._description = description
self._langs = langs

@property
def layout_id(self) -> Str:
"""Return the keyboard layout ID."""
return self._layout_id

@layout_id.setter
def layout_id(self, value: Str):
self._layout_id = value

@property
def description(self) -> Str:
"""Return the description of the layout."""
return self._description

@description.setter
def description(self, value: Str):
self._description = value

@property
def langs(self) -> List[Str]:
"""Return the list of associated languages."""
return self._langs

@langs.setter
def langs(self, value: List[Str]):
self._langs = value
28 changes: 28 additions & 0 deletions pyanaconda/modules/localization/localization.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
import gettext

import langtable

from pyanaconda.anaconda_loggers import get_module_logger
from pyanaconda.core.configuration.anaconda import conf
from pyanaconda.core.dbus import DBus
from pyanaconda.core.signal import Signal
from pyanaconda.localization import (
_build_layout_infos,
get_available_translations,
get_common_languages,
get_english_name,
Expand All @@ -34,6 +37,7 @@
from pyanaconda.modules.common.base import KickstartService
from pyanaconda.modules.common.constants.services import LOCALIZATION
from pyanaconda.modules.common.containers import TaskContainer
from pyanaconda.modules.common.structures.keyboard_layout import KeyboardLayout
from pyanaconda.modules.common.structures.language import LanguageData, LocaleData
from pyanaconda.modules.localization.installation import (
KeyboardInstallationTask,
Expand All @@ -50,6 +54,8 @@

log = get_module_logger(__name__)

Xkb_ = lambda x: gettext.translation("xkeyboard-config", fallback=True).gettext(x)
iso_ = lambda x: gettext.translation("iso_639", fallback=True).gettext(x)

class LocalizationService(KickstartService):
"""The Localization service."""
Expand Down Expand Up @@ -80,6 +86,8 @@ def __init__(self):
self.compositor_selected_layout_changed = Signal()
self.compositor_layouts_changed = Signal()

self._layout_infos = _build_layout_infos()

self._localed_wrapper = None

def publish(self):
Expand Down Expand Up @@ -177,6 +185,26 @@ def get_locale_data(self, locale_id):

return tdata

def get_locale_keyboard_layouts(self, lang):
"""Get localized keyboard layouts for a given locale.
:param lang: locale string (e.g., "cs_CZ.UTF-8")
:return: list of dictionaries with keyboard layout information
"""
language_id = lang.split("_")[0].lower()
english_name = get_english_name(language_id)

return [
KeyboardLayout(
layout_id=name,
description=Xkb_(info.desc),
langs=info.langs,
)
# Include only the layouts of the current language
for name, info in self._layout_infos.items()
if english_name in info.langs
]

@property
def language(self):
"""Return the language."""
Expand Down
16 changes: 16 additions & 0 deletions pyanaconda/modules/localization/localization_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from pyanaconda.modules.common.base import KickstartModuleInterface
from pyanaconda.modules.common.constants.services import LOCALIZATION
from pyanaconda.modules.common.containers import TaskContainer
from pyanaconda.modules.common.structures.keyboard_layout import KeyboardLayout
from pyanaconda.modules.common.structures.language import LanguageData, LocaleData


Expand Down Expand Up @@ -91,6 +92,21 @@ def GetLocaleData(self, locale_id: Str) -> Structure:
locale_data = self.implementation.get_locale_data(locale_id)
return LocaleData.to_structure(locale_data)

def GetLocaleKeyboardLayouts(self, lang: Str) -> List[Structure]:
"""Get keyboard layouts for the specified language.
For example: [
{"description": "US layout", "layoutId": "us", "variantId": ""},
{"description": "Czech QWERTY", "layoutId": "cz", "variantId": "qwerty"}
]
:param lang: Language code string (e.g., "en_US.UTF-8")
:return: List of keyboard layout dictionaries
"""
return KeyboardLayout.to_structure_list(
self.implementation.get_locale_keyboard_layouts(lang)
)

@property
def Language(self) -> Str:
"""The language the system will use."""
Expand Down
23 changes: 2 additions & 21 deletions pyanaconda/ui/gui/xkl_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,19 @@

import gettext
import threading
from collections import namedtuple

import iso639
from xkbregistry import rxkb

from pyanaconda import localization
from pyanaconda.core.async_utils import async_action_wait
from pyanaconda.core.string import upcase_first_letter
from pyanaconda.keyboard import normalize_layout_variant
from pyanaconda.localization import _build_layout_infos
from pyanaconda.modules.common.constants.services import LOCALIZATION

Xkb_ = lambda x: gettext.translation("xkeyboard-config", fallback=True).gettext(x)
iso_ = lambda x: gettext.translation("iso_639", fallback=True).gettext(x)

# namedtuple for information about a keyboard layout (its language and description)
LayoutInfo = namedtuple("LayoutInfo", ["langs", "desc"])

class XklWrapper:
"""
Class that used to wrap libxklavier functionality.
Expand Down Expand Up @@ -62,26 +58,11 @@ def __init__(self):
self._rxkb = rxkb.Context()

self._layout_infos = {}
self._build_layout_infos()
self._layout_infos = _build_layout_infos()

self._switch_opt_infos = {}
self._build_switch_opt_infos()

def _build_layout_infos(self):
for layout in self._rxkb.layouts.values():
name = layout.name
if layout.variant:
name += ' (' + layout.variant + ')'

langs = []
for lang in layout.iso639_codes:
if iso639.find(iso639_2=lang):
langs.append(iso639.to_name(lang))

if name not in self._layout_infos:
self._layout_infos[name] = LayoutInfo(langs, layout.description)
else:
self._layout_infos[name].langs.extend(langs)

def _build_switch_opt_infos(self):
for group in self._rxkb.option_groups:
Expand Down

0 comments on commit e590a17

Please sign in to comment.