From 50e230c417c3925725c022e64d2907157918af8e Mon Sep 17 00:00:00 2001 From: OPNA2608 Date: Wed, 30 Oct 2024 14:08:21 +0100 Subject: [PATCH 1/5] nixos/lomiri: nixfmt --- .../services/desktop-managers/lomiri.nix | 149 ++++++++++-------- 1 file changed, 86 insertions(+), 63 deletions(-) diff --git a/nixos/modules/services/desktop-managers/lomiri.nix b/nixos/modules/services/desktop-managers/lomiri.nix index 75834eab0cd8..39c295dc1cb8 100644 --- a/nixos/modules/services/desktop-managers/lomiri.nix +++ b/nixos/modules/services/desktop-managers/lomiri.nix @@ -1,8 +1,14 @@ -{ config, pkgs, lib, ... }: +{ + config, + pkgs, + lib, + ... +}: let cfg = config.services.desktopManager.lomiri; -in { +in +{ options.services.desktopManager.lomiri = { enable = lib.mkEnableOption '' the Lomiri graphical shell (formerly known as Unity8) @@ -11,42 +17,46 @@ in { config = lib.mkIf cfg.enable { environment = { - systemPackages = (with pkgs; [ - glib # XDG MIME-related tools identify it as GNOME, add gio for MIME identification to work - libayatana-common - ubports-click - ]) ++ (with pkgs.lomiri; [ - hfd-service - history-service - libusermetrics - lomiri - lomiri-calculator-app - lomiri-camera-app - lomiri-clock-app - lomiri-content-hub - lomiri-docviewer-app - lomiri-download-manager - lomiri-filemanager-app - lomiri-gallery-app - lomiri-polkit-agent - lomiri-schemas # exposes some required dbus interfaces - lomiri-session # wrappers to properly launch the session - lomiri-sounds - lomiri-system-settings - lomiri-terminal-app - lomiri-thumbnailer - lomiri-url-dispatcher - lomiri-wallpapers - mediascanner2 # TODO possibly needs to be kicked off by graphical-session.target - morph-browser - qtmir # not having its desktop file for Xwayland available causes any X11 application to crash the session - suru-icon-theme - telephony-service - teleports - ]); + systemPackages = + (with pkgs; [ + glib # XDG MIME-related tools identify it as GNOME, add gio for MIME identification to work + libayatana-common + ubports-click + ]) + ++ (with pkgs.lomiri; [ + hfd-service + history-service + libusermetrics + lomiri + lomiri-calculator-app + lomiri-camera-app + lomiri-clock-app + lomiri-content-hub + lomiri-docviewer-app + lomiri-download-manager + lomiri-filemanager-app + lomiri-gallery-app + lomiri-polkit-agent + lomiri-schemas # exposes some required dbus interfaces + lomiri-session # wrappers to properly launch the session + lomiri-sounds + lomiri-system-settings + lomiri-terminal-app + lomiri-thumbnailer + lomiri-url-dispatcher + lomiri-wallpapers + mediascanner2 # TODO possibly needs to be kicked off by graphical-session.target + morph-browser + qtmir # not having its desktop file for Xwayland available causes any X11 application to crash the session + suru-icon-theme + telephony-service + teleports + ]); # To override the default keyboard layout in Lomiri - etc.${pkgs.lomiri.lomiri.passthru.etcLayoutsFile}.text = lib.strings.replaceStrings [","] ["\n"] config.services.xserver.xkb.layout; + etc.${pkgs.lomiri.lomiri.passthru.etcLayoutsFile}.text = lib.strings.replaceStrings [ "," ] [ + "\n" + ] config.services.xserver.xkb.layout; }; hardware = { @@ -84,21 +94,26 @@ in { services.ayatana-indicators = { enable = true; - packages = (with pkgs; [ - ayatana-indicator-datetime - ayatana-indicator-display - ayatana-indicator-messages - ayatana-indicator-power - ayatana-indicator-session - ] ++ lib.optionals config.hardware.bluetooth.enable [ - ayatana-indicator-bluetooth - ] ++ lib.optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [ - ayatana-indicator-sound - ]) ++ (with pkgs.lomiri; [ - telephony-service - ] ++ lib.optionals config.networking.networkmanager.enable [ - lomiri-indicator-network - ]); + packages = + ( + with pkgs; + [ + ayatana-indicator-datetime + ayatana-indicator-display + ayatana-indicator-messages + ayatana-indicator-power + ayatana-indicator-session + ] + ++ lib.optionals config.hardware.bluetooth.enable [ ayatana-indicator-bluetooth ] + ++ lib.optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [ + ayatana-indicator-sound + ] + ) + ++ ( + with pkgs.lomiri; + [ telephony-service ] + ++ lib.optionals config.networking.networkmanager.enable [ lomiri-indicator-network ] + ); }; services.udisks2.enable = true; @@ -159,7 +174,13 @@ in { "lomiri-polkit-agent" = rec { description = "Lomiri Polkit agent"; - wantedBy = [ "lomiri.service" "lomiri-full-greeter.service" "lomiri-full-shell.service" "lomiri-greeter.service" "lomiri-shell.service" ]; + wantedBy = [ + "lomiri.service" + "lomiri-full-greeter.service" + "lomiri-full-shell.service" + "lomiri-greeter.service" + "lomiri-shell.service" + ]; after = [ "graphical-session.target" ]; partOf = wantedBy; serviceConfig = { @@ -172,17 +193,19 @@ in { systemd.services = { "dbus-com.lomiri.UserMetrics" = { - serviceConfig = { - Type = "dbus"; - BusName = "com.lomiri.UserMetrics"; - User = "usermetrics"; - StandardOutput = "syslog"; - SyslogIdentifier = "com.lomiri.UserMetrics"; - ExecStart = "${pkgs.lomiri.libusermetrics}/libexec/libusermetrics/usermetricsservice"; - } // lib.optionalAttrs (!config.security.apparmor.enable) { - # Due to https://gitlab.com/ubports/development/core/libusermetrics/-/issues/8, auth must be disabled when not using AppArmor, lest the next database usage breaks - Environment = "USERMETRICS_NO_AUTH=1"; - }; + serviceConfig = + { + Type = "dbus"; + BusName = "com.lomiri.UserMetrics"; + User = "usermetrics"; + StandardOutput = "syslog"; + SyslogIdentifier = "com.lomiri.UserMetrics"; + ExecStart = "${pkgs.lomiri.libusermetrics}/libexec/libusermetrics/usermetricsservice"; + } + // lib.optionalAttrs (!config.security.apparmor.enable) { + # Due to https://gitlab.com/ubports/development/core/libusermetrics/-/issues/8, auth must be disabled when not using AppArmor, lest the next database usage breaks + Environment = "USERMETRICS_NO_AUTH=1"; + }; }; }; From 0b145d934850ec5e39f3fd911a2e5fa5f64b1994 Mon Sep 17 00:00:00 2001 From: OPNA2608 Date: Wed, 30 Oct 2024 14:09:15 +0100 Subject: [PATCH 2/5] nixos/lomiri: Add internal basics option for shared shell/greeter things --- .../services/desktop-managers/lomiri.nix | 389 ++++++++++-------- 1 file changed, 212 insertions(+), 177 deletions(-) diff --git a/nixos/modules/services/desktop-managers/lomiri.nix b/nixos/modules/services/desktop-managers/lomiri.nix index 39c295dc1cb8..c8faa352a644 100644 --- a/nixos/modules/services/desktop-managers/lomiri.nix +++ b/nixos/modules/services/desktop-managers/lomiri.nix @@ -13,211 +13,246 @@ in enable = lib.mkEnableOption '' the Lomiri graphical shell (formerly known as Unity8) ''; + + basics = lib.mkOption { + internal = true; + description = '' + Enable basic things for getting Lomiri working. + ''; + type = lib.types.bool; + default = config.services.xserver.displayManager.lightdm.greeters.lomiri.enable || cfg.enable; + }; }; - config = lib.mkIf cfg.enable { - environment = { - systemPackages = - (with pkgs; [ - glib # XDG MIME-related tools identify it as GNOME, add gio for MIME identification to work - libayatana-common - ubports-click - ]) - ++ (with pkgs.lomiri; [ - hfd-service - history-service - libusermetrics - lomiri - lomiri-calculator-app - lomiri-camera-app - lomiri-clock-app - lomiri-content-hub - lomiri-docviewer-app - lomiri-download-manager - lomiri-filemanager-app - lomiri-gallery-app - lomiri-polkit-agent - lomiri-schemas # exposes some required dbus interfaces - lomiri-session # wrappers to properly launch the session - lomiri-sounds - lomiri-system-settings - lomiri-terminal-app - lomiri-thumbnailer - lomiri-url-dispatcher - lomiri-wallpapers - mediascanner2 # TODO possibly needs to be kicked off by graphical-session.target - morph-browser - qtmir # not having its desktop file for Xwayland available causes any X11 application to crash the session - suru-icon-theme - telephony-service - teleports - ]); + config = lib.mkMerge [ + # Basics for getting Lomiri to work + (lib.mkIf cfg.basics { + environment = { + # To override the default keyboard layout in Lomiri + etc.${pkgs.lomiri.lomiri.passthru.etcLayoutsFile}.text = lib.strings.replaceStrings [ "," ] [ + "\n" + ] config.services.xserver.xkb.layout; - # To override the default keyboard layout in Lomiri - etc.${pkgs.lomiri.lomiri.passthru.etcLayoutsFile}.text = lib.strings.replaceStrings [ "," ] [ - "\n" - ] config.services.xserver.xkb.layout; - }; + pathsToLink = [ + # Data + "/share/locale" # TODO LUITK hardcoded default locale path, fix individual apps to not rely on it + "/share/wallpapers" + ]; - hardware = { - bluetooth.enable = lib.mkDefault true; - }; + systemPackages = with pkgs.lomiri; [ + lomiri-wallpapers # default + additional wallpaper + suru-icon-theme # basic indicator icons + ]; + }; - networking.networkmanager.enable = lib.mkDefault true; + fonts.packages = with pkgs; [ + ubuntu-classic # Ubuntu is default font + ]; - systemd.packages = with pkgs.lomiri; [ - hfd-service - lomiri-download-manager - ]; + # Xwayland is partly hardcoded in Mir so it can't really be fully turned off, and it must be on PATH for X11 apps *and Lomiri's web browser* to work. + # Until Mir/Lomiri can be properly used without it, force it on so everything behaves as expected. + programs.xwayland.enable = lib.mkForce true; - services.dbus.packages = with pkgs.lomiri; [ - hfd-service - libusermetrics - lomiri-download-manager - ]; - - fonts.packages = with pkgs; [ - # Applications tend to default to Ubuntu font - ubuntu-classic - ]; - - # Copy-pasted basic stuff - hardware.graphics.enable = lib.mkDefault true; - fonts.enableDefaultPackages = lib.mkDefault true; - programs.dconf.enable = lib.mkDefault true; - - # Xwayland is partly hardcoded in Mir so it can't really be fully turned off, and it must be on PATH for X11 apps *and Lomiri's web browser* to work. - # Until Mir/Lomiri can be properly used without it, force it on so everything behaves as expected. - programs.xwayland.enable = lib.mkForce true; - - services.accounts-daemon.enable = true; - - services.ayatana-indicators = { - enable = true; - packages = - ( + services.ayatana-indicators = { + enable = true; + packages = ( with pkgs; [ - ayatana-indicator-datetime - ayatana-indicator-display - ayatana-indicator-messages - ayatana-indicator-power - ayatana-indicator-session + ayatana-indicator-datetime # Clock + ayatana-indicator-session # Controls for shutting down etc ] - ++ lib.optionals config.hardware.bluetooth.enable [ ayatana-indicator-bluetooth ] - ++ lib.optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [ - ayatana-indicator-sound - ] - ) - ++ ( - with pkgs.lomiri; - [ telephony-service ] - ++ lib.optionals config.networking.networkmanager.enable [ lomiri-indicator-network ] ); - }; + }; + }) - services.udisks2.enable = true; - services.upower.enable = true; - services.geoclue2.enable = true; + # Full Lomiri DE + (lib.mkIf cfg.enable { + # We need the basic setup as well + services.desktopManager.lomiri.basics = true; - services.gnome.evolution-data-server = { - enable = true; - plugins = with pkgs; [ - # TODO: lomiri.address-book-service + environment = { + systemPackages = + (with pkgs; [ + glib # XDG MIME-related tools identify it as GNOME, add gio for MIME identification to work + libayatana-common + ubports-click + ]) + ++ (with pkgs.lomiri; [ + hfd-service + history-service + libusermetrics + lomiri + lomiri-calculator-app + lomiri-camera-app + lomiri-clock-app + lomiri-content-hub + lomiri-docviewer-app + lomiri-download-manager + lomiri-filemanager-app + lomiri-gallery-app + lomiri-polkit-agent + lomiri-schemas # exposes some required dbus interfaces + lomiri-session # wrappers to properly launch the session + lomiri-sounds + lomiri-system-settings + lomiri-terminal-app + lomiri-thumbnailer + lomiri-url-dispatcher + mediascanner2 # TODO possibly needs to be kicked off by graphical-session.target + morph-browser + qtmir # not having its desktop file for Xwayland available causes any X11 application to crash the session + telephony-service + teleports + ]); + }; + + hardware = { + bluetooth.enable = lib.mkDefault true; + }; + + networking.networkmanager.enable = lib.mkDefault true; + + systemd.packages = with pkgs.lomiri; [ + hfd-service + lomiri-download-manager ]; - }; - services.telepathy.enable = true; + services.dbus.packages = with pkgs.lomiri; [ + hfd-service + libusermetrics + lomiri-download-manager + ]; - services.displayManager = { - defaultSession = lib.mkDefault "lomiri"; - sessionPackages = with pkgs.lomiri; [ lomiri-session ]; - }; + # Copy-pasted basic stuff + hardware.graphics.enable = lib.mkDefault true; + fonts.enableDefaultPackages = lib.mkDefault true; + programs.dconf.enable = lib.mkDefault true; - services.xserver = { - enable = lib.mkDefault true; - displayManager.lightdm = { - enable = lib.mkDefault true; - greeters.lomiri.enable = lib.mkDefault true; - }; - }; + services.accounts-daemon.enable = true; - environment.pathsToLink = [ - # Configs for inter-app data exchange system - "/share/lomiri-content-hub/peers" - # Configs for inter-app URL requests - "/share/lomiri-url-dispatcher/urls" - # Splash screens & other images for desktop apps launched via lomiri-app-launch - "/share/lomiri-app-launch" - # TODO Try to get maliit stuff working - "/share/maliit/plugins" - # At least the network indicator is still under the unity name, due to leftover Unity-isms - "/share/unity" - # Data - "/share/locale" # TODO LUITK hardcoded default locale path, fix individual apps to not rely on it - "/share/sounds" - "/share/wallpapers" - ]; - - systemd.user.services = { - # Unconditionally run service that collects system-installed URL handlers before LUD - # TODO also run user-installed one? - "lomiri-url-dispatcher-update-system-dir" = { - description = "Lomiri URL dispatcher system directory updater"; - wantedBy = [ "lomiri-url-dispatcher.service" ]; - before = [ "lomiri-url-dispatcher.service" ]; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.lomiri.lomiri-url-dispatcher}/libexec/lomiri-url-dispatcher/lomiri-update-directory /run/current-system/sw/share/lomiri-url-dispatcher/urls/"; - }; + services.ayatana-indicators = { + enable = true; + packages = + ( + with pkgs; + [ + ayatana-indicator-display + ayatana-indicator-messages + ayatana-indicator-power + ] + ++ lib.optionals config.hardware.bluetooth.enable [ ayatana-indicator-bluetooth ] + ++ lib.optionals (config.hardware.pulseaudio.enable || config.services.pipewire.pulse.enable) [ + ayatana-indicator-sound + ] + ) + ++ ( + with pkgs.lomiri; + [ telephony-service ] + ++ lib.optionals config.networking.networkmanager.enable [ lomiri-indicator-network ] + ); }; - "lomiri-polkit-agent" = rec { - description = "Lomiri Polkit agent"; - wantedBy = [ - "lomiri.service" - "lomiri-full-greeter.service" - "lomiri-full-shell.service" - "lomiri-greeter.service" - "lomiri-shell.service" + services.udisks2.enable = true; + services.upower.enable = true; + services.geoclue2.enable = true; + + services.gnome.evolution-data-server = { + enable = true; + plugins = with pkgs; [ + # TODO: lomiri.address-book-service ]; - after = [ "graphical-session.target" ]; - partOf = wantedBy; - serviceConfig = { - Type = "simple"; - Restart = "always"; - ExecStart = "${pkgs.lomiri.lomiri-polkit-agent}/libexec/lomiri-polkit-agent/policykit-agent"; + }; + + services.telepathy.enable = true; + + services.displayManager = { + defaultSession = lib.mkDefault "lomiri"; + sessionPackages = with pkgs.lomiri; [ lomiri-session ]; + }; + + services.xserver = { + enable = lib.mkDefault true; + displayManager.lightdm = { + enable = lib.mkDefault true; + greeters.lomiri.enable = lib.mkDefault true; }; }; - }; - systemd.services = { - "dbus-com.lomiri.UserMetrics" = { - serviceConfig = - { - Type = "dbus"; - BusName = "com.lomiri.UserMetrics"; - User = "usermetrics"; - StandardOutput = "syslog"; - SyslogIdentifier = "com.lomiri.UserMetrics"; - ExecStart = "${pkgs.lomiri.libusermetrics}/libexec/libusermetrics/usermetricsservice"; - } - // lib.optionalAttrs (!config.security.apparmor.enable) { - # Due to https://gitlab.com/ubports/development/core/libusermetrics/-/issues/8, auth must be disabled when not using AppArmor, lest the next database usage breaks - Environment = "USERMETRICS_NO_AUTH=1"; + environment.pathsToLink = [ + # Configs for inter-app data exchange system + "/share/lomiri-content-hub/peers" + # Configs for inter-app URL requests + "/share/lomiri-url-dispatcher/urls" + # Splash screens & other images for desktop apps launched via lomiri-app-launch + "/share/lomiri-app-launch" + # TODO Try to get maliit stuff working + "/share/maliit/plugins" + # At least the network indicator is still under the unity name, due to leftover Unity-isms + "/share/unity" + # Data + "/share/sounds" + ]; + + systemd.user.services = { + # Unconditionally run service that collects system-installed URL handlers before LUD + # TODO also run user-installed one? + "lomiri-url-dispatcher-update-system-dir" = { + description = "Lomiri URL dispatcher system directory updater"; + wantedBy = [ "lomiri-url-dispatcher.service" ]; + before = [ "lomiri-url-dispatcher.service" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.lomiri.lomiri-url-dispatcher}/libexec/lomiri-url-dispatcher/lomiri-update-directory /run/current-system/sw/share/lomiri-url-dispatcher/urls/"; }; + }; + + "lomiri-polkit-agent" = rec { + description = "Lomiri Polkit agent"; + wantedBy = [ + "lomiri.service" + "lomiri-full-greeter.service" + "lomiri-full-shell.service" + "lomiri-greeter.service" + "lomiri-shell.service" + ]; + after = [ "graphical-session.target" ]; + partOf = wantedBy; + serviceConfig = { + Type = "simple"; + Restart = "always"; + ExecStart = "${pkgs.lomiri.lomiri-polkit-agent}/libexec/lomiri-polkit-agent/policykit-agent"; + }; + }; }; - }; - users.users.usermetrics = { - group = "usermetrics"; - home = "/var/lib/usermetrics"; - createHome = true; - isSystemUser = true; - }; + systemd.services = { + "dbus-com.lomiri.UserMetrics" = { + serviceConfig = + { + Type = "dbus"; + BusName = "com.lomiri.UserMetrics"; + User = "usermetrics"; + StandardOutput = "syslog"; + SyslogIdentifier = "com.lomiri.UserMetrics"; + ExecStart = "${pkgs.lomiri.libusermetrics}/libexec/libusermetrics/usermetricsservice"; + } + // lib.optionalAttrs (!config.security.apparmor.enable) { + # Due to https://gitlab.com/ubports/development/core/libusermetrics/-/issues/8, auth must be disabled when not using AppArmor, lest the next database usage breaks + Environment = "USERMETRICS_NO_AUTH=1"; + }; + }; + }; - users.groups.usermetrics = { }; - }; + users.users.usermetrics = { + group = "usermetrics"; + home = "/var/lib/usermetrics"; + createHome = true; + isSystemUser = true; + }; + + users.groups.usermetrics = { }; + }) + ]; meta.maintainers = lib.teams.lomiri.members; } From de4db8a6b0fcdc381f06fa4be26be42d9755d538 Mon Sep 17 00:00:00 2001 From: OPNA2608 Date: Wed, 30 Oct 2024 14:12:48 +0100 Subject: [PATCH 3/5] nixos/lightdm-greeters/lomiri: nixfmt --- .../x11/display-managers/lightdm-greeters/lomiri.nix | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix index 0cc79178358b..ced4d023dfdd 100644 --- a/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix @@ -1,4 +1,9 @@ -{ config, lib, pkgs, ... }: +{ + config, + lib, + pkgs, + ... +}: let From d14f6e598623f22ddc47427fa68a2013faae27a8 Mon Sep 17 00:00:00 2001 From: OPNA2608 Date: Wed, 30 Oct 2024 14:15:02 +0100 Subject: [PATCH 4/5] nixos/lightdm-greeters/lomiri: Enable lomiri.basics option for shared settings --- .../services/x11/display-managers/lightdm-greeters/lomiri.nix | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix b/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix index ced4d023dfdd..387f485e2fea 100644 --- a/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix +++ b/nixos/modules/services/x11/display-managers/lightdm-greeters/lomiri.nix @@ -22,6 +22,9 @@ in }; config = lib.mkIf (ldmcfg.enable && cfg.enable) { + # Lomiri greeter == Lomiri shell in special mode, need some basics setup at least + services.desktopManager.lomiri.basics = true; + services.xserver.displayManager.lightdm.greeters.gtk.enable = false; services.xserver.displayManager.lightdm.greeter = lib.mkDefault { From 23e40c0ca47b29ef2850a8ef7962e8ee06f7254b Mon Sep 17 00:00:00 2001 From: OPNA2608 Date: Wed, 30 Oct 2024 14:27:21 +0100 Subject: [PATCH 5/5] tests/lomiri: Only enable greeter in greeter test To test a scenario where only the greeter portion is being enabled, not the whole DE. --- nixos/tests/lomiri.nix | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/nixos/tests/lomiri.nix b/nixos/tests/lomiri.nix index 1ff68e85b94d..4e400ccf121e 100644 --- a/nixos/tests/lomiri.nix +++ b/nixos/tests/lomiri.nix @@ -72,11 +72,13 @@ in inherit description password; }; - services.desktopManager.lomiri.enable = lib.mkForce true; - services.displayManager.defaultSession = lib.mkForce "lomiri"; - - # Help with OCR - fonts.packages = [ pkgs.inconsolata ]; + services.xserver.enable = true; + services.xserver.windowManager.icewm.enable = true; + services.xserver.displayManager.lightdm = { + enable = true; + greeters.lomiri.enable = true; + }; + services.displayManager.defaultSession = lib.mkForce "none+icewm"; }; enableOCR = true; @@ -110,13 +112,8 @@ in # Login machine.send_chars("${password}\n") - machine.wait_until_succeeds("pgrep -u ${user} -f 'lomiri --mode=full-shell'") - - # Output rendering from Lomiri has started when it starts printing performance diagnostics - machine.wait_for_console_text("Last frame took") - # Look for datetime's clock, one of the last elements to load - wait_for_text(r"(AM|PM)") - machine.screenshot("lomiri_launched") + machine.wait_for_x() + machine.screenshot("session_launched") ''; } );