From 5db937b78416b4bcfc945e23f23a2d01224a1524 Mon Sep 17 00:00:00 2001 From: Martin Kolman Date: Thu, 9 Nov 2023 17:52:44 +0100 Subject: [PATCH] Fallback to Wayland if Xorg is not available In environments where Anaconda handles the overall GUI environment setup (eq. netinst/boot.iso) try to use Wayland compositor if Xorg server is not found. Also add support for a (temporary?) boot inst.wayland boot option, that can be used to force Wayland compositor to be used. This makes it possible to check if Waland can be used in the given environment even if the given installation image still contains Xorg. --- anaconda.py | 9 +++ anaconda.spec.in | 5 ++ pyanaconda/display.py | 125 +++++++++++++++++++++++++++++++--- pyanaconda/ui/gui/__init__.py | 2 + 4 files changed, 130 insertions(+), 11 deletions(-) diff --git a/anaconda.py b/anaconda.py index 7bc7d20c4825..1695e3777d27 100755 --- a/anaconda.py +++ b/anaconda.py @@ -172,6 +172,13 @@ def setup_environment(): print("anaconda must be run as root.") sys.exit(1) + # FIXME: try setting XDG_RUNTIME_DIR early + os.environ["XDG_RUNTIME_DIR"] = "/run/user/0" + # FIXME: enable GTK startup debugging + os.environ["G_MESSAGES_DEBUG"] = "all" + os.environ["GDK_DEBUG"] = "all" +# os.environ["GTK_DEBUG"] = "all" + print("Starting installer, one moment...") # Allow a file to be loaded as early as possible @@ -226,6 +233,8 @@ def setup_environment(): if startup_utils.prompt_for_ssh(opts): sys.exit(0) + log.debug("AAA: MAIN PID/PPID %d/%d", os.getpid(), os.getppid()) + log.info("%s %s", sys.argv[0], util.get_anaconda_version_string(build_time_version=True)) log.debug("Image packages list: %s", util.get_image_packages_info()) diff --git a/anaconda.spec.in b/anaconda.spec.in index 0afd56b88044..d73eeecfcba9 100644 --- a/anaconda.spec.in +++ b/anaconda.spec.in @@ -256,6 +256,11 @@ Requires: nm-connection-editor Requires: librsvg2 Requires: gnome-kiosk Requires: brltty +# Wayland support dependecies +# FIXME: conditionalize this ? +Requires: python3-pam +Requires: sssd-client +Requires: xorg-x11-server-Xwayland # dependencies for rpm-ostree payload module Requires: rpm-ostree >= %{rpmostreever} Requires: ostree diff --git a/pyanaconda/display.py b/pyanaconda/display.py index 2b67b9af3c04..7aeccbc86f5e 100644 --- a/pyanaconda/display.py +++ b/pyanaconda/display.py @@ -28,6 +28,7 @@ from pyanaconda.core.configuration.anaconda import conf from pyanaconda.core.process_watchers import WatchProcesses +from pyanaconda.core.kernel import kernel_arguments from pyanaconda import startup_utils from pyanaconda.core import util, constants, hw from pyanaconda import vnc @@ -50,6 +51,10 @@ log = get_module_logger(__name__) stdout_log = get_stdout_logger() +import gi +gi.require_version("Gdk", "3.0") +from gi.repository import Gdk + X_TIMEOUT_ADVICE = \ "Do not load the stage2 image over a slow network link.\n" \ "Wait longer for the X server startup with the inst.xtimeout= boot option." \ @@ -245,6 +250,79 @@ def write_xdriver(driver, root=None): f.close() +# Wayland + +def start_wayland_compositor(): + """Start Wayland compositor for the Anaconda GUI. + + Add XDG_DATA_DIRS to the environment to pull in our overridden schema + files. + + FIXME: Will XDG_DATA_DIRS work with Wayland compositor ? + """ + 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'] + else: + xdg_data_dirs = datadir + '/window-manager:/usr/share' + + def wayland_preexec(): + # to set GUI subprocess SIGINT handler + # FIXME: does this even work with a Wayland compoitor ? + signal.signal(signal.SIGINT, signal.SIG_IGN) + +# childproc = util.startProgram(["gnome-kiosk", "--sm-disable", "--wayland"], +# env_add={ +# 'XDG_DATA_DIRS': xdg_data_dirs, +# 'XDG_RUNTIME_DIR': "/run/user/0"}, +# preexec_fn=wayland_preexec) + + +#"/run-in-new-session.py --user root --service anaconda-session --session-type wayland --session-class user /bin/gnome-kiosk --wayland --sm-disable" + + log.debug("AAA: DISPLAY PID %d %d", os.getpid(), os.getppid()) + + # FIXME: try to force wayland + Gdk.set_allowed_backends("wayland") + + childproc = util.startProgram(["run-in-new-session.py", "--user", "root", + "--service", "anaconda-session", + "--vt", "6", + "--session-type", "wayland", "--session-class", "user", + "gnome-kiosk", "--sm-disable"], + env_add={ + 'XDG_DATA_DIRS': xdg_data_dirs, + 'XDG_RUNTIME_DIR': "/run/user/0"}, + preexec_fn=wayland_preexec) + + #WatchProcesses.watch_process(childproc, "gnome-kiosk") + + WatchProcesses.watch_process(childproc, "run-in-new-session.py") + + +def set_wayland_resolution(runres): + """Set Wayland server screen resolution. + + FIXME: implement this using Mutter DBus API + FIXME: keep runres or use horizontal/vertical resolution instead ? + + :param str runres: a resolution specification string + """ + log.error("FIXME: set screen resolution - not yet implemented on Wayland") + + +def do_extra_wayland_actions(runres, gui_mode): + """Perform Wayland related actions not related to startup. + + :param str runres: a resolution specification string + :param gui_mode: an Anaconda display mode + """ + if runres and gui_mode and not flags.usevnc: + set_wayland_resolution(runres) + + start_user_systemd() + + # general display startup def setup_display(anaconda, options): """Setup the display for the installation environment. @@ -319,16 +397,35 @@ def setup_display(anaconda, options): for error_message in vnc_error_messages: stdout_log.warning(error_message) - # Should we try to start Xorg? - want_x = anaconda.gui_mode and not (flags.preexisting_x11 or flags.usevnc) - - # Is Xorg is actually available? - if want_x and not os.access("/usr/bin/Xorg", os.X_OK): - stdout_log.warning(_("Graphical installation is not available. " - "Starting text mode.")) - time.sleep(2) - anaconda.display_mode = constants.DisplayModes.TUI - want_x = False + # Xorg or Wayland? + want_x = False + want_wayland = False + + # try to run in local GUI mode + if anaconda.gui_mode and not (flags.preexisting_x11 or flags.usevnc): + # is Xorg actually available? + xorg_server_available = os.access("/usr/bin/Xorg", os.X_OK) + + # is our Wayland compositor available ? + wayland_compositor_available = os.access("/usr/bin/gnome-kiosk", os.X_OK) + + # do we have at least one of the needed deps we need to run in graphical mode ? + if xorg_server_available: + # check for Wayland override + if "wayland" in kernel_arguments and wayland_compositor_available: + # this way we can test if insaller on Wayland works on a given system + want_wayland = True + else: + # no Wayland override or no Wayland, just use X + want_x = True + elif wayland_compositor_available: + want_wayland = True + else: + # neither X or Wayland is available + anaconda.display_mode = constants.DisplayModes.TUI + stdout_log.warning(_("Graphical installation is not available. " + "Starting text mode.")) + time.sleep(2) if anaconda.tui_mode and flags.vncquestion: # we prefer vnc over text mode, so ask about that @@ -346,7 +443,13 @@ def setup_display(anaconda, options): # check_memory may have changed the display mode want_x = want_x and (anaconda.gui_mode) - if want_x: + want_wayland = want_wayland and (anaconda.gui_mode) + + if want_wayland: + log.debug("Using Wayland compositor to provide locally running graphical interface.") + start_wayland_compositor() + elif want_x: + log.debug("Using Xorg server to provide locally running graphical interface.") try: start_x11(xtimeout) do_startup_x11_actions() diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index 7cddde299002..3a312e566ff1 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -288,6 +288,7 @@ def __init__(self, fullscreen=False, decorated=False): :param bool fullscreen: if True, fullscreen the window, if false maximize """ super().__init__() + # Keep the latest main window. self.__class__.__instance = self @@ -705,6 +706,7 @@ def _instantiateAction(self, actionClass): return obj def run(self): + log.debug("AAA: GTK PID/PPID %d/%d", os.getpid(), os.getppid()) (success, _args) = Gtk.init_check(None) if not success: raise RuntimeError("Failed to initialize Gtk")