diff --git a/subiquity/client/client.py b/subiquity/client/client.py index ed981b18..07df3c4f 100644 --- a/subiquity/client/client.py +++ b/subiquity/client/client.py @@ -93,6 +93,7 @@ class SubiquityClient(TuiApplication): return SubiquityUI(self, self.help_menu) controllers = [ + "Serial", "Welcome", "Refresh", "Keyboard", diff --git a/subiquity/client/controllers/__init__.py b/subiquity/client/controllers/__init__.py index fca8d162..66f0be3c 100644 --- a/subiquity/client/controllers/__init__.py +++ b/subiquity/client/controllers/__init__.py @@ -22,11 +22,13 @@ from .network import NetworkController from .progress import ProgressController from .proxy import ProxyController from .refresh import RefreshController +from .serial import SerialController from .snaplist import SnapListController from .ssh import SSHController from .welcome import WelcomeController from .zdev import ZdevController +# see SubiquityClient.controllers for another list __all__ = [ 'FilesystemController', 'IdentityController', @@ -37,6 +39,7 @@ __all__ = [ 'ProxyController', 'RefreshController', 'RepeatedController', + 'SerialController', 'SnapListController', 'SSHController', 'WelcomeController', diff --git a/subiquity/client/controllers/serial.py b/subiquity/client/controllers/serial.py new file mode 100644 index 00000000..571e6039 --- /dev/null +++ b/subiquity/client/controllers/serial.py @@ -0,0 +1,39 @@ +# Copyright 2021 Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import logging + +from subiquity.client.controller import SubiquityTuiController +from subiquity.ui.views.serial import SerialView +from subiquitycore.tuicontroller import Skip + +log = logging.getLogger('subiquity.client.controllers.serial') + + +class SerialController(SubiquityTuiController): + async def make_ui(self): + if not self.app.opts.run_on_serial: + raise Skip() + ssh_info = await self.app.client.meta.ssh_info.GET() + return SerialView(self, ssh_info) + + def done(self, rich): + log.debug("SerialController.done rich %s next_screen", rich) + self.app.set_rich(rich) + self.app.next_screen() + + def cancel(self): + # Can't go back from here! + pass diff --git a/subiquity/client/controllers/welcome.py b/subiquity/client/controllers/welcome.py index 7b4ec645..ff600174 100644 --- a/subiquity/client/controllers/welcome.py +++ b/subiquity/client/controllers/welcome.py @@ -16,6 +16,7 @@ import logging from subiquitycore import i18n +from subiquitycore.tuicontroller import Skip from subiquity.client.controller import SubiquityTuiController from subiquity.ui.views.welcome import WelcomeView @@ -27,14 +28,12 @@ class WelcomeController(SubiquityTuiController): endpoint_name = 'locale' async def make_ui(self): + if not self.app.rich_mode: + raise Skip() language = await self.endpoint.GET() i18n.switch_language(language) - serial = self.app.opts.run_on_serial - if serial: - ssh_info = await self.app.client.meta.ssh_info.GET() - else: - ssh_info = None - return WelcomeView(self, language, serial, ssh_info) + self.serial = self.app.opts.run_on_serial + return WelcomeView(self, language, self.serial) def run_answers(self): if 'lang' in self.answers: @@ -45,6 +44,8 @@ class WelcomeController(SubiquityTuiController): i18n.switch_language(code) self.app.next_screen(self.endpoint.POST(code)) - def cancel(self): - # Can't go back from here! - pass + def cancel(self, sender=None): + if not self.serial: + # Can't go back from here unless we're on serial! + pass + self.app.prev_screen() diff --git a/subiquity/server/controllers/locale.py b/subiquity/server/controllers/locale.py index 8f019e75..c994dc16 100644 --- a/subiquity/server/controllers/locale.py +++ b/subiquity/server/controllers/locale.py @@ -31,9 +31,6 @@ class LocaleController(SubiquityController): autoinstall_schema = {'type': 'string'} autoinstall_default = 'en_US.UTF-8' - def interactive(self): - return self.app.interactive - def load_autoinstall_data(self, data): os.environ["LANG"] = data diff --git a/subiquity/ui/views/serial.py b/subiquity/ui/views/serial.py new file mode 100644 index 00000000..3ae73a6d --- /dev/null +++ b/subiquity/ui/views/serial.py @@ -0,0 +1,89 @@ +# Copyright 2021 Canonical, Ltd. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +"""Welcome + +Welcome provides user with language selection +""" + +import logging + +from urwid import Text + +from subiquitycore.ui.buttons import forward_btn, other_btn +from subiquitycore.ui.utils import rewrap, screen +from subiquitycore.view import BaseView + +log = logging.getLogger("subiquity.views.serial") + + +SERIAL_TEXT = """ + +As the installer is running on a serial console, it has started in +basic mode, using only the ASCII character set and black and white +colours. + +If you are connecting from a terminal emulator such as gnome-terminal +that supports unicode and rich colours you can switch to "rich mode" +which uses unicode, colours and supports many languages. + +""" + +SSH_TEXT = """ +You can also connect to the installer over the network via SSH, which +will allow use of rich mode. +""" + + +class SerialView(BaseView): + title = "Serial" + + def __init__(self, controller, ssh_info): + self.controller = controller + self.ssh_info = ssh_info + super().__init__(self.make_serial()) + + def make_serial(self): + btns = [ + forward_btn( + label="Continue in rich mode", + on_press=self.rich_mode), + forward_btn( + label="Continue in basic mode", + on_press=self.basic_mode), + ] + widgets = [ + Text(""), + Text(rewrap(SERIAL_TEXT)), + Text(""), + ] + if self.ssh_info: + widgets.append(Text(rewrap(SSH_TEXT))) + widgets.append(Text("")) + btns.append(other_btn( + label="View SSH instructions", + on_press=self.ssh_help)) + return screen(widgets, btns) + + def rich_mode(self, sender): + self.controller.done(True) + + def basic_mode(self, sender): + self.controller.done(False) + + def ssh_help(self, sender): + menu = self.controller.app.help_menu + menu.ssh_info = self.ssh_info + menu.ssh_help() diff --git a/subiquity/ui/views/tests/test_welcome.py b/subiquity/ui/views/tests/test_welcome.py index 0bb83f03..091b81ff 100644 --- a/subiquity/ui/views/tests/test_welcome.py +++ b/subiquity/ui/views/tests/test_welcome.py @@ -14,7 +14,7 @@ class WelcomeViewTests(unittest.TestCase): controller = mock.create_autospec(spec=WelcomeController) with mock.patch("subiquity.ui.views.welcome.get_languages") as p: p.return_value = languages - return WelcomeView(controller, languages[0][0], False, None) + return WelcomeView(controller, languages[0][0], False) def test_basic(self): # Clicking the button for a language calls "switch_language" diff --git a/subiquity/ui/views/welcome.py b/subiquity/ui/views/welcome.py index 4b68dc56..8680c000 100644 --- a/subiquity/ui/views/welcome.py +++ b/subiquity/ui/views/welcome.py @@ -38,23 +38,6 @@ Select the language to use for the installer and to be configured in the installed system. """) -SERIAL_TEXT = """ - -As the installer is running on a serial console, it has started in -basic mode, using only the ASCII character set and black and white -colours. - -If you are connecting from a terminal emulator such as gnome-terminal -that supports unicode and rich colours you can switch to "rich mode" -which uses unicode, colours and supports many languages. - -""" - -SSH_TEXT = """ -You can also connect to the installer over the network via SSH, which -will allow use of rich mode. -""" - CLOUD_INIT_FAIL_TEXT = """ cloud-init failed to complete after 10 minutes of waiting. This suggests a bug, which we would appreciate help understanding. If you @@ -81,15 +64,11 @@ def get_languages(): class WelcomeView(BaseView): title = "Willkommen! Bienvenue! Welcome! Добро пожаловать! Welkom!" - def __init__(self, controller, cur_lang, serial, ssh_info): + def __init__(self, controller, cur_lang, serial): self.controller = controller self.cur_lang = cur_lang - if serial and not controller.app.rich_mode: - s = self.make_serial_choices(ssh_info) - self.title = "Welcome!" - else: - s = self.make_language_choices() - super().__init__(s) + self.serial = serial + super().__init__(self.make_language_choices()) def make_language_choices(self): btns = [] @@ -109,51 +88,16 @@ class WelcomeView(BaseView): user_arg=code)) lb = ListBox(btns) + back = None + if self.serial: + back = other_btn(_("Back"), on_press=self.controller.cancel) if current_index is not None: lb.base_widget.focus_position = current_index return screen( - lb, buttons=None, narrow_rows=True, + lb, focus_buttons=False, narrow_rows=True, + buttons=[back] if back else None, excerpt=_("Use UP, DOWN and ENTER keys to select your language.")) - def make_serial_choices(self, ssh_info): - btns = [ - other_btn( - label="Switch to rich mode", - on_press=self.enable_rich), - forward_btn( - label="Continue in basic mode", - on_press=self.choose_language, - user_arg='C'), - ] - widgets = [ - Text(""), - Text(rewrap(SERIAL_TEXT)), - Text(""), - ] - if ssh_info: - widgets.append(Text(rewrap(SSH_TEXT))) - widgets.append(Text("")) - btns.insert(1, other_btn( - label="View SSH instructions", - on_press=self.ssh_help, - user_arg=ssh_info)) - widgets.extend([ - button_pile(btns), - ]) - lb = ListBox(widgets) - return screen(lb, buttons=None) - - def enable_rich(self, sender): - self.controller.app.toggle_rich() - self.title = self.__class__.title - self.controller.ui.set_header(self.title) - self._w = self.make_language_choices() - - def ssh_help(self, sender, ssh_info): - menu = self.controller.app.help_menu - menu.ssh_info = ssh_info - menu.ssh_help() - def choose_language(self, sender, code): log.debug('WelcomeView %s', code) self.controller.done(code) diff --git a/subiquitycore/tui.py b/subiquitycore/tui.py index 2136d7d0..d21627ce 100644 --- a/subiquitycore/tui.py +++ b/subiquitycore/tui.py @@ -312,6 +312,11 @@ class TuiApplication(Application): self.aio_loop.call_later(0.06, _run_script) + def set_rich(self, rich): + if rich == self.rich_mode: + return + self.toggle_rich() + def toggle_rich(self): if self.rich_mode: urwid.util.set_encoding('ascii')