From 0b9a9e1217a64b72150900440fc37e26bba19966 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@collabora.com>
Date: Tue, 25 Feb 2020 17:57:31 +0000
Subject: [PATCH] runtime: Add an off-switch for the host graphics stack

In practice we always want the host graphics stack, but maybe this
will be useful when debugging.

Signed-off-by: Simon McVittie <smcv@collabora.com>
---
 meson.build             | 14 ++++++++++-
 pressure-vessel-test-ui | 52 +++++++++++++++++++++++++++++++++++++++++
 src/runtime.c           | 30 ++++++++++++++++++++++--
 src/runtime.h           | 14 +++++++++++
 src/wrap.c              | 17 ++++++++++++++
 5 files changed, 124 insertions(+), 3 deletions(-)

diff --git a/meson.build b/meson.build
index 3cf5dcd1e..89efe5df8 100644
--- a/meson.build
+++ b/meson.build
@@ -30,6 +30,7 @@ project(
   ],
 )
 
+gnome = import('gnome')
 prove = find_program('prove', required : false)
 sh = find_program('sh', required : true)
 
@@ -107,6 +108,7 @@ project_warning_cflags = [
 
 project_include_dirs = include_directories(
     '.',
+    'src',
     'subprojects',
 )
 
@@ -178,6 +180,16 @@ add_project_arguments(
   language : 'c',
 )
 
+# Headers to scan for enum/flags types.
+headers = [
+  'src/runtime.h',
+]
+
+enums = gnome.mkenums_simple(
+  'enumtypes',
+  sources : headers,
+)
+
 executable(
   'pressure-vessel-with-lock',
   sources : [
@@ -231,7 +243,7 @@ executable(
     'src/wrap.c',
     'src/wrap-interactive.c',
     'src/wrap-interactive.h',
-  ],
+  ] + enums,
   c_args : [
     '-Wno-unused-local-typedefs',
   ],
diff --git a/pressure-vessel-test-ui b/pressure-vessel-test-ui
index a0b5270e9..dbb93d939 100755
--- a/pressure-vessel-test-ui
+++ b/pressure-vessel-test-ui
@@ -61,6 +61,23 @@ def tristate_environment(name):
     return None
 
 
+def boolean_environment(name, default):
+    # type: (str, bool) -> bool
+    value = os.getenv(name)
+
+    if value is None:
+        return default
+
+    if value == '1':
+        return True
+
+    if value in ('', '0'):
+        return False
+
+    logger.warning('Unrecognised value %r for $%s', value, name)
+    return default
+
+
 class Gui:
     def __init__(self):
         # type: (...) -> None
@@ -145,6 +162,30 @@ class Gui:
 
         row += 1
 
+        self.host_graphics_check = Gtk.CheckButton.new_with_label(
+            'Use host-system graphics stack'
+        )
+        value = boolean_environment('PRESSURE_VESSEL_HOST_GRAPHICS', True)
+        self.host_graphics_check.set_active(value)
+        self.grid.attach(self.host_graphics_check, 1, row, 1, 1)
+        row += 1
+
+        label = Gtk.Label.new('')
+        label.set_markup(
+            '<small><i>'
+            "Most games and GPUs won't work when this is disabled."
+            '</i></small>'
+        )
+        label.set_halign(Gtk.Align.START)
+        label.set_line_wrap(True)
+        self.grid.attach(label, 1, row, 1, 1)
+        row += 1
+
+        self._container_runtime_changed(self.container_runtime_combo)
+        self.container_runtime_combo.connect(
+            'changed',
+            self._container_runtime_changed)
+
         self.unshare_home_check = Gtk.CheckButton.new_with_label(
             'Use a separate home directory per game'
         )
@@ -258,6 +299,12 @@ class Gui:
         else:
             self.xterm_check.set_sensitive(True)
 
+    def _container_runtime_changed(self, combo):
+        if combo.get_active_id() == '/':
+            self.host_graphics_check.set_sensitive(False)
+        else:
+            self.host_graphics_check.set_sensitive(True)
+
     def _describe_runtime(
         self,
         path        # type: str
@@ -362,6 +409,11 @@ class Gui:
             argv.append('--runtime')
             argv.append(os.path.join(id, 'files'))
 
+        if self.host_graphics_check.get_active():
+            argv.append('--with-host-graphics')
+        else:
+            argv.append('--without-host-graphics')
+
         if self.unshare_home_check.get_active():
             argv.append('--unshare-home')
         else:
diff --git a/src/runtime.c b/src/runtime.c
index 20133cf91..cb02a96dc 100644
--- a/src/runtime.c
+++ b/src/runtime.c
@@ -32,6 +32,7 @@
 
 #include "bwrap.h"
 #include "bwrap-lock.h"
+#include "enumtypes.h"
 #include "flatpak-run-private.h"
 #include "utils.h"
 
@@ -55,6 +56,8 @@ struct _PvRuntime
   gchar *overrides_bin;
   gchar *container_access;
   FlatpakBwrap *container_access_adverb;
+
+  PvRuntimeFlags flags;
   gboolean any_libc_from_host;
   gboolean all_libc_from_host;
 };
@@ -67,6 +70,7 @@ struct _PvRuntimeClass
 enum {
   PROP_0,
   PROP_BUBBLEWRAP,
+  PROP_FLAGS,
   PROP_SOURCE_FILES,
   PROP_TOOLS_DIRECTORY,
   N_PROPERTIES
@@ -128,6 +132,10 @@ pv_runtime_get_property (GObject *object,
         g_value_set_string (value, self->bubblewrap);
         break;
 
+      case PROP_FLAGS:
+        g_value_set_flags (value, self->flags);
+        break;
+
       case PROP_SOURCE_FILES:
         g_value_set_string (value, self->source_files);
         break;
@@ -157,6 +165,10 @@ pv_runtime_set_property (GObject *object,
         self->bubblewrap = g_value_dup_string (value);
         break;
 
+      case PROP_FLAGS:
+        self->flags = g_value_get_flags (value);
+        break;
+
       case PROP_SOURCE_FILES:
         /* Construct-only */
         g_return_if_fail (self->source_files == NULL);
@@ -294,6 +306,13 @@ pv_runtime_class_init (PvRuntimeClass *cls)
                          (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
                           G_PARAM_STATIC_STRINGS));
 
+  properties[PROP_FLAGS] =
+    g_param_spec_flags ("flags", "Flags",
+                        "Flags affecting how we set up the runtime",
+                        PV_TYPE_RUNTIME_FLAGS, PV_RUNTIME_FLAGS_NONE,
+                        (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                         G_PARAM_STATIC_STRINGS));
+
   properties[PROP_SOURCE_FILES] =
     g_param_spec_string ("source-files", "Source files",
                          ("Path to read-only runtime files (merged-/usr "
@@ -316,11 +335,14 @@ PvRuntime *
 pv_runtime_new (const char *source_files,
                 const char *bubblewrap,
                 const char *tools_dir,
+                PvRuntimeFlags flags,
                 GError **error)
 {
   g_return_val_if_fail (source_files != NULL, NULL);
   g_return_val_if_fail (bubblewrap != NULL, NULL);
   g_return_val_if_fail (tools_dir != NULL, NULL);
+  g_return_val_if_fail ((flags & ~(PV_RUNTIME_FLAGS_HOST_GRAPHICS_STACK)) == 0,
+                        NULL);
 
   return g_initable_new (PV_TYPE_RUNTIME,
                          NULL,
@@ -328,6 +350,7 @@ pv_runtime_new (const char *source_files,
                          "bubblewrap", bubblewrap,
                          "source-files", source_files,
                          "tools-directory", tools_dir,
+                         "flags", flags,
                          NULL);
 }
 
@@ -942,8 +965,11 @@ bind_runtime (PvRuntime *self,
                             "--ro-bind", "/etc/group", "/etc/group",
                             NULL);
 
-  if (!pv_runtime_use_host_graphics_stack (self, bwrap, error))
-    return FALSE;
+  if (self->flags & PV_RUNTIME_FLAGS_HOST_GRAPHICS_STACK)
+    {
+      if (!pv_runtime_use_host_graphics_stack (self, bwrap, error))
+        return FALSE;
+    }
 
   /* This needs to be done after pv_runtime_use_host_graphics_stack()
    * has decided whether to bring in the host system's libc. */
diff --git a/src/runtime.h b/src/runtime.h
index 5eef8b552..0fc2ec30e 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -29,6 +29,19 @@
 #include "flatpak-bwrap-private.h"
 #include "flatpak-utils-base-private.h"
 
+/**
+ * PvRuntimeFlags:
+ * @PV_RUNTIME_FLAGS_HOST_GRAPHICS_STACK: Use host system graphics stack
+ * @PV_RUNTIME_FLAGS_NONE: None of the above
+ *
+ * Flags affecting how we set up the runtime.
+ */
+typedef enum
+{
+  PV_RUNTIME_FLAGS_HOST_GRAPHICS_STACK = (1 << 0),
+  PV_RUNTIME_FLAGS_NONE = 0
+} PvRuntimeFlags;
+
 typedef struct _PvRuntime PvRuntime;
 typedef struct _PvRuntimeClass PvRuntimeClass;
 
@@ -43,6 +56,7 @@ GType pv_runtime_get_type (void);
 PvRuntime *pv_runtime_new (const char *source_files,
                            const char *bubblewrap,
                            const char *tools_dir,
+                           PvRuntimeFlags flags,
                            GError **error);
 
 void pv_runtime_append_lock_adverb (PvRuntime *self,
diff --git a/src/wrap.c b/src/wrap.c
index 2c5232335..f70843a12 100644
--- a/src/wrap.c
+++ b/src/wrap.c
@@ -322,6 +322,7 @@ static char *opt_freedesktop_app_id = NULL;
 static char *opt_steam_app_id = NULL;
 static char *opt_home = NULL;
 static gboolean opt_host_fallback = FALSE;
+static gboolean opt_host_graphics = TRUE;
 static PvShell opt_shell = PV_SHELL_NONE;
 static GPtrArray *opt_ld_preload = NULL;
 static char *opt_runtime_base = NULL;
@@ -583,6 +584,14 @@ static GOptionEntry options[] =
   { "version-only", '\0',
     G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_version_only,
     "Print version number (no other information) and exit.", NULL },
+  { "with-host-graphics", '\0',
+    G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_host_graphics,
+    "If using --runtime, use the host graphics stack (default)", NULL },
+  { "without-host-graphics", '\0',
+    G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &opt_host_graphics,
+    "If using --runtime, don't use the host graphics stack. "
+    "This is likely to result in software rendering or a crash.",
+    NULL },
   { "test", '\0',
     G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &opt_test,
     "Smoke test pressure-vessel-wrap and exit.", NULL },
@@ -692,6 +701,8 @@ main (int argc,
     g_clear_pointer (&opt_home, g_free);
 
   opt_share_home = tristate_environment ("PRESSURE_VESSEL_SHARE_HOME");
+  opt_host_graphics = boolean_environment ("PRESSURE_VESSEL_HOST_GRAPHICS",
+                                           TRUE);
   opt_verbose = boolean_environment ("PRESSURE_VESSEL_VERBOSE", FALSE);
 
   if (!opt_shell_cb ("$PRESSURE_VESSEL_SHELL",
@@ -1019,11 +1030,17 @@ main (int argc,
 
   if (opt_runtime != NULL && opt_runtime[0] != '\0')
     {
+      PvRuntimeFlags flags = PV_RUNTIME_FLAGS_NONE;
+
+      if (opt_host_graphics)
+        flags |= PV_RUNTIME_FLAGS_HOST_GRAPHICS_STACK;
+
       g_debug ("Configuring runtime %s...", opt_runtime);
 
       runtime = pv_runtime_new (opt_runtime,
                                 bwrap_executable,
                                 tools_dir,
+                                flags,
                                 error);
 
       if (runtime == NULL)
-- 
GitLab