[gnome-terminal/wip/intent] WIP: intent




commit 76220dc83ab3be42def2d57a8d9240412a45db5f
Author: Christian Persch <chpe src gnome org>
Date:   Tue Aug 3 19:16:19 2021 +0200

    WIP: intent

 data/org.gnome.Terminal.desktop.in |   1 +
 src/org.gnome.Terminal.xml         |  14 ++-
 src/server.cc                      |   4 +-
 src/terminal-app.cc                | 103 +++++++++++----
 src/terminal-app.hh                |   3 +-
 src/terminal-defines.hh            |   3 +
 src/terminal-gdbus.cc              | 249 ++++++++++++++++++++++++++++++++++++-
 src/terminal-gdbus.hh              |  32 ++++-
 src/terminal-screen.cc             |  63 +++++++---
 src/terminal-screen.hh             |  11 +-
 10 files changed, 425 insertions(+), 58 deletions(-)
---
diff --git a/data/org.gnome.Terminal.desktop.in b/data/org.gnome.Terminal.desktop.in
index 915c077f..072e2e71 100644
--- a/data/org.gnome.Terminal.desktop.in
+++ b/data/org.gnome.Terminal.desktop.in
@@ -11,6 +11,7 @@ StartupNotify=true
 StartupWMClass=Gnome-terminal
 X-GNOME-SingleWindow=false
 Actions=new-window;preferences;
+Implements=org.freedesktop.Terminal1;
 
 [Desktop Action new-window]
 Name=New Window
diff --git a/src/org.gnome.Terminal.xml b/src/org.gnome.Terminal.xml
index a6ada0e5..59c8a6f4 100644
--- a/src/org.gnome.Terminal.xml
+++ b/src/org.gnome.Terminal.xml
@@ -2,7 +2,7 @@
 <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Introspection 0.1//EN"
                       "http://www.freedesktop.org/software/dbus/introspection.dtd";>
 <!--
-  Copyright © 2011 Christian Persch
+  Copyright © 2011, 2021 Christian Persch
 
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
@@ -40,4 +40,16 @@
       <arg type="i" name="exit_code" direction="in" />
     </signal>
   </interface>
+
+  <interface name="org.freedesktop.Terminal1">
+    <annotation name="org.gtk.GDBus.C.Name" value="Intent" />
+    <method name="LaunchCommand">
+      <arg type="aay" name="argv" direction="in" />
+      <arg type="ay" name="cwd" direction="in" />
+      <arg type="ay" name="desktop_entry" direction="in" />
+      <arg type="aay" name="envv" direction="in" />
+      <arg type="a{sv}" name="options" direction="in" />
+      <arg type="a{sv}" name="platform_data" direction="in" />
+    </method>
+  </interface>
 </node>
diff --git a/src/server.cc b/src/server.cc
index 358d62ae..94c6462a 100644
--- a/src/server.cc
+++ b/src/server.cc
@@ -43,6 +43,7 @@
 #include "terminal-libgsystem.hh"
 
 static char *app_id = nullptr;
+static gboolean no_intent = false;
 
 #define INACTIVITY_TIMEOUT (100 /* ms */)
 
@@ -67,6 +68,7 @@ option_app_id_cb (const gchar *option_name,
 
 static const GOptionEntry options[] = {
   { "app-id", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, (void*)option_app_id_cb, "Application ID", 
"ID" },
+  { "no-intent", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, (void*)&no_intent, nullptr, nullptr },
   { nullptr }
 };
 
@@ -163,7 +165,7 @@ init_server (int argc,
   }
 
   /* Now we can create the app */
-  GApplication *app = terminal_app_new (app_id);
+  auto const app = terminal_app_new(app_id, !no_intent);
   g_free (app_id);
   app_id = nullptr;
 
diff --git a/src/terminal-app.cc b/src/terminal-app.cc
index a06485cf..84317e8e 100644
--- a/src/terminal-app.cc
+++ b/src/terminal-app.cc
@@ -95,7 +95,9 @@ struct _TerminalApp
 {
   GtkApplication parent_instance;
 
-  GDBusObjectManagerServer *object_manager;
+  GDBusObjectManagerServer *org_gnome_object_manager;
+  TerminalIntent* terminal_intent;
+  bool implement_intent;
 
   TerminalSettingsList *profiles_list;
 
@@ -136,6 +138,11 @@ enum
   LAST_SIGNAL
 };
 
+enum {
+  PROP_0,
+  PROP_IMPLEMENT_INTENT,
+};
+
 static guint signals[LAST_SIGNAL];
 
 /* Debugging helper */
@@ -928,8 +935,6 @@ terminal_app_dbus_register (GApplication    *application,
                             GError         **error)
 {
   TerminalApp *app = TERMINAL_APP (application);
-  gs_unref_object TerminalObjectSkeleton *object = nullptr;
-  gs_unref_object TerminalFactory *factory = nullptr;
 
   if (!G_APPLICATION_CLASS (terminal_app_parent_class)->dbus_register (application,
                                                                        connection,
@@ -953,15 +958,30 @@ terminal_app_dbus_register (GApplication    *application,
   }
 #endif /* ENABLE_SEARCH_PROVIDER */
 
-  object = terminal_object_skeleton_new (TERMINAL_FACTORY_OBJECT_PATH);
-  factory = terminal_factory_impl_new ();
-  terminal_object_skeleton_set_factory (object, factory);
+  if (app->implement_intent) {
+    app->terminal_intent = terminal_intent_impl_new();
+    if (!g_dbus_interface_skeleton_export(G_DBUS_INTERFACE_SKELETON(app->terminal_intent),
+                                          connection,
+                                          TERMINAL_INTENT_OBJECT_PATH,
+                                          error))
+      return false;
+  }
+
+  gs_unref_object TerminalObjectSkeleton* object = nullptr;
+  gs_unref_object TerminalFactory* factory = nullptr;
+  object = terminal_object_skeleton_new(TERMINAL_FACTORY_OBJECT_PATH);
+  factory = terminal_factory_impl_new();
+  terminal_object_skeleton_set_factory(object, factory);
 
-  app->object_manager = g_dbus_object_manager_server_new (TERMINAL_OBJECT_PATH_PREFIX);
-  g_dbus_object_manager_server_export (app->object_manager, G_DBUS_OBJECT_SKELETON (object));
+  app->org_gnome_object_manager =
+    g_dbus_object_manager_server_new(TERMINAL_OBJECT_PATH_PREFIX);
 
-  /* And export the object */
-  g_dbus_object_manager_server_set_connection (app->object_manager, connection);
+  g_dbus_object_manager_server_export(app->org_gnome_object_manager,
+                                     G_DBUS_OBJECT_SKELETON(object));
+
+  /* And export the objects */
+  g_dbus_object_manager_server_set_connection(app->org_gnome_object_manager,
+                                             connection);
   return TRUE;
 }
 
@@ -972,10 +992,17 @@ terminal_app_dbus_unregister (GApplication    *application,
 {
   TerminalApp *app = TERMINAL_APP (application);
 
-  if (app->object_manager) {
-    g_dbus_object_manager_server_unexport (app->object_manager, TERMINAL_FACTORY_OBJECT_PATH);
-    g_object_unref (app->object_manager);
-    app->object_manager = nullptr;
+  if (app->org_gnome_object_manager) {
+    g_dbus_object_manager_server_unexport(app->org_gnome_object_manager,
+                                         TERMINAL_FACTORY_OBJECT_PATH);
+    g_object_unref(app->org_gnome_object_manager);
+    app->org_gnome_object_manager = nullptr;
+  }
+
+  if (app->terminal_intent) {
+    g_dbus_interface_skeleton_unexport(G_DBUS_INTERFACE_SKELETON(app->terminal_intent));
+    g_object_unref(app->terminal_intent);
+    app->terminal_intent = nullptr;
   }
 
 #ifdef ENABLE_SEARCH_PROVIDER
@@ -991,6 +1018,25 @@ terminal_app_dbus_unregister (GApplication    *application,
                                                                     object_path);
 }
 
+static void
+terminal_app_set_property(GObject* object,
+                          guint prop_id,
+                          GValue const* value,
+                          GParamSpec* pspec)
+{
+  auto const app = TERMINAL_APP(object);
+
+  switch (prop_id)
+    {
+      case PROP_IMPLEMENT_INTENT:
+        app->implement_intent = g_value_get_boolean(value) != false;
+        break;
+      default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
 static void
 terminal_app_class_init (TerminalAppClass *klass)
 {
@@ -998,6 +1044,7 @@ terminal_app_class_init (TerminalAppClass *klass)
   GApplicationClass *g_application_class = G_APPLICATION_CLASS (klass);
 
   object_class->finalize = terminal_app_finalize;
+  object_class->set_property = terminal_app_set_property;
 
   g_application_class->activate = terminal_app_activate;
   g_application_class->startup = terminal_app_startup;
@@ -1012,18 +1059,32 @@ terminal_app_class_init (TerminalAppClass *klass)
                   nullptr, nullptr,
                   g_cclosure_marshal_VOID__OBJECT,
                   G_TYPE_NONE, 1, G_TYPE_OBJECT);
+
+  g_object_class_install_property
+    (object_class,
+     PROP_IMPLEMENT_INTENT,
+     g_param_spec_boolean("implement-intent", nullptr, nullptr,
+                          false,
+                          GParamFlags(G_PARAM_WRITABLE |
+                                      G_PARAM_CONSTRUCT_ONLY |
+                                      G_PARAM_STATIC_NAME |
+                                      G_PARAM_STATIC_NICK |
+                                      G_PARAM_STATIC_BLURB)));
+
 }
 
 /* Public API */
 
 GApplication *
-terminal_app_new (const char *app_id)
+terminal_app_new (const char *app_id,
+                  gboolean implement_intent)
 {
   const GApplicationFlags flags = G_APPLICATION_IS_SERVICE;
 
   return reinterpret_cast<GApplication*>
     (g_object_new (TERMINAL_TYPE_APP,
                   "application-id", app_id ? app_id : TERMINAL_APPLICATION_ID,
+                   "implement-intent", implement_intent,
                   "flags", flags,
                   nullptr));
 }
@@ -1060,8 +1121,8 @@ terminal_app_get_receiver_impl_by_object_path (TerminalApp *app,
                                                const char *object_path)
 {
   gs_unref_object GDBusObject *skeleton =
-    g_dbus_object_manager_get_object (G_DBUS_OBJECT_MANAGER (app->object_manager),
-                                      object_path);
+    g_dbus_object_manager_get_object(G_DBUS_OBJECT_MANAGER(app->org_gnome_object_manager),
+                                    object_path);
   if (skeleton == nullptr || !TERMINAL_IS_OBJECT_SKELETON (skeleton))
     return nullptr;
 
@@ -1107,7 +1168,7 @@ terminal_app_register_screen (TerminalApp *app,
   terminal_object_skeleton_set_receiver (skeleton, TERMINAL_RECEIVER (impl));
   g_object_unref (impl);
 
-  g_dbus_object_manager_server_export (app->object_manager,
+  g_dbus_object_manager_server_export (app->org_gnome_object_manager,
                                        G_DBUS_OBJECT_SKELETON (skeleton));
 }
 
@@ -1129,7 +1190,7 @@ terminal_app_unregister_screen (TerminalApp *app,
   if (impl != nullptr)
     terminal_receiver_impl_unset_screen (impl);
 
-  g_dbus_object_manager_server_unexport (app->object_manager, object_path);
+  g_dbus_object_manager_server_unexport (app->org_gnome_object_manager, object_path);
 }
 
 GdkAtom *
@@ -1316,8 +1377,8 @@ terminal_app_get_system_font (TerminalApp *app)
 GDBusObjectManagerServer *
 terminal_app_get_object_manager (TerminalApp *app)
 {
-  g_warn_if_fail (app->object_manager != nullptr);
-  return app->object_manager;
+  g_warn_if_fail (app->org_gnome_object_manager != nullptr);
+  return app->org_gnome_object_manager;
 }
 
 gboolean
diff --git a/src/terminal-app.hh b/src/terminal-app.hh
index b86aea5d..cf614a87 100644
--- a/src/terminal-app.hh
+++ b/src/terminal-app.hh
@@ -46,7 +46,8 @@ typedef struct _TerminalApp TerminalApp;
 
 GType terminal_app_get_type (void);
 
-GApplication *terminal_app_new (const char *app_id);
+GApplication *terminal_app_new (const char *app_id,
+                                gboolean implement_intent);
 
 #define terminal_app_get (TerminalApp *) g_application_get_default
 
diff --git a/src/terminal-defines.hh b/src/terminal-defines.hh
index b9ffba25..db6a06cb 100644
--- a/src/terminal-defines.hh
+++ b/src/terminal-defines.hh
@@ -40,6 +40,9 @@ enum {
 
 #define TERMINAL_SEARCH_PROVIDER_PATH           TERMINAL_OBJECT_PATH_PREFIX "/SearchProvider"
 
+#define TERMINAL_INTENT_OBJECT_PATH            "/org/freedesktop/intent/Terminal1"
+#define TERMINAL_INTENT_INTERFACE_NAME         "org.freedesktop.Terminal1"
+
 #define TERMINAL_ENV_SERVICE_NAME               "GNOME_TERMINAL_SERVICE"
 #define TERMINAL_ENV_SCREEN                     "GNOME_TERMINAL_SCREEN"
 
diff --git a/src/terminal-gdbus.cc b/src/terminal-gdbus.cc
index 66bff6aa..9d382f6e 100644
--- a/src/terminal-gdbus.cc
+++ b/src/terminal-gdbus.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright © 2011, 2012 Christian Persch
+ * Copyright © 2011, 2012, 2021 Christian Persch
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
 
 #include <gio/gio.h>
 #include <gio/gunixfdlist.h>
+#include <glib/gi18n.h>
 
 #include "terminal-app.hh"
 #include "terminal-debug.hh"
@@ -30,6 +31,10 @@
 #include "terminal-window.hh"
 #include "terminal-libgsystem.hh"
 
+#ifdef GDK_WINDOWING_X11
+#include <gdk/gdkx.h>
+#endif
+
 /* ------------------------------------------------------------------------- */
 
 #define TERMINAL_RECEIVER_IMPL_GET_PRIVATE(impl)(G_TYPE_INSTANCE_GET_PRIVATE ((impl), 
TERMINAL_TYPE_RECEIVER_IMPL, TerminalReceiverImplPrivate))
@@ -85,14 +90,14 @@ terminal_receiver_impl_set_screen (TerminalReceiverImpl *impl,
 /* Class implementation */
 
 typedef struct {
-  TerminalReceiver *receiver;
-  GDBusMethodInvocation *invocation;
+  void* object;
+  GDBusMethodInvocation* invocation;
 } ExecData;
 
 static void
 exec_data_free (ExecData *data)
 {
-  g_object_unref (data->receiver);
+  g_object_unref (data->object);
   g_object_unref (data->invocation);
   g_free (data);
 }
@@ -107,7 +112,8 @@ exec_cb (TerminalScreen *screen, /* unused, may be %nullptr */
   if (error) {
     g_dbus_method_invocation_return_gerror (data->invocation, error);
   } else {
-    terminal_receiver_complete_exec (data->receiver, data->invocation, nullptr /* outfdlist */);
+    terminal_receiver_complete_exec(TERMINAL_RECEIVER(data->object),
+                                    data->invocation, nullptr /* outfdlist */);
   }
 }
 
@@ -207,7 +213,7 @@ terminal_receiver_impl_exec (TerminalReceiver *receiver,
   exec_argv = (char **) g_variant_get_bytestring_array (arguments, &exec_argc);
 
   ExecData *exec_data = g_new (ExecData, 1);
-  exec_data->receiver = (TerminalReceiver*)g_object_ref (receiver);
+  exec_data->object = g_object_ref(receiver);
   /* We want to transfer the ownership of @invocation to ExecData here, but
    * we have to temporarily ref it so that in the error case below (where
    * terminal_screen_exec() frees the exec data via the supplied callback,
@@ -565,3 +571,234 @@ terminal_factory_impl_new (void)
   return reinterpret_cast<TerminalFactory*>
     (g_object_new (TERMINAL_TYPE_FACTORY_IMPL, nullptr));
 }
+
+/* ---------------------------------------------------------------------------
+ * org.freedesktop.Terminal1 intent implementation
+ * ---------------------------------------------------------------------------
+ */
+
+static void
+exec_intent_cb(TerminalScreen *screen, /* unused, may be %nullptr */
+               GError *error, /* set on error, %nullptr on success */
+               ExecData *data)
+{
+  /* Note: these calls transfer the ref */
+  g_object_ref(data->invocation);
+  if (error) {
+    g_dbus_method_invocation_return_gerror(data->invocation, error);
+  } else {
+    terminal_intent_complete_launch_command(TERMINAL_INTENT(data->object),
+                                           data->invocation);
+  }
+}
+
+static gboolean
+terminal_intent_impl_launch_command(TerminalIntent* intent,
+                                   GDBusMethodInvocation* invocation,
+                                   char const* const* argv,
+                                   char const* cwd,
+                                   char const* desktop_entry,
+                                   char const* const* envv,
+                                   GVariant* options,
+                                   GVariant* platform_data)
+{
+  TerminalApp *app = terminal_app_get ();
+
+  if (!terminal_util_check_envv(envv)) {
+    /* Transfers ownership of @invocation */
+    g_dbus_method_invocation_return_error_literal(invocation,
+                                                 G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                                 "Invalid envv array passed");
+      return true;
+  }
+
+  /* Try to get some info from the passed desktop entry */
+  gs_free char* desktop_cwd = nullptr;
+  gs_free char* desktop_icon = nullptr;
+  gs_free char* desktop_title = nullptr;
+  gs_free char* desktop_wm_class = nullptr;
+  gs_free char* desktop_profile_uuid = nullptr;
+
+  if (desktop_entry[0]) {
+    gs_free char* desktop_entry_utf8 = g_utf8_make_valid(desktop_entry, -1);
+    gs_unref_key_file GKeyFile* key_file = g_key_file_new();
+
+    gs_free_error GError* err = nullptr;
+    if (!g_key_file_load_from_file(key_file,
+                                   desktop_entry,
+                                   GKeyFileFlags(G_KEY_FILE_NONE),
+                                   &err)) {
+      /* Transfers ownership of @invocation */
+      g_dbus_method_invocation_return_error(invocation,
+                                            G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                            "Failed to load desktop entry \"%s\": %s",
+                                            desktop_entry_utf8, err->message);
+      return true;
+    }
+
+    gs_free char* version = g_key_file_get_string(key_file,
+                                                  G_KEY_FILE_DESKTOP_GROUP,
+                                                  G_KEY_FILE_DESKTOP_KEY_VERSION,
+                                                  nullptr);
+    gs_free char* type = g_key_file_get_string(key_file,
+                                               G_KEY_FILE_DESKTOP_GROUP,
+                                               G_KEY_FILE_DESKTOP_KEY_TYPE,
+                                               nullptr);
+    auto const is_terminal = g_key_file_get_boolean(key_file,
+                                                    G_KEY_FILE_DESKTOP_GROUP,
+                                                    G_KEY_FILE_DESKTOP_KEY_TERMINAL,
+                                                    nullptr);
+    if (!version ||
+        !g_str_equal(version, "1.0") ||
+        !type ||
+        !g_str_equal(type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) ||
+        !is_terminal) {
+      /* Transfers ownership of @invocation */
+      g_dbus_method_invocation_return_error(invocation,
+                                            G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                            "\"%s\" is not a valid desktop file of a terminal application",
+                                            desktop_entry_utf8);
+      return true;
+    }
+
+
+    desktop_cwd = g_key_file_get_string(key_file,
+                                        G_KEY_FILE_DESKTOP_GROUP,
+                                        G_KEY_FILE_DESKTOP_KEY_PATH,
+                                        nullptr);
+
+    desktop_icon = g_key_file_get_locale_string(key_file,
+                                                G_KEY_FILE_DESKTOP_GROUP,
+                                                G_KEY_FILE_DESKTOP_KEY_ICON,
+                                                nullptr, // default locale
+                                                nullptr);
+
+    desktop_title = g_key_file_get_locale_string(key_file,
+                                                 G_KEY_FILE_DESKTOP_GROUP,
+                                                 G_KEY_FILE_DESKTOP_KEY_NAME,
+                                                 nullptr, // default locale
+                                                 nullptr);
+    if (!desktop_title || !desktop_title[0]) {
+      g_clear_pointer(&desktop_title, GDestroyNotify(g_free));
+      desktop_title = g_key_file_get_locale_string(key_file,
+                                                   G_KEY_FILE_DESKTOP_GROUP,
+                                                   G_KEY_FILE_DESKTOP_KEY_GENERIC_NAME,
+                                                   nullptr, // default locale
+                                                   nullptr);
+    }
+
+    desktop_wm_class = g_key_file_get_string(key_file,
+                                             G_KEY_FILE_DESKTOP_GROUP,
+                                             G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS,
+                                             nullptr);
+
+    desktop_profile_uuid = g_key_file_get_string(key_file,
+                                                 G_KEY_FILE_DESKTOP_GROUP,
+                                                 "X-GNOME-Profile-UUID",
+                                                 nullptr);
+  }
+
+  auto option_keep = gboolean{false};
+  (void)g_variant_lookup(options, "keep-terminal-open", "b", &option_keep);
+
+  char const* option_profile_uuid = nullptr;
+  (void)g_variant_lookup(options, "x-gnome-profile", "&s", &option_profile_uuid);
+
+  GError* err = nullptr;
+  gs_unref_object GSettings* profile =
+    terminal_profiles_list_ref_profile_by_uuid(terminal_app_get_profiles_list (app),
+                                               option_profile_uuid ? option_profile_uuid : 
desktop_profile_uuid, /* nullptr if default */
+                                               &err);
+  if (profile == nullptr) {
+    g_dbus_method_invocation_take_error(invocation, err);
+    return true;
+  }
+
+  /* Now open new window and terminal */
+  auto window = terminal_window_new(G_APPLICATION(app));
+
+  char const* startup_id = nullptr;
+  if (g_variant_lookup(platform_data, "desktop-startup-id", "&s", &startup_id))
+    gtk_window_set_startup_id(GTK_WINDOW(window), startup_id);
+
+  if (desktop_wm_class) {
+#ifdef GDK_WINDOWING_X11
+    if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
+      gtk_window_set_wmclass(GTK_WINDOW(window), desktop_wm_class, desktop_wm_class);
+#endif
+  }
+
+  auto const zoom = 1.0;
+
+  auto const screen = terminal_screen_new(profile,
+                                          desktop_title ? desktop_title : _("Terminal"),
+                                          zoom);
+
+  if (option_keep)
+    terminal_screen_set_exit_action(screen, TERMINAL_EXIT_HOLD);
+
+  terminal_window_add_screen(window, screen, -1);
+
+  gtk_window_present(GTK_WINDOW(window));
+
+  auto exec_data = g_new(ExecData, 1);
+  exec_data->object = g_object_ref(intent);
+  /* We want to transfer the ownership of @invocation to ExecData here, but
+   * we have to temporarily ref it so that in the error case below (where
+   * terminal_screen_exec() frees the exec data via the supplied callback,
+   * the g_dbus_method_invocation_take_error() calll still can take ownership
+   * of the invocation's ref passed to this function (terminal_receiver_impl_exec()).
+   */
+  exec_data->invocation = (GDBusMethodInvocation*)g_object_ref (invocation);
+
+  if (!terminal_screen_exec(screen,
+                            argv,
+                            envv,
+                            false /* shell */,
+                            cwd[0] ? cwd : desktop_cwd ? desktop_cwd : nullptr,
+                            nullptr, nullptr,
+                            TerminalScreenExecCallback(exec_intent_cb),
+                            exec_data /* adopted */,
+                            GDestroyNotify(exec_data_free),
+                            nullptr /* cancellable */,
+                            &err)) {
+    /* Transfers ownership of @invocation */
+    g_dbus_method_invocation_take_error(invocation, err);
+  }
+
+  /* Now we can remove that extra ref again. */
+  g_object_unref (invocation);
+
+  return true;
+}
+
+static void
+terminal_intent_impl_iface_init (TerminalIntentIface *iface)
+{
+  iface->handle_launch_command = terminal_intent_impl_launch_command;
+}
+
+G_DEFINE_TYPE_WITH_CODE (TerminalIntentImpl, terminal_intent_impl, TERMINAL_TYPE_INTENT_SKELETON,
+                         G_IMPLEMENT_INTERFACE (TERMINAL_TYPE_INTENT, terminal_intent_impl_iface_init))
+
+static void
+terminal_intent_impl_init (TerminalIntentImpl *impl)
+{
+}
+
+static void
+terminal_intent_impl_class_init (TerminalIntentImplClass *klass)
+{
+}
+
+/**
+ * terminal_intent_impl_new:
+ *
+ * Returns: (transfer full): a new #TerminalIntentImpl
+ */
+TerminalIntent *
+terminal_intent_impl_new (void)
+{
+  return reinterpret_cast<TerminalIntent*>
+    (g_object_new (TERMINAL_TYPE_INTENT_IMPL, nullptr));
+}
diff --git a/src/terminal-gdbus.hh b/src/terminal-gdbus.hh
index 38c24bc4..baa6a9b7 100644
--- a/src/terminal-gdbus.hh
+++ b/src/terminal-gdbus.hh
@@ -1,5 +1,5 @@
 /*
- *  Copyright © 2011 Christian Persch
+ *  Copyright © 2011, 2021 Christian Persch
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -15,8 +15,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef TERMINAL_RECEIVER_IMPL_H
-#define TERMINAL_RECEIVER_IMPL_H
+#pragma once
 
 #include <glib-object.h>
 
@@ -85,6 +84,29 @@ GType terminal_factory_impl_get_type (void);
 
 TerminalFactory *terminal_factory_impl_new (void);
 
-G_END_DECLS
+/* ------------------------------------------------------------------------- */
+
+#define TERMINAL_TYPE_INTENT_IMPL              (terminal_intent_impl_get_type ())
+#define TERMINAL_INTENT_IMPL(object)           (G_TYPE_CHECK_INSTANCE_CAST ((object), 
TERMINAL_TYPE_INTENT_IMPL, TerminalIntentImpl))
+#define TERMINAL_INTENT_IMPL_CLASS(klass)      (G_TYPE_CHECK_CLASS_CAST ((klass), TERMINAL_TYPE_INTENT_IMPL, 
TerminalIntentImplClass))
+#define TERMINAL_IS_INTENT_IMPL(object)        (G_TYPE_CHECK_INSTANCE_TYPE ((object), 
TERMINAL_TYPE_INTENT_IMPL))
+#define TERMINAL_IS_INTENT_IMPL_CLASS(klass)   (G_TYPE_CHECK_CLASS_TYPE ((klass), TERMINAL_TYPE_INTENT_IMPL))
+#define TERMINAL_INTENT_IMPL_GET_CLASS(obj)    (G_TYPE_INSTANCE_GET_CLASS ((obj), TERMINAL_TYPE_INTENT_IMPL, 
TerminalIntentImplClass))
+
+typedef struct _TerminalIntentImpl        TerminalIntentImpl;
+typedef struct _TerminalIntentImplClass   TerminalIntentImplClass;
+
+struct _TerminalIntentImplClass {
+  TerminalIntentSkeletonClass parent_class;
+};
 
-#endif /* !TERMINAL_RECEIVER_IMPL_H */
+struct _TerminalIntentImpl
+{
+  TerminalIntentSkeleton parent_instance;
+};
+
+GType terminal_intent_impl_get_type (void);
+
+TerminalIntent *terminal_intent_impl_new (void);
+
+G_END_DECLS
diff --git a/src/terminal-screen.cc b/src/terminal-screen.cc
index 382ac296..6036a89e 100644
--- a/src/terminal-screen.cc
+++ b/src/terminal-screen.cc
@@ -108,6 +108,9 @@ struct _TerminalScreenPrivate
   gboolean exec_on_realize;
   guint idle_exec_source;
   ExecData *exec_data;
+
+  bool exit_action_set;
+  TerminalExitAction exit_action;
 };
 
 enum
@@ -176,12 +179,12 @@ static void terminal_screen_show_info_bar (TerminalScreen *screen,
 
 
 static char**terminal_screen_get_child_environment (TerminalScreen *screen,
-                                                    char **initial_envv,
+                                                    char const* const* initial_envv,
                                                     char **path,
                                                     char **shell);
 
 static gboolean terminal_screen_get_child_command (TerminalScreen *screen,
-                                                   char          **exec_argv,
+                                                   char const* const* exec_argv,
                                                    const char     *path_env,
                                                    const char     *shell_env,
                                                    gboolean        shell,
@@ -236,9 +239,9 @@ fake_dup3 (int fd, int fd2, int flags)
 #endif /* !__linux__ */
 
 static char*
-strv_to_string (char **strv)
+strv_to_string (char const* const* strv)
 {
-  return strv ? g_strjoinv (" ", strv) : g_strdup ("(null)");
+  return strv ? g_strjoinv (" ", (char**)strv) : g_strdup ("(null)");
 }
 
 static char*
@@ -896,10 +899,10 @@ terminal_screen_reexec (TerminalScreen *screen,
 
 gboolean
 terminal_screen_exec (TerminalScreen *screen,
-                      char **argv,
-                      char **initial_envv,
+                      char const* const* argv,
+                      char const* const* initial_envv,
                       gboolean as_shell,
-                      const char *cwd,
+                      char const* cwd,
                       GUnixFDList *fd_list,
                       GVariant *fd_array,
                       TerminalScreenExecCallback callback,
@@ -919,7 +922,7 @@ terminal_screen_exec (TerminalScreen *screen,
                            "[screen %p] exec: argv:[%s] envv:%p(%u) as-shell:%s cwd:%s\n",
                            screen,
                            (argv_str = strv_to_string(argv)),
-                           initial_envv, initial_envv ? g_strv_length (initial_envv) : 0,
+                           initial_envv, initial_envv ? g_strv_length((char**)initial_envv) : 0,
                            as_shell ? "true":"false",
                            cwd);
   }
@@ -944,7 +947,7 @@ terminal_screen_exec (TerminalScreen *screen,
 
   gs_free char *path = nullptr;
   gs_free char *shell = nullptr;
-  gs_strfreev char **envv = terminal_screen_get_child_environment (screen,
+  gs_strfreev char **envv = terminal_screen_get_child_environment(screen,
                                                                   initial_envv,
                                                                   &path,
                                                                   &shell);
@@ -997,7 +1000,7 @@ terminal_screen_exec (TerminalScreen *screen,
     data->fd_map = nullptr;
   }
 
-  data->argv = g_strdupv (argv);
+  data->argv = g_strdupv ((char**)argv);
   data->exec_argv = g_strdupv (exec_argv);
   data->cwd = g_strdup (cwd);
   data->envv = g_strdupv (envv);
@@ -1348,7 +1351,7 @@ should_preserve_cwd (TerminalPreserveWorkingDirectory preserve_cwd,
 
 static gboolean
 terminal_screen_get_child_command (TerminalScreen *screen,
-                                   char          **argv,
+                                   char const* const* argv,
                                    const char     *path_env,
                                    const char     *shell_env,
                                    gboolean        as_shell,
@@ -1371,7 +1374,7 @@ terminal_screen_get_child_command (TerminalScreen *screen,
 
   if (argv)
     {
-      exec_argv = g_strdupv (argv);
+      exec_argv = g_strdupv((char**)argv);
 
       /* argv and cwd come from the command line client, so it must always be used */
       *preserve_cwd_p = TRUE;
@@ -1432,10 +1435,10 @@ terminal_screen_get_child_command (TerminalScreen *screen,
 }
 
 static char**
-terminal_screen_get_child_environment (TerminalScreen *screen,
-                                       char **initial_envv,
-                                       char **path,
-                                       char **shell)
+terminal_screen_get_child_environment(TerminalScreen *screen,
+                                     char const* const* initial_envv,
+                                     char **path,
+                                     char **shell)
 {
   TerminalApp *app = terminal_app_get ();
   char **env;
@@ -1449,11 +1452,11 @@ terminal_screen_get_child_environment (TerminalScreen *screen,
   env_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
   if (initial_envv)
-    env = initial_envv;
+    env = (char**)initial_envv;
   else {
     env = current_environ = g_get_environ ();
     /* Remove this variable which we set in server.c:main() */
-    env = g_environ_unsetenv (env, "G_ENABLE_DIAGNOSTIC");
+    env = g_environ_unsetenv(env, "G_ENABLE_DIAGNOSTIC");
   }
 
   for (i = 0; env[i]; ++i)
@@ -1871,7 +1874,12 @@ terminal_screen_child_exited (VteTerminal *terminal,
 
   priv->child_pid = -1;
 
-  action = TerminalExitAction(g_settings_get_enum (priv->profile, TERMINAL_PROFILE_EXIT_ACTION_KEY));
+  if (priv->exit_action_set)
+    action = priv->exit_action;
+  else
+    action = TerminalExitAction(g_settings_get_enum (priv->profile, TERMINAL_PROFILE_EXIT_ACTION_KEY));
+
+  auto const can_relaunch = !priv->exit_action_set;
 
   switch (action)
     {
@@ -1885,8 +1893,12 @@ terminal_screen_child_exited (VteTerminal *terminal,
       GtkWidget *info_bar;
 
       info_bar = terminal_info_bar_new (GTK_MESSAGE_INFO,
-                                        _("_Relaunch"), RESPONSE_RELAUNCH,
                                         nullptr);
+      if (can_relaunch)
+        gtk_info_bar_add_button(GTK_INFO_BAR(info_bar),
+                                _("_Relaunch"),
+                                RESPONSE_RELAUNCH);
+
       if (WIFEXITED (status)) {
         terminal_info_bar_format_text (TERMINAL_INFO_BAR (info_bar),
                                       _("The child process exited normally with status %d."), WEXITSTATUS 
(status));
@@ -2332,3 +2344,14 @@ terminal_screen_get_uuid (TerminalScreen *screen)
 
   return screen->priv->uuid;
 }
+
+void
+terminal_screen_set_exit_action(TerminalScreen* screen,
+                                TerminalExitAction action)
+{
+  g_return_if_fail(TERMINAL_IS_SCREEN(screen));
+
+  auto const priv = screen->priv;
+  priv->exit_action = action;
+  priv->exit_action_set = true;
+}
diff --git a/src/terminal-screen.hh b/src/terminal-screen.hh
index df59b1a5..16ef8f6f 100644
--- a/src/terminal-screen.hh
+++ b/src/terminal-screen.hh
@@ -24,6 +24,8 @@
 
 #include <vte/vte.h>
 
+#include "terminal-enums.hh"
+
 G_BEGIN_DECLS
 
 typedef enum {
@@ -84,10 +86,10 @@ typedef void (* TerminalScreenExecCallback) (TerminalScreen *screen,
                                              gpointer        user_data);
 
 gboolean terminal_screen_exec (TerminalScreen *screen,
-                               char **argv,
-                               char **envv,
+                               char const* const* argv,
+                               char const* const* envv,
                                gboolean as_shell,
-                               const char *cwd,
+                               char const* cwd,
                                GUnixFDList *fd_list,
                                GVariant *fd_array,
                                TerminalScreenExecCallback callback,
@@ -126,6 +128,9 @@ void       terminal_screen_get_cell_size (TerminalScreen *screen,
 
 void _terminal_screen_update_scrollbar (TerminalScreen *screen);
 
+void terminal_screen_set_exit_action(TerminalScreen* screen,
+                                     TerminalExitAction action);
+
 void terminal_screen_save_config (TerminalScreen *screen,
                                   GKeyFile *key_file,
                                   const char *group);


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]