diff -Nru ubiquity-22.04.14/debian/changelog ubiquity-22.04.15/debian/changelog --- ubiquity-22.04.14/debian/changelog 2022-04-13 12:59:45.000000000 +0000 +++ ubiquity-22.04.15/debian/changelog 2022-04-13 22:56:30.000000000 +0000 @@ -1,3 +1,9 @@ +ubiquity (22.04.15) jammy; urgency=medium + + * Set GNOME keyboard layout during installation (LP: #1875062) + + -- Benjamin Drung Wed, 13 Apr 2022 15:56:30 -0700 + ubiquity (22.04.14) jammy; urgency=medium * Updated translations. diff -Nru ubiquity-22.04.14/tests/test_ubi_console_setup.py ubiquity-22.04.15/tests/test_ubi_console_setup.py --- ubiquity-22.04.14/tests/test_ubi_console_setup.py 1970-01-01 00:00:00.000000000 +0000 +++ ubiquity-22.04.15/tests/test_ubi_console_setup.py 2022-04-13 17:34:07.000000000 +0000 @@ -0,0 +1,102 @@ +#!/usr/bin/python3 + +import os +import unittest +import unittest.mock +from test.support import run_unittest + +import debconf + +from ubiquity import plugin_manager + +ubi_console_setup = plugin_manager.load_plugin("ubi-console-setup") + + +@unittest.skipUnless("DEBCONF_SYSTEMRC" in os.environ, "Need a database.") +class TestPageBase(unittest.TestCase): + def setUp(self): + # We could mock out the db for this, but we ultimately want to make + # sure that the debconf questions it's getting exist. + self.page = ubi_console_setup.Page(None, ui=unittest.mock.Mock()) + self.page.db = debconf.DebconfCommunicator("ubi-test", cloexec=True) + self.addCleanup(self.page.db.shutdown) + + +class TestPage(TestPageBase): + DEFAULT_ENGLISH_KEYBOARDS = [ + ("xkb", "us"), + ("xkb", "au"), + ("xkb", "cm"), + ("xkb", "gb"), + ] + + @unittest.mock.patch("ubi-console-setup.gsettings.set_list") + def test_set_gnome_keyboard_layout_with_variant(self, set_list_mock): + self.page.gnome_input_sources = self.DEFAULT_ENGLISH_KEYBOARDS + self.page.set_gnome_keyboard_layout("de", "neo") + set_list_mock.assert_has_calls( + [ + unittest.mock.call( + "org.gnome.desktop.input-sources", + "sources", + [("xkb", "de+neo")], + ), + unittest.mock.call( + "org.gnome.desktop.input-sources", + "sources", + [("xkb", "de+neo")] + self.DEFAULT_ENGLISH_KEYBOARDS, + ), + ] + ) + + @unittest.mock.patch("ubi-console-setup.gsettings.set_list") + def test_set_gnome_keyboard_layout_without_variant(self, set_list_mock): + self.page.gnome_input_sources = self.DEFAULT_ENGLISH_KEYBOARDS + self.page.set_gnome_keyboard_layout("de", "") + set_list_mock.assert_has_calls( + [ + unittest.mock.call( + "org.gnome.desktop.input-sources", + "sources", + [("xkb", "de")], + ), + unittest.mock.call( + "org.gnome.desktop.input-sources", + "sources", + [("xkb", "de")] + self.DEFAULT_ENGLISH_KEYBOARDS, + ), + ] + ) + + @unittest.mock.patch("ubi-console-setup.gsettings.set_list") + def test_set_gnome_keyboard_layout_to_existing_layout(self, set_list_mock): + self.page.gnome_input_sources = self.DEFAULT_ENGLISH_KEYBOARDS + self.page.set_gnome_keyboard_layout("gb", "") + set_list_mock.assert_has_calls( + [ + unittest.mock.call( + "org.gnome.desktop.input-sources", + "sources", + [("xkb", "gb")], + ), + unittest.mock.call( + "org.gnome.desktop.input-sources", + "sources", + self.DEFAULT_ENGLISH_KEYBOARDS, + ), + ] + ) + + @unittest.mock.patch("ubi-console-setup.gsettings.set_list") + def test_set_gnome_keyboard_layout_with_empty_input_sources( + self, set_list_mock + ): + self.page.gnome_input_sources = None + self.page.set_gnome_keyboard_layout("us", "") + set_list_mock.assert_called_once_with( + "org.gnome.desktop.input-sources", "sources", [("xkb", "us")] + ) + + +if __name__ == "__main__": + run_unittest(TestPage) # pragma: no cover diff -Nru ubiquity-22.04.14/ubiquity/plugins/ubi-console-setup.py ubiquity-22.04.15/ubiquity/plugins/ubi-console-setup.py --- ubiquity-22.04.14/ubiquity/plugins/ubi-console-setup.py 2022-04-13 12:21:26.000000000 +0000 +++ ubiquity-22.04.15/ubiquity/plugins/ubi-console-setup.py 2022-04-13 17:34:07.000000000 +0000 @@ -21,7 +21,7 @@ import os import re -from ubiquity import keyboard_names, misc, osextras, plugin +from ubiquity import gsettings, keyboard_names, misc, osextras, plugin NAME = 'console_setup' @@ -455,6 +455,8 @@ self.has_variants = False + self.gnome_input_sources = self.get_gnome_input_sources() + # Technically we should provide a version as the second argument, # but that isn't currently needed and it would require querying # apt/dpkg for the current version, which would be slow, so we don't @@ -696,10 +698,42 @@ (model, layout, variant, options) = \ self.adjust_keyboard(model, layout, variant, []) + self.set_gnome_keyboard_layout(layout, variant) self.debug("Setting keyboard layout: %s %s %s %s" % (model, layout, variant, options)) self.apply_real_keyboard(model, layout, variant, options) + @staticmethod + def get_gnome_input_sources(): + return gsettings.get_list("org.gnome.desktop.input-sources", "sources") + + def set_gnome_input_sources(self, input_sources): + self.debug( + "Setting org.gnome.desktop.input-sources sources to %r", + input_sources, + ) + gsettings.set_list( + "org.gnome.desktop.input-sources", "sources", input_sources + ) + + def set_gnome_keyboard_layout(self, layout, variant): + if variant: + input_source = ("xkb", f"{layout}+{variant}") + else: + input_source = ("xkb", f"{layout}") + + # The only reliable way to change the keyboard layout in GNOME is to + # set the input sources to exacly the desired layout. See also + # https://discourse.gnome.org/t/how-to-set-gnome-keyboard-layout-programatically/9459 + self.set_gnome_input_sources([input_source]) + if not self.gnome_input_sources: + return + if input_source in self.gnome_input_sources: + input_sources = self.gnome_input_sources + else: + input_sources = [input_source] + self.gnome_input_sources + self.set_gnome_input_sources(input_sources) + def apply_real_keyboard(self, model, layout, variant, options): args = [] if model is not None and model != '': @@ -812,6 +846,7 @@ options_list = options.split(',') else: options_list = [] + self.set_gnome_keyboard_layout(layout, variant) self.apply_real_keyboard(model, layout, variant, options_list) plugin.Plugin.cleanup(self)