[gnome-terminal] client: legacy: Improve --tab behaviour to open a new tab in an existing window



commit 4f2d2a394282bb0d15ee13bb8c7cb6efee0cd8f4
Author: Christian Persch <chpe src gnome org>
Date:   Sat Nov 11 21:49:12 2017 +0100

    client: legacy: Improve --tab behaviour to open a new tab in an existing window
    
    An often-requested feature has been for "gnome-terminal --tab" to open a
    new tab in an existing window, see the discussion in bug 83203. While
    in general, when called from an external source, it's often unclear
    which window to choose, we can at least provide this when called from
    within gnome-terminal.
    
    For this, introduce some environment variables GNOME_TERMINAL_SERVICE
    that is the D-Bus unique name of the gnome-terminal-server instance for
    the terminal, and GNOME_TERMINAL_SCREEN that is the D-Bus object path
    for the D-Bus controller for that terminal.
    
    On the server side, these variables will be set for each terminal
    created in the environment of the child process.
    
    On the client side, these variables will be used to define which
    window the "--tab" option refers to. Also, optionally, when using
    the new "--print-environment" option, the client will print the
    corresponding variables for the *newly created* terminal.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=83203

 configure.ac                 |    2 +-
 src/terminal-app.c           |   91 +++++++++++++++--
 src/terminal-app.h           |    6 +
 src/terminal-client-utils.c  |    7 +-
 src/terminal-defines.h       |    5 +-
 src/terminal-gdbus.c         |  211 ++++++++++++++++++--------------------
 src/terminal-gdbus.h         |    2 +-
 src/terminal-options.c       |  236 ++++++++++++++++++++++++++++++++----------
 src/terminal-options.h       |   24 +++-
 src/terminal-profiles-list.c |    1 -
 src/terminal-screen.c        |   39 ++++---
 src/terminal.c               |  165 ++++++++++++++++--------------
 12 files changed, 509 insertions(+), 280 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 6a410d0..71d5d7f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -38,7 +38,7 @@ AM_GLIB_GNU_GETTEXT
 
 GLIB_REQUIRED=2.42.0
 GLIB_MIN_REQUIRED=2.42
-GLIB_MAX_ALLOWED=2.42
+GLIB_MAX_ALLOWED=2.50
 
 GIO_REQUIRED=2.34.0
 GSETTINGS_DESKTOP_SCHEMAS_REQUIRED=0.1.0
diff --git a/src/terminal-app.c b/src/terminal-app.c
index a98ea82..5e782d9 100644
--- a/src/terminal-app.c
+++ b/src/terminal-app.c
@@ -975,29 +975,104 @@ TerminalScreen *
 terminal_app_get_screen_by_uuid (TerminalApp *app,
                                  const char  *uuid)
 {
+  g_return_val_if_fail (TERMINAL_IS_APP (app), NULL);
+
   return g_hash_table_lookup (app->screen_map, uuid);
 }
 
+char *
+terminal_app_dup_screen_object_path (TerminalApp *app,
+                                     TerminalScreen *screen)
+{
+  char *object_path = g_strdup_printf (TERMINAL_RECEIVER_OBJECT_PATH_FORMAT,
+                                       terminal_screen_get_uuid (screen));
+  object_path = g_strdelimit (object_path,  "-", '_');
+  g_assert (g_variant_is_object_path (object_path));
+  return object_path;
+}
+
+/**
+ * terminal_app_get_receiver_impl_by_object_path:
+ * @app:
+ * @object_path:
+ *
+ * Returns: (transfer full): the #TerminalReceiverImpl for @object_path, or %NULL
+ */
+static TerminalReceiverImpl *
+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);
+  if (skeleton == NULL || !TERMINAL_IS_OBJECT_SKELETON (skeleton))
+    return NULL;
+
+  TerminalReceiverImpl *impl = NULL;
+  g_object_get (skeleton, "receiver", &impl, NULL);
+  if (impl == NULL)
+    return NULL;
+
+  g_assert (TERMINAL_IS_RECEIVER_IMPL (impl));
+  return impl;
+}
+
+/**
+ * terminal_app_get_screen_by_object_path:
+ * @app:
+ * @object_path:
+ *
+ * Returns: (transfer full): the #TerminalScreen for @object_path, or %NULL
+ */
+TerminalScreen *
+terminal_app_get_screen_by_object_path (TerminalApp *app,
+                                        const char *object_path)
+{
+  gs_unref_object TerminalReceiverImpl *impl =
+    terminal_app_get_receiver_impl_by_object_path (app, object_path);
+  if (impl == NULL)
+    return NULL;
+
+  return terminal_receiver_impl_get_screen (impl);
+}
+
 void
 terminal_app_register_screen (TerminalApp *app,
                               TerminalScreen *screen)
 {
-  const char *uuid;
-
-  uuid = terminal_screen_get_uuid (screen);
+  const char *uuid = terminal_screen_get_uuid (screen);
   g_hash_table_insert (app->screen_map, g_strdup (uuid), screen);
+
+  gs_free char *object_path = terminal_app_dup_screen_object_path (app, screen);
+  TerminalObjectSkeleton *skeleton = terminal_object_skeleton_new (object_path);
+
+  TerminalReceiverImpl *impl = terminal_receiver_impl_new (screen);
+  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_SKELETON (skeleton));
 }
 
 void
 terminal_app_unregister_screen (TerminalApp *app,
                                 TerminalScreen *screen)
 {
-  gboolean found;
-  const char *uuid;
+  const char *uuid = terminal_screen_get_uuid (screen);
+  gboolean found = g_hash_table_remove (app->screen_map, uuid);
+  g_warn_if_fail (found);
+  if (!found)
+    return; /* repeat unregistering */
+
+  gs_free char *object_path = terminal_app_dup_screen_object_path (app, screen);
+  gs_unref_object TerminalReceiverImpl *impl =
+    terminal_app_get_receiver_impl_by_object_path (app, object_path);
+  g_warn_if_fail (impl != NULL);
+
+  if (impl != NULL)
+    terminal_receiver_impl_unset_screen (impl);
 
-  uuid = terminal_screen_get_uuid (screen);
-  found = g_hash_table_remove (app->screen_map, uuid);
-  g_assert (found == TRUE);
+  g_dbus_object_manager_server_unexport (app->object_manager, object_path);
 }
 
 GdkAtom *
diff --git a/src/terminal-app.h b/src/terminal-app.h
index 89d405c..c88fe93 100644
--- a/src/terminal-app.h
+++ b/src/terminal-app.h
@@ -79,9 +79,15 @@ TerminalScreen *terminal_app_new_terminal (TerminalApp     *app,
                                            char           **child_env,
                                            double           zoom);
 
+char *terminal_app_dup_screen_object_path (TerminalApp *app,
+                                           TerminalScreen *screen);
+
 TerminalScreen *terminal_app_get_screen_by_uuid (TerminalApp *app,
                                                  const char  *uuid);
 
+TerminalScreen *terminal_app_get_screen_by_object_path (TerminalApp *app,
+                                                        const char *object_path);
+
 void terminal_app_register_screen (TerminalApp *app,
                                    TerminalScreen *screen);
 
diff --git a/src/terminal-client-utils.c b/src/terminal-client-utils.c
index 2ab2feb..4ecff7b 100644
--- a/src/terminal-client-utils.c
+++ b/src/terminal-client-utils.c
@@ -22,6 +22,7 @@
 #include "config.h"
 
 #include "terminal-client-utils.h"
+#include "terminal-defines.h"
 #include "terminal-libgsystem.h"
 
 #include <string.h>
@@ -47,7 +48,7 @@
  *
  * Appends common options to @builder.
  */
-void 
+void
 terminal_client_append_create_instance_options (GVariantBuilder *builder,
                                                 const char      *display_name,
                                                 const char      *startup_id,
@@ -106,7 +107,7 @@ terminal_client_append_create_instance_options (GVariantBuilder *builder,
  *
  * Appends the environment and the working directory to @builder.
  */
-void 
+void
 terminal_client_append_exec_options (GVariantBuilder *builder,
                                      const char      *working_directory,
                                      PassFdElement   *fd_array,
@@ -127,6 +128,8 @@ terminal_client_append_exec_options (GVariantBuilder *builder,
   envv = g_environ_unsetenv (envv, "TERM");
   envv = g_environ_unsetenv (envv, "VTE_VERSION");
   envv = g_environ_unsetenv (envv, "WINDOWID");
+  envv = g_environ_unsetenv (envv, TERMINAL_ENV_SERVICE_NAME);
+  envv = g_environ_unsetenv (envv, TERMINAL_ENV_SCREEN);
 
   g_variant_builder_add (builder, "{sv}",
                          "environ",
diff --git a/src/terminal-defines.h b/src/terminal-defines.h
index 7e2dc0b..b9ffba2 100644
--- a/src/terminal-defines.h
+++ b/src/terminal-defines.h
@@ -35,11 +35,14 @@ enum {
 #define TERMINAL_FACTORY_OBJECT_PATH            TERMINAL_OBJECT_PATH_PREFIX "/Factory0"
 #define TERMINAL_FACTORY_INTERFACE_NAME         TERMINAL_OBJECT_INTERFACE_PREFIX ".Factory0"
 
-#define TERMINAL_RECEIVER_OBJECT_PATH_FORMAT    TERMINAL_OBJECT_PATH_PREFIX "/window/%u/terminal/%s"
+#define TERMINAL_RECEIVER_OBJECT_PATH_FORMAT    TERMINAL_OBJECT_PATH_PREFIX "/screen/%s"
 #define TEMRINAL_RECEIVER_INTERFACE_NAME        TERMINAL_OBJECT_INTERFACE_PREFIX ".Terminal0"
 
 #define TERMINAL_SEARCH_PROVIDER_PATH           TERMINAL_OBJECT_PATH_PREFIX "/SearchProvider"
 
+#define TERMINAL_ENV_SERVICE_NAME               "GNOME_TERMINAL_SERVICE"
+#define TERMINAL_ENV_SCREEN                     "GNOME_TERMINAL_SCREEN"
+
 G_END_DECLS
 
 #endif /* !TERMINAL_DEFINES_H */
diff --git a/src/terminal-gdbus.c b/src/terminal-gdbus.c
index 82405ff..10ba48e 100644
--- a/src/terminal-gdbus.c
+++ b/src/terminal-gdbus.c
@@ -28,6 +28,7 @@
 #include "terminal-mdi-container.h"
 #include "terminal-util.h"
 #include "terminal-window.h"
+#include "terminal-libgsystem.h"
 
 /* ------------------------------------------------------------------------- */
 
@@ -44,17 +45,6 @@ enum {
 
 /* helper functions */
 
-static char *
-get_object_path_for_screen (TerminalWindow *window,
-                            TerminalScreen *screen)
-{
-  return g_strdelimit (g_strdup_printf (TERMINAL_RECEIVER_OBJECT_PATH_FORMAT,
-                                        gtk_application_window_get_id (GTK_APPLICATION_WINDOW (window)),
-                                        terminal_screen_get_uuid (screen)),
-                       "-", '_');
-
-}
-
 static void
 child_exited_cb (VteTerminal *terminal,
                  int exit_code,
@@ -65,7 +55,7 @@ child_exited_cb (VteTerminal *terminal,
 
 static void
 terminal_receiver_impl_set_screen (TerminalReceiverImpl *impl,
-                                TerminalScreen *screen)
+                                   TerminalScreen *screen)
 {
   TerminalReceiverImplPrivate *priv;
 
@@ -85,11 +75,8 @@ terminal_receiver_impl_set_screen (TerminalReceiverImpl *impl,
   priv->screen = screen;
   if (screen) {
     g_signal_connect (screen, "child-exited",
-                      G_CALLBACK (child_exited_cb), 
+                      G_CALLBACK (child_exited_cb),
                       impl);
-    g_signal_connect_swapped (screen, "destroy",
-                              G_CALLBACK (_terminal_receiver_impl_unset_screen), 
-                              impl);
   }
 
   g_object_notify (G_OBJECT (impl), "screen");
@@ -97,7 +84,7 @@ terminal_receiver_impl_set_screen (TerminalReceiverImpl *impl,
 
 /* Class implementation */
 
-static gboolean 
+static gboolean
 terminal_receiver_impl_exec (TerminalReceiver *receiver,
                              GDBusMethodInvocation *invocation,
                              GUnixFDList *fd_list,
@@ -292,15 +279,15 @@ terminal_receiver_impl_class_init (TerminalReceiverImplClass *klass)
 TerminalReceiverImpl *
 terminal_receiver_impl_new (TerminalScreen *screen)
 {
-  return g_object_new (TERMINAL_TYPE_RECEIVER_IMPL, 
-                       "screen", screen, 
+  return g_object_new (TERMINAL_TYPE_RECEIVER_IMPL,
+                       "screen", screen,
                        NULL);
 }
 
 /**
  * terminal_receiver_impl_get_screen:
  * @impl: a #TerminalReceiverImpl
- * 
+ *
  * Returns: (transfer none): the impl's #TerminalScreen, or %NULL
  */
 TerminalScreen *
@@ -312,13 +299,13 @@ terminal_receiver_impl_get_screen (TerminalReceiverImpl *impl)
 }
 
 /**
- * terminal_receiver_impl_get_screen:
+ * terminal_receiver_impl_unget_screen:
  * @impl: a #TerminalReceiverImpl
- * 
+ *
  * Unsets the impls #TerminalScreen.
  */
 void
-_terminal_receiver_impl_unset_screen (TerminalReceiverImpl *impl)
+terminal_receiver_impl_unset_screen (TerminalReceiverImpl *impl)
 {
   g_return_if_fail (TERMINAL_IS_RECEIVER_IMPL (impl));
 
@@ -334,86 +321,70 @@ struct _TerminalFactoryImplPrivate {
   gpointer dummy;
 };
 
-#define RECEIVER_IMPL_SKELETON_DATA_KEY  "terminal-object-skeleton"
-
-static void
-screen_destroy_cb (GObject *screen,
-                   gpointer user_data)
-{
-  GDBusObjectManagerServer *object_manager;
-  GDBusObjectSkeleton *skeleton;
-  const char *object_path;
-
-  skeleton = g_object_get_data (screen, RECEIVER_IMPL_SKELETON_DATA_KEY);
-  if (skeleton == NULL)
-    return;
-
-  object_manager = terminal_app_get_object_manager (terminal_app_get ());
-  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (skeleton));
-  g_dbus_object_manager_server_unexport (object_manager, object_path);
-  g_object_set_data (screen, RECEIVER_IMPL_SKELETON_DATA_KEY, NULL);
-}
-
 static gboolean
 terminal_factory_impl_create_instance (TerminalFactory *factory,
                                        GDBusMethodInvocation *invocation,
                                        GVariant *options)
 {
   TerminalApp *app = terminal_app_get ();
-  TerminalSettingsList *profiles_list;
-  GDBusObjectManagerServer *object_manager;
-  TerminalWindow *window;
-  TerminalScreen *screen;
-  TerminalReceiverImpl *impl;
-  TerminalObjectSkeleton *skeleton;
-  char *object_path;
-  GSettings *profile = NULL;
-  const char *profile_uuid, *title, *encoding;
-  gboolean zoom_set = FALSE;
-  gdouble zoom = 1.0;
-  guint window_id;
-  gboolean show_menubar;
-  gboolean active;
-  gboolean have_new_window, present_window, present_window_set;
-  GError *err = NULL;
-
-  /* Look up the profile */
-  if (!g_variant_lookup (options, "profile", "&s", &profile_uuid))
-    profile_uuid = NULL;
 
-  if (!g_variant_lookup (options, "encoding", "&s", &encoding))
-    encoding = NULL; /* use profile encoding */
+  /* If a parent screen is specified, use that to fill in missing information */
+  TerminalScreen *parent_screen = NULL;
+  const char *parent_screen_object_path;
+  if (g_variant_lookup (options, "parent-screen", "&o", &parent_screen_object_path)) {
+    parent_screen = terminal_app_get_screen_by_object_path (app, parent_screen_object_path);
+    if (parent_screen == NULL) {
+      g_dbus_method_invocation_return_error (invocation,
+                                             G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                             "Failed to get screen from object path %s",
+                                             parent_screen_object_path);
+      return TRUE;
+    }
+  }
 
-  profiles_list = terminal_app_get_profiles_list (app);
-  profile = terminal_profiles_list_ref_profile_by_uuid (profiles_list, profile_uuid, &err);
-  if (profile == NULL)
-    {
-      g_dbus_method_invocation_return_gerror (invocation, err);
-      g_error_free (err);
-      goto out;
+  /* Try getting a parent window, first by parent screen then by window ID;
+   * if that fails, create a new window.
+   */
+  TerminalWindow *window = NULL;
+  gboolean have_new_window = FALSE;
+  const char *window_from_screen_object_path;
+  if (g_variant_lookup (options, "window-from-screen", "&o", &window_from_screen_object_path)) {
+    TerminalScreen *window_screen =
+      terminal_app_get_screen_by_object_path (app, window_from_screen_object_path);
+    if (window_screen == NULL) {
+      g_dbus_method_invocation_return_error (invocation,
+                                             G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+                                             "Failed to get screen from object path %s",
+                                             parent_screen_object_path);
+      return TRUE;
     }
 
-  if (g_variant_lookup (options, "window-id", "u", &window_id)) {
-    GtkWindow *win;
+    GtkWidget *win = gtk_widget_get_toplevel (GTK_WIDGET (window_screen));
+    if (TERMINAL_IS_WINDOW (win))
+      window = TERMINAL_WINDOW (win);
+  }
 
-    win = gtk_application_get_window_by_id (GTK_APPLICATION (app), window_id);
+  /* Support old client */
+  guint window_id;
+  if (window == NULL && g_variant_lookup (options, "window-id", "u", &window_id)) {
+    GtkWindow *win = gtk_application_get_window_by_id (GTK_APPLICATION (app), window_id);
 
     if (!TERMINAL_IS_WINDOW (win)) {
       g_dbus_method_invocation_return_error (invocation,
                                              G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
                                              "Nonexisting window %u referenced",
                                              window_id);
-      goto out;
+      return TRUE;
     }
 
     window = TERMINAL_WINDOW (win);
-    have_new_window = FALSE;
-  } else {
+  }
+
+  /* Still no parent window? Create a new one */
+  if (window == NULL) {
     const char *startup_id, *role;
     gboolean start_maximized, start_fullscreen;
 
-    /* Create a new window */
-
     /* We don't do multi-display anymore */
 #if 0
     const char *display_name;
@@ -429,8 +400,8 @@ terminal_factory_impl_create_instance (TerminalFactory *factory,
 #endif
 
     int monitor = 0;
-
     window = terminal_app_new_window (app, monitor);
+    have_new_window = TRUE;
 
     if (g_variant_lookup (options, "desktop-startup-id", "^&ay", &startup_id))
       gtk_window_set_startup_id (GTK_WINDOW (window), startup_id);
@@ -439,6 +410,7 @@ terminal_factory_impl_create_instance (TerminalFactory *factory,
     if (g_variant_lookup (options, "role", "&s", &role))
       gtk_window_set_role (GTK_WINDOW (window), role);
 
+    gboolean show_menubar;
     if (g_variant_lookup (options, "show-menubar", "b", &show_menubar))
       terminal_window_set_menubar_visible (window, show_menubar);
 
@@ -454,43 +426,62 @@ terminal_factory_impl_create_instance (TerminalFactory *factory,
     have_new_window = TRUE;
   }
 
-  g_assert (window != NULL);
+  g_assert_nonnull (window);
 
+  const char *title;
   if (!g_variant_lookup (options, "title", "&s", &title))
     title = NULL;
-  if (g_variant_lookup (options, "zoom", "d", &zoom))
-    zoom_set = TRUE;
 
-  screen = terminal_screen_new (profile, encoding, NULL, title, NULL, NULL,
-                                zoom_set ? zoom : 1.0);
-  terminal_window_add_screen (window, screen, -1);
+  double zoom;
+  if (!g_variant_lookup (options, "zoom", "d", &zoom)) {
+    if (parent_screen != NULL)
+      zoom = vte_terminal_get_font_scale (VTE_TERMINAL (parent_screen));
+    else
+      zoom = 1.0;
+  }
 
-  object_path = get_object_path_for_screen (window, screen);
-  g_assert (g_variant_is_object_path (object_path));
+  const char *encoding;
+  if (!g_variant_lookup (options, "encoding", "&s", &encoding)) {
+    if (parent_screen != NULL)
+      encoding = vte_terminal_get_encoding (VTE_TERMINAL (parent_screen));
+    else
+      encoding = NULL; /* use profile encoding */
+  }
 
-  skeleton = terminal_object_skeleton_new (object_path);
-  impl = terminal_receiver_impl_new (screen);
-  terminal_object_skeleton_set_receiver (skeleton, TERMINAL_RECEIVER (impl));
-  g_object_unref (impl);
+  /* Look up the profile */
+  gs_unref_object GSettings *profile = NULL;
+  const char *profile_uuid;
+  if (!g_variant_lookup (options, "profile", "&s", &profile_uuid))
+    profile_uuid = NULL;
 
-  object_manager = terminal_app_get_object_manager (app);
-  g_dbus_object_manager_server_export (object_manager, G_DBUS_OBJECT_SKELETON (skeleton));
-  g_object_set_data_full (G_OBJECT (screen), RECEIVER_IMPL_SKELETON_DATA_KEY,
-                          skeleton, (GDestroyNotify) g_object_unref);
-  g_signal_connect (screen, "destroy",
-                    G_CALLBACK (screen_destroy_cb), app);
+  if (profile_uuid == NULL && parent_screen != NULL) {
+    profile = terminal_screen_ref_profile (parent_screen);
+  } else {
+    GError *err = NULL;
+    profile = terminal_profiles_list_ref_profile_by_uuid (terminal_app_get_profiles_list (app),
+                                                          profile_uuid /* default if NULL */,
+                                                          &err);
+    if (profile == NULL) {
+      g_dbus_method_invocation_return_gerror (invocation, err);
+      g_error_free (err);
+      return TRUE;
+    }
+  }
+
+  g_assert_nonnull (profile);
+
+  /* Now we can create the new screen */
+  TerminalScreen *screen = terminal_screen_new (profile, encoding, NULL, title, NULL, NULL, zoom);
+  terminal_window_add_screen (window, screen, -1);
 
+  /* Apply window properties */
+  gboolean active;
   if (g_variant_lookup (options, "active", "b", &active) &&
       active) {
     terminal_window_switch_screen (window, screen);
     gtk_widget_grab_focus (GTK_WIDGET (screen));
   }
 
-  if (g_variant_lookup (options, "present-window", "b", &present_window))
-    present_window_set = TRUE;
-  else
-    present_window_set = FALSE;
-
   if (have_new_window) {
     const char *geometry;
 
@@ -500,17 +491,15 @@ terminal_factory_impl_create_instance (TerminalFactory *factory,
                              "Invalid geometry string \"%s\"", geometry);
   }
 
+  gboolean present_window;
+  gboolean present_window_set = g_variant_lookup (options, "present-window", "b", &present_window);
+
   if (have_new_window || (present_window_set && present_window))
     gtk_window_present (GTK_WINDOW (window));
 
+  gs_free char *object_path = terminal_app_dup_screen_object_path (app, screen);
   terminal_factory_complete_create_instance (factory, invocation, object_path);
 
-  g_free (object_path);
-
-out:
-  if (profile)
-    g_object_unref (profile);
-
   return TRUE; /* handled */
 }
 
@@ -532,7 +521,7 @@ terminal_factory_impl_init (TerminalFactoryImpl *impl)
 static void
 terminal_factory_impl_class_init (TerminalFactoryImplClass *klass)
 {
-  g_type_class_add_private (klass, sizeof (TerminalFactoryImplPrivate));
+  /* g_type_class_add_private (klass, sizeof (TerminalFactoryImplPrivate)); */
 }
 
 /**
diff --git a/src/terminal-gdbus.h b/src/terminal-gdbus.h
index ea57c71..16e80d7 100644
--- a/src/terminal-gdbus.h
+++ b/src/terminal-gdbus.h
@@ -55,7 +55,7 @@ TerminalReceiverImpl *terminal_receiver_impl_new (TerminalScreen *screen);
 
 TerminalScreen *terminal_receiver_impl_get_screen (TerminalReceiverImpl *impl);
 
-void _terminal_receiver_impl_unset_screen (TerminalReceiverImpl *impl);
+void terminal_receiver_impl_unset_screen (TerminalReceiverImpl *impl);
 
 /* ------------------------------------------------------------------------- */
 
diff --git a/src/terminal-options.c b/src/terminal-options.c
index 478345e..4d74a26 100644
--- a/src/terminal-options.c
+++ b/src/terminal-options.c
@@ -3,7 +3,7 @@
  * Copyright © 2002 Red Hat, Inc.
  * Copyright © 2002 Sun Microsystems
  * Copyright © 2003 Mariano Suarez-Alvarez
- * Copyright © 2008 Christian Persch
+ * Copyright © 2008, 2017 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
@@ -31,6 +31,7 @@
 
 #include "terminal-options.h"
 #include "terminal-client-utils.h"
+#include "terminal-defines.h"
 #include "terminal-screen.h"
 #include "terminal-app.h"
 #include "terminal-util.h"
@@ -39,21 +40,50 @@
 
 static int verbosity = 1;
 
+static char * G_GNUC_FORMAT (1)
+format_as_comment (const char *format)
+{
+  return g_strdup_printf ("# %s", format);
+}
+
 void
 terminal_fprintf (FILE* fp,
                   int verbosity_level,
-                  char const* format,
+                  const char* format,
                   ...)
 {
         if (verbosity < verbosity_level)
                 return;
 
+        gs_free char *commented_format = format_as_comment (format);
         va_list args;
         va_start(args, format);
-        g_vfprintf(fp, format, args);
+        g_vfprintf(fp, commented_format, args);
         va_end(args);
 }
 
+#if GLIB_CHECK_VERSION (2, 50, 0)
+
+/* Need to install a special log writer so we never output
+ * anything without the '# ' prepended, in case --print-environment
+ * is used.
+ */
+GLogWriterOutput
+terminal_log_writer (GLogLevelFlags log_level,
+                     const GLogField *fields,
+                     gsize n_fields,
+                     gpointer user_data)
+{
+  for (gsize i = 0; i < n_fields; i++) {
+    if (g_str_equal (fields[i].key, "MESSAGE"))
+      terminal_printerr ("%s\n", (const char*)fields[i].value);
+  }
+
+  return G_LOG_WRITER_HANDLED;
+}
+
+#endif /* GLIB 2.50 */
+
 static GOptionContext *get_goption_context (TerminalOptions *options);
 
 static TerminalSettingsList *
@@ -163,7 +193,7 @@ initial_window_free (InitialWindow *iw)
 
 static void
 apply_defaults (TerminalOptions *options,
-                InitialWindow        *iw)
+                InitialWindow *iw)
 {
   if (options->default_role)
     {
@@ -187,24 +217,34 @@ apply_defaults (TerminalOptions *options,
 }
 
 static InitialWindow*
-ensure_top_window (TerminalOptions *options)
+add_new_window (TerminalOptions *options,
+                char *profile /* adopts */,
+                gboolean implicit_if_first_window)
 {
   InitialWindow *iw;
 
-  if (options->initial_windows == NULL)
-    {
-      iw = initial_window_new (0);
-      iw->tabs = g_list_append (NULL, initial_tab_new (NULL));
-      apply_defaults (options, iw);
+  iw = initial_window_new (0);
+  iw->implicit_first_window = (options->initial_windows == NULL) && implicit_if_first_window;
+  iw->tabs = g_list_prepend (NULL, initial_tab_new (profile));
+  apply_defaults (options, iw);
 
-      options->initial_windows = g_list_append (options->initial_windows, iw);
-    }
+
+  options->initial_windows = g_list_append (options->initial_windows, iw);
+  return iw;
+}
+
+static InitialWindow*
+ensure_top_window (TerminalOptions *options,
+                   gboolean implicit_if_first_window)
+{
+  InitialWindow *iw;
+
+  if (options->initial_windows == NULL)
+    iw = add_new_window (options, NULL /* profile */, implicit_if_first_window);
   else
-    {
       iw = g_list_last (options->initial_windows)->data;
-    }
 
-  g_assert (iw->tabs);
+  g_assert_nonnull (iw->tabs);
 
   return iw;
 }
@@ -215,30 +255,15 @@ ensure_top_tab (TerminalOptions *options)
   InitialWindow *iw;
   InitialTab *it;
 
-  iw = ensure_top_window (options);
+  iw = ensure_top_window (options, TRUE);
 
-  g_assert (iw->tabs);
+  g_assert_nonnull (iw->tabs);
 
   it = g_list_last (iw->tabs)->data;
 
   return it;
 }
 
-static InitialWindow*
-add_new_window (TerminalOptions *options,
-                char            *profile /* adopts */)
-{
-  InitialWindow *iw;
-
-  iw = initial_window_new (0);
-  iw->tabs = g_list_prepend (NULL, initial_tab_new (profile));
-  apply_defaults (options, iw);
-
-  options->initial_windows = g_list_append (options->initial_windows, iw);
-
-  return iw;
-}
-
 /* handle deprecated command line options */
 
 static void
@@ -453,22 +478,24 @@ option_window_callback (const gchar *option_name,
   TerminalOptions *options = data;
   char *profile;
 
-  profile = terminal_profiles_list_dup_uuid_or_name (terminal_options_ensure_profiles_list (options),
-                                                     value, error);
+  if (value != NULL) {
+    profile = terminal_profiles_list_dup_uuid_or_name (terminal_options_ensure_profiles_list (options),
+                                                       value, error);
 
-  if (value && profile == NULL)
-  {
+    if (value && profile == NULL) {
       terminal_printerr ("Profile '%s' specified but not found. Attempting to fall back "
                          "to the default profile.\n", value);
       g_clear_error (error);
       profile = terminal_profiles_list_dup_uuid_or_name (terminal_options_ensure_profiles_list (options),
                                                          NULL, error);
-  }
+    }
 
-  if (profile == NULL)
-    return FALSE;
+    if (profile == NULL)
+      return FALSE;
+  } else
+    profile = NULL;
 
-  add_new_window (options, profile /* adopts */);
+  add_new_window (options, profile /* adopts */, FALSE);
 
   return TRUE;
 }
@@ -482,10 +509,13 @@ option_tab_callback (const gchar *option_name,
   TerminalOptions *options = data;
   char *profile;
 
-  profile = terminal_profiles_list_dup_uuid_or_name (terminal_options_ensure_profiles_list (options),
-                                                     value, error);
-  if (profile == NULL)
-    return FALSE;
+  if (value != NULL) {
+    profile = terminal_profiles_list_dup_uuid_or_name (terminal_options_ensure_profiles_list (options),
+                                                       value, error);
+    if (profile == NULL)
+      return FALSE;
+  } else
+    profile = NULL;
 
   if (options->initial_windows)
     {
@@ -495,7 +525,7 @@ option_tab_callback (const gchar *option_name,
       iw->tabs = g_list_append (iw->tabs, initial_tab_new (profile /* adopts */));
     }
   else
-    add_new_window (options, profile /* adopts */);
+    add_new_window (options, profile /* adopts */, TRUE);
 
   return TRUE;
 }
@@ -923,8 +953,6 @@ digest_options_callback (GOptionContext *context,
 
 /**
  * terminal_options_parse:
- * @working_directory: the default working directory
- * @startup_id: the startup notification ID
  * @argcp: (inout) address of the argument count. Changed if any arguments were handled
  * @argvp: (inout) address of the argument vector. Any parameters understood by
  *   the terminal #GOptionContext are removed
@@ -936,9 +964,7 @@ digest_options_callback (GOptionContext *context,
  *   or %NULL on error.
  */
 TerminalOptions *
-terminal_options_parse (const char *working_directory,
-                        const char *startup_id,
-                        int *argcp,
+terminal_options_parse (int *argcp,
                         char ***argvp,
                         GError **error)
 {
@@ -950,13 +976,14 @@ terminal_options_parse (const char *working_directory,
 
   options = g_new0 (TerminalOptions, 1);
 
-  options->remote_arguments = FALSE;
+  options->print_environment = FALSE;
   options->default_window_menubar_forced = FALSE;
   options->default_window_menubar_state = TRUE;
   options->default_fullscreen = FALSE;
   options->default_maximize = FALSE;
   options->execute = FALSE;
 
+  const char *startup_id = g_getenv ("DESKTOP_STARTUP_ID");
   options->startup_id = g_strdup (startup_id && startup_id[0] ? startup_id : NULL);
   options->display_name = NULL;
   options->initial_windows = NULL;
@@ -967,7 +994,28 @@ terminal_options_parse (const char *working_directory,
   options->zoom_set = FALSE;
   options->any_wait = FALSE;
 
-  options->default_working_dir = g_strdup (working_directory);
+  options->default_working_dir = g_get_current_dir ();
+
+  /* Collect info from gnome-terminal private env vars */
+  const char *server_unique_name = g_getenv (TERMINAL_ENV_SERVICE_NAME);
+  if (server_unique_name != NULL) {
+    if (g_dbus_is_unique_name (server_unique_name))
+      options->server_unique_name = g_strdup (server_unique_name);
+    else
+      terminal_printerr ("# Warning: %s set but \"%s\" is not a unique D-Bus name.\n",
+                         TERMINAL_ENV_SERVICE_NAME,
+                         server_unique_name);
+  }
+
+  const char *parent_screen_object_path = g_getenv (TERMINAL_ENV_SCREEN);
+  if (parent_screen_object_path != NULL) {
+    if (g_variant_is_object_path (parent_screen_object_path))
+      options->parent_screen_object_path = g_strdup (parent_screen_object_path);
+    else
+      terminal_printerr ("# Warning: %s set but \"%s\" is not a valid D-Bus object path.\n",
+                         TERMINAL_ENV_SCREEN,
+                         parent_screen_object_path);
+  }
 
   /* The old -x/--execute option is broken, so we need to pre-scan for it. */
   /* We now also support passing the command after the -- switch. */
@@ -1014,6 +1062,13 @@ terminal_options_parse (const char *working_directory,
     return NULL;
   }
 
+  /* Do this here so that gdk_display is initialized */
+  if (options->startup_id == NULL)
+    options->startup_id = terminal_client_get_fallback_startup_id ();
+  /* Still NULL? */
+  if (options->startup_id == NULL)
+    terminal_printerr_detail ("Warning: DESKTOP_STARTUP_ID not set and no fallback available.\n");
+
   return options;
 }
 
@@ -1150,7 +1205,30 @@ terminal_options_merge_config (TerminalOptions *options,
 void
 terminal_options_ensure_window (TerminalOptions *options)
 {
-  ensure_top_window (options);
+  ensure_top_window (options, FALSE);
+}
+
+/**
+ * terminal_options_get_service_name:
+ * @options:
+ *
+ * Returns the DBus service name of the terminal server.
+ *
+ * Returns: (transfer none): the DBus service name of the terminal server to use
+ */
+const char *
+terminal_options_get_service_name (TerminalOptions *options)
+{
+  /* Prefer an explicitly specified --app-id */
+  if (options->server_app_id != NULL)
+    return options->server_app_id;
+
+  /* If that's not set, use the env var, if set */
+  if (options->server_unique_name != NULL)
+    return options->server_unique_name;
+
+  /* Finally fall back to the default */
+  return TERMINAL_APPLICATION_ID;
 }
 
 /**
@@ -1173,6 +1251,9 @@ terminal_options_free (TerminalOptions *options)
 
   g_strfreev (options->exec_argv);
 
+  g_free (options->server_unique_name);
+  g_free (options->parent_screen_object_path);
+
   g_free (options->display_name);
   g_free (options->startup_id);
   g_free (options->server_app_id);
@@ -1224,10 +1305,51 @@ get_goption_context (TerminalOptions *options)
       unsupported_option_callback,
       NULL, NULL
     },
-    { "preferences", 0, 0, G_OPTION_ARG_NONE, &options->show_preferences, N_("Show preferences window"), 
NULL },
-    { "version", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, option_version_cb, 
NULL, NULL },
-    { "verbose", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_verbosity_cb, N_("Increase 
diagnostic verbosity"), NULL },
-    { "quiet", 'q', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, option_verbosity_cb, N_("Suppress output"), 
NULL },
+    {
+      "preferences",
+      0,
+      0,
+      G_OPTION_ARG_NONE,
+      &options->show_preferences,
+      N_("Show preferences window"),
+      NULL
+    },
+    {
+      "print-environment",
+      'p',
+      0,
+      G_OPTION_ARG_NONE,
+      &options->print_environment,
+      N_("Print environment variables to interact with the terminal"),
+      NULL
+    },
+    {
+      "version",
+      0,
+      G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN,
+      G_OPTION_ARG_CALLBACK,
+      option_version_cb,
+      NULL,
+      NULL
+    },
+    {
+      "verbose",
+      'v',
+      G_OPTION_FLAG_NO_ARG,
+      G_OPTION_ARG_CALLBACK,
+      option_verbosity_cb,
+      N_("Increase diagnostic verbosity"),
+      NULL
+    },
+    {
+      "quiet",
+      'q',
+      G_OPTION_FLAG_NO_ARG,
+      G_OPTION_ARG_CALLBACK,
+      option_verbosity_cb,
+      N_("Suppress output"),
+      NULL
+    },
     { NULL, 0, 0, 0, NULL, NULL, NULL }
   };
 
diff --git a/src/terminal-options.h b/src/terminal-options.h
index 85ee130..5f7c01e 100644
--- a/src/terminal-options.h
+++ b/src/terminal-options.h
@@ -65,8 +65,12 @@ typedef struct
 {
   TerminalSettingsList *profiles_list; /* may be NULL */
 
+  gboolean print_environment;
+
+  char    *server_unique_name;
+  char    *parent_screen_object_path;
+
   char    *server_app_id;
-  gboolean remote_arguments;
   char    *startup_id;
   char    *display_name;
   gboolean show_preferences;
@@ -112,6 +116,7 @@ typedef struct
 typedef struct
 {
   guint source_tag;
+  gboolean implicit_first_window;
 
   GList *tabs; /* list of InitialTab */
 
@@ -136,9 +141,7 @@ typedef enum {
   TERMINAL_OPTION_ERROR_INCOMPATIBLE_CONFIG_FILE
 } TerminalOptionError;
 
-TerminalOptions *terminal_options_parse (const char *working_directory,
-                                         const char *startup_id,
-                                         int *argcp,
+TerminalOptions *terminal_options_parse (int *argcp,
                                          char ***argvp,
                                          GError **error);
 
@@ -149,7 +152,9 @@ gboolean terminal_options_merge_config (TerminalOptions *options,
 
 void terminal_options_ensure_window (TerminalOptions *options);
 
-void terminal_options_free (TerminalOptions *options);
+const char *terminal_options_get_service_name (TerminalOptions *options);
+
+  void terminal_options_free (TerminalOptions *options);
 
 typedef enum {
   TERMINAL_VERBOSITY_QUIET  = 0,
@@ -160,7 +165,7 @@ typedef enum {
 
 void terminal_fprintf (FILE* fp,
                        int verbosity_level,
-                       char const* format,
+                       const char* format,
                        ...) G_GNUC_PRINTF(3, 4);
 
 #define terminal_print_level(level,...) terminal_fprintf(stdout, TERMINAL_VERBOSITY_ ## level, __VA_ARGS__)
@@ -174,6 +179,13 @@ void terminal_fprintf (FILE* fp,
 #define terminal_printerr(...) terminal_print_level(NORMAL, __VA_ARGS__)
 #define terminal_printerr_debug(...) terminal_print_level(DEBUG, __VA_ARGS__)
 
+#if GLIB_CHECK_VERSION (2, 50, 0)
+GLogWriterOutput terminal_log_writer (GLogLevelFlags log_level,
+                                      const GLogField *fields,
+                                      gsize n_fields,
+                                      gpointer user_data);
+#endif
+
 G_END_DECLS
 
 #endif /* !TERMINAL_OPTIONS_H */
diff --git a/src/terminal-profiles-list.c b/src/terminal-profiles-list.c
index 721dbff..6a85bc1 100644
--- a/src/terminal-profiles-list.c
+++ b/src/terminal-profiles-list.c
@@ -165,7 +165,6 @@ terminal_profiles_list_ref_profile_by_uuid (TerminalSettingsList *list,
     return NULL;
 
   profile = terminal_settings_list_ref_child (list, profile_uuid);
-  g_assert (profile != NULL);
   return profile;
 }
 
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index b18de53..36d47ce 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -48,6 +48,7 @@
 #include "terminal-accels.h"
 #include "terminal-app.h"
 #include "terminal-debug.h"
+#include "terminal-defines.h"
 #include "terminal-encoding.h"
 #include "terminal-enums.h"
 #include "terminal-intl.h"
@@ -81,6 +82,7 @@ typedef struct
 struct _TerminalScreenPrivate
 {
   char *uuid;
+  gboolean registered; /* D-Bus interface is registered */
 
   GSettings *profile; /* never NULL */
   guint profile_changed_id;
@@ -585,12 +587,12 @@ static void
 terminal_screen_constructed (GObject *object)
 {
   TerminalScreen *screen = TERMINAL_SCREEN (object);
-  TerminalApp *app;
+  TerminalScreenPrivate *priv = screen->priv;
 
   G_OBJECT_CLASS (terminal_screen_parent_class)->constructed (object);
 
-  app = terminal_app_get ();
-  terminal_app_register_screen (app, screen);
+  terminal_app_register_screen (terminal_app_get (), screen);
+  priv->registered = TRUE;
 }
 
 static void
@@ -611,6 +613,11 @@ terminal_screen_dispose (GObject *object)
       priv->launch_child_source_id = 0;
     }
 
+  if (priv->registered) {
+    terminal_app_unregister_screen (terminal_app_get (), screen);
+    priv->registered = FALSE;
+  }
+
   G_OBJECT_CLASS (terminal_screen_parent_class)->dispose (object);
 }
 
@@ -619,10 +626,6 @@ terminal_screen_finalize (GObject *object)
 {
   TerminalScreen *screen = TERMINAL_SCREEN (object);
   TerminalScreenPrivate *priv = screen->priv;
-  TerminalApp *app;
-
-  app = terminal_app_get ();
-  terminal_app_unregister_screen (app, screen);
 
   g_signal_handlers_disconnect_by_func (terminal_app_get_desktop_interface_settings (terminal_app_get ()),
                                         G_CALLBACK (terminal_screen_system_font_changed_cb),
@@ -1038,7 +1041,6 @@ terminal_screen_get_profile (TerminalScreen *screen)
 {
   TerminalScreenPrivate *priv = screen->priv;
 
-  g_assert (priv->profile != NULL);
   return priv->profile;
 }
 
@@ -1047,8 +1049,9 @@ terminal_screen_ref_profile (TerminalScreen *screen)
 {
   TerminalScreenPrivate *priv = screen->priv;
 
-  g_assert (priv->profile != NULL);
-  return g_object_ref (priv->profile);
+  if (priv->profile != NULL)
+    return g_object_ref (priv->profile);
+  return NULL;
 }
 
 static void
@@ -1166,9 +1169,8 @@ get_child_environment (TerminalScreen *screen,
                        const char *cwd,
                        char **shell)
 {
+  TerminalApp *app = terminal_app_get ();
   TerminalScreenPrivate *priv = screen->priv;
-  GtkWidget *term = GTK_WIDGET (screen);
-  GtkWidget *window;
   char **env;
   char *e, *v;
   GHashTable *env_table;
@@ -1176,10 +1178,6 @@ get_child_environment (TerminalScreen *screen,
   GPtrArray *retval;
   guint i;
 
-  window = gtk_widget_get_toplevel (term);
-  g_assert (window != NULL);
-  g_assert (gtk_widget_is_toplevel (window));
-
   env_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
 
   env = priv->initial_env;
@@ -1218,6 +1216,15 @@ get_child_environment (TerminalScreen *screen,
 
   terminal_util_add_proxy_env (env_table);
 
+  /* Add gnome-terminal private env vars used to communicate back to g-t-server */
+  GDBusConnection *connection = g_application_get_dbus_connection (G_APPLICATION (app));
+  g_hash_table_replace (env_table, g_strdup (TERMINAL_ENV_SERVICE_NAME),
+                        g_strdup (g_dbus_connection_get_unique_name (connection)));
+
+  g_hash_table_replace (env_table, g_strdup (TERMINAL_ENV_SCREEN),
+                        terminal_app_dup_screen_object_path (app, screen));
+
+  /* Convert to strv */
   retval = g_ptr_array_sized_new (g_hash_table_size (env_table));
   g_hash_table_iter_init (&iter, env_table);
   while (g_hash_table_iter_next (&iter, (gpointer *) &e, (gpointer *) &v))
diff --git a/src/terminal.c b/src/terminal.c
index 05aef55..d6faaa8 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -36,6 +36,7 @@
 #include <gtk/gtk.h>
 
 #include "terminal-debug.h"
+#include "terminal-defines.h"
 #include "terminal-i18n.h"
 #include "terminal-options.h"
 #include "terminal-gdbus-generated.h"
@@ -91,8 +92,8 @@ run_receiver (TerminalReceiver *receiver)
 /* Factory helpers */
 
 static gboolean
-get_factory_exit_status (const char *message,
-                         const char *service_name,
+get_factory_exit_status (TerminalOptions *options,
+                         const char *message,
                          int *exit_status)
 {
   gs_free char *pattern = NULL, *number = NULL;
@@ -102,7 +103,8 @@ get_factory_exit_status (const char *message,
   char *end;
   GError *err = NULL;
 
-  pattern = g_strdup_printf ("org.freedesktop.DBus.Error.Spawn.ChildExited: Process %s exited with status 
(\\d+)$", service_name);
+  pattern = g_strdup_printf ("org.freedesktop.DBus.Error.Spawn.ChildExited: Process %s exited with status 
(\\d+)$",
+                             terminal_options_get_service_name (options));
   regex = g_regex_new (pattern, 0, 0, &err);
   g_assert_no_error (err);
 
@@ -110,7 +112,7 @@ get_factory_exit_status (const char *message,
     return FALSE;
 
   number = g_match_info_fetch (match_info, 1);
-  g_assert_true (number != NULL);
+  g_assert_nonnull (number);
 
   errno = 0;
   v = g_ascii_strtoll (number, &end, 10);
@@ -122,14 +124,14 @@ get_factory_exit_status (const char *message,
 }
 
 static gboolean
-handle_factory_error (GError *error,
-                      const char *service_name)
+handle_factory_error (TerminalOptions *options,
+                      GError *error)
 {
   int exit_status;
 
   if (!g_dbus_error_is_remote_error (error) ||
       !g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_CHILD_EXITED) ||
-      !get_factory_exit_status (error->message, service_name, &exit_status))
+      !get_factory_exit_status (options, error->message, &exit_status))
     return FALSE;
 
   g_dbus_error_strip_remote_error (error);
@@ -157,10 +159,10 @@ handle_factory_error (GError *error,
 }
 
 static gboolean
-handle_create_instance_error (GError *error,
-                              const char *service_name)
+handle_create_instance_error (TerminalOptions *options,
+                              GError *error)
 {
-  if (handle_factory_error (error, service_name))
+  if (handle_factory_error (options, error))
     return TRUE;
 
   g_dbus_error_strip_remote_error (error);
@@ -169,11 +171,10 @@ handle_create_instance_error (GError *error,
 }
 
 static gboolean
-handle_create_receiver_proxy_error (GError *error,
-                                    const char *service_name,
-                                    const char *object_path)
+handle_create_receiver_proxy_error (TerminalOptions *options,
+                                    GError *error)
 {
-  if (handle_factory_error (error, service_name))
+  if (handle_factory_error (options, error))
     return TRUE;
 
   g_dbus_error_strip_remote_error (error);
@@ -182,10 +183,10 @@ handle_create_receiver_proxy_error (GError *error,
 }
 
 static gboolean
-handle_exec_error (GError *error,
-                   const char *service_name)
+handle_exec_error (TerminalOptions *options,
+                   GError *error)
 {
-  if (handle_factory_error (error, service_name))
+  if (handle_factory_error (options, error))
     return TRUE;
 
   g_dbus_error_strip_remote_error (error);
@@ -194,7 +195,7 @@ handle_exec_error (GError *error,
 }
 
 static void
-handle_show_preferences (const char *service_name)
+handle_show_preferences (TerminalOptions *options)
 {
   gs_free_error GError *error = NULL;
   gs_unref_object GDBusConnection *bus = NULL;
@@ -211,6 +212,7 @@ handle_show_preferences (const char *service_name)
    * is derived from the service name, i.e. for service name
    * "foo.bar.baz" the object path is "/foo/bar/baz".
    */
+  const char *service_name = terminal_options_get_service_name (options);
   object_path = g_strdelimit (g_strdup_printf (".%s", service_name), ".", '/');
 
   g_variant_builder_init (&builder, G_VARIANT_TYPE ("(sava{sv})"));
@@ -251,46 +253,43 @@ handle_show_preferences (const char *service_name)
  *   error
  */
 static gboolean
-handle_options (TerminalFactory *factory,
-                const char *service_name,
-                TerminalOptions *options,
+handle_options (TerminalOptions *options,
+                TerminalFactory *factory,
                 TerminalReceiver **wait_for_receiver)
 {
-  GList *lw;
-  const char *encoding;
 
   /* We need to forward the locale encoding to the server, see bug #732128 */
+  const char *encoding;
   g_get_charset (&encoding);
 
   if (options->show_preferences) {
-    handle_show_preferences (service_name);
+    handle_show_preferences (options);
   } else {
     /* Make sure we open at least one window */
     terminal_options_ensure_window (options);
   }
 
-  for (lw = options->initial_windows;  lw != NULL; lw = lw->next)
+  const char *factory_unique_name = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (factory));
+
+  for (GList *lw = options->initial_windows;  lw != NULL; lw = lw->next)
     {
       InitialWindow *iw = lw->data;
-      GList *lt;
-      guint window_id;
 
-      g_assert (iw->tabs);
+      g_assert_nonnull (iw);
+
+      guint window_id = 0;
 
-      window_id = 0;
+      gs_free char *previous_screen_object_path = NULL;
+      if (iw->implicit_first_window)
+        previous_screen_object_path = g_strdup (options->parent_screen_object_path);
 
       /* Now add the tabs */
-      for (lt = iw->tabs; lt != NULL; lt = lt->next)
+      for (GList *lt = iw->tabs; lt != NULL; lt = lt->next)
         {
           InitialTab *it = lt->data;
-          GVariantBuilder builder;
-          char **argv;
-          int argc;
-          char *p;
-          gs_free_error GError *err = NULL;
-          gs_free char *object_path = NULL;
-          gs_unref_object TerminalReceiver *receiver = NULL;
+          g_assert_nonnull (it);
 
+          GVariantBuilder builder;
           g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
 
           terminal_client_append_create_instance_options (&builder,
@@ -305,10 +304,18 @@ handle_options (TerminalFactory *factory,
                                                           iw->start_maximized,
                                                           iw->start_fullscreen);
 
+          /* This will be used to apply missing defaults */
+          if (options->parent_screen_object_path)
+            g_variant_builder_add (&builder, "{sv}",
+                                   "parent-screen", g_variant_new_object_path 
(options->parent_screen_object_path));
+
+          /* This will be used to get the parent window */
+          if (previous_screen_object_path)
+            g_variant_builder_add (&builder, "{sv}",
+                                   "window-from-screen", g_variant_new_object_path 
(previous_screen_object_path));
           if (window_id)
             g_variant_builder_add (&builder, "{sv}",
                                    "window-id", g_variant_new_uint32 (window_id));
-
           /* Restored windows shouldn't demand attention; see bug #586308. */
           if (iw->source_tag == SOURCE_SESSION)
             g_variant_builder_add (&builder, "{sv}",
@@ -320,19 +327,22 @@ handle_options (TerminalFactory *factory,
             g_variant_builder_add (&builder, "{sv}",
                                    "show-menubar", g_variant_new_boolean (iw->menubar_state));
 
-          if (!terminal_factory_call_create_instance_sync 
+          gs_free_error GError *err = NULL;
+          gs_free char *object_path = NULL;
+          if (!terminal_factory_call_create_instance_sync
                  (factory,
                   g_variant_builder_end (&builder),
                   &object_path,
                   NULL /* cancellable */,
                   &err)) {
-            if (handle_create_instance_error (err, service_name))
+            if (handle_create_instance_error (options, err))
               return FALSE;
             else
               continue; /* Continue processing the remaining options! */
           }
 
-          p = strstr (object_path, "/window/");
+          /* Deprecated and not working on new server anymore */
+          char *p = strstr (object_path, "/window/");
           if (p) {
             char *end = NULL;
             guint64 value;
@@ -344,16 +354,19 @@ handle_options (TerminalFactory *factory,
               window_id = (guint) value;
           }
 
-          receiver = terminal_receiver_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
-                                                               G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-                                                               (it->wait ? 0 : 
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS),
-                                                               options->server_app_id ? 
options->server_app_id
-                                                                                      : 
TERMINAL_APPLICATION_ID,
-                                                               object_path,
-                                                               NULL /* cancellable */,
-                                                               &err);
+          g_free (previous_screen_object_path);
+          previous_screen_object_path = g_strdup (object_path);
+
+          gs_unref_object TerminalReceiver *receiver =
+            terminal_receiver_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+                                                      G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
+                                                      (it->wait ? 0 : 
G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS),
+                                                      factory_unique_name,
+                                                      object_path,
+                                                      NULL /* cancellable */,
+                                                      &err);
           if (receiver == NULL) {
-            if (handle_create_receiver_proxy_error (err, service_name, object_path))
+            if (handle_create_receiver_proxy_error (options, err))
               return FALSE;
             else
               continue; /* Continue processing the remaining options! */
@@ -361,14 +374,14 @@ handle_options (TerminalFactory *factory,
 
           g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
 
-          argv = it->exec_argv ? it->exec_argv : options->exec_argv,
-          argc = argv ? g_strv_length (argv) : 0;
+          char **argv = it->exec_argv ? it->exec_argv : options->exec_argv;
+          int argc = argv ? g_strv_length (argv) : 0;
 
           PassFdElement *fd_array = it->fd_array ? (PassFdElement*)it->fd_array->data : NULL;
           gsize fd_array_len = it->fd_array ? it->fd_array->len : 0;
 
           terminal_client_append_exec_options (&builder,
-                                               it->working_dir ? it->working_dir 
+                                               it->working_dir ? it->working_dir
                                                                : options->default_working_dir,
                                                fd_array, fd_array_len,
                                                argc == 0);
@@ -379,7 +392,7 @@ handle_options (TerminalFactory *factory,
                                                  it->fd_list, NULL /* outfdlist */,
                                                  NULL /* cancellable */,
                                                  &err)) {
-            if (handle_exec_error (err, service_name))
+            if (handle_exec_error (options, err))
               return FALSE;
             else
               continue; /* Continue processing the remaining options! */
@@ -387,6 +400,9 @@ handle_options (TerminalFactory *factory,
 
           if (it->wait)
             gs_transfer_out_value (wait_for_receiver, &receiver);
+
+          if (options->print_environment)
+            g_print ("%s=%s\n", TERMINAL_ENV_SCREEN, object_path);
         }
     }
 
@@ -398,14 +414,18 @@ main (int argc, char **argv)
 {
   int i;
   gs_free char **argv_copy = NULL;
-  const char *startup_id, *display_name;
+  const char *display_name;
   GdkDisplay *display;
   gs_free_options TerminalOptions *options = NULL;
   gs_unref_object TerminalFactory *factory = NULL;
   gs_free_error GError *error = NULL;
-  gs_free char *working_directory = NULL;
   int exit_code = EXIT_FAILURE;
-  const char *service_name;
+
+#if GLIB_CHECK_VERSION (2, 50, 0)
+  g_log_set_writer_func (terminal_log_writer, NULL, NULL);
+#endif
+
+  g_set_prgname ("gterminalclient");
 
   setlocale (LC_ALL, "");
 
@@ -419,14 +439,7 @@ main (int argc, char **argv)
     argv_copy [i] = argv [i];
   argv_copy [i] = NULL;
 
-  startup_id = g_getenv ("DESKTOP_STARTUP_ID");
-
-  working_directory = g_get_current_dir ();
-
-  options = terminal_options_parse (working_directory,
-                                    startup_id,
-                                    &argc, &argv,
-                                    &error);
+  options = terminal_options_parse (&argc, &argv, &error);
   if (options == NULL) {
     terminal_printerr (_("Failed to parse arguments: %s\n"), error->message);
     goto out;
@@ -434,19 +447,11 @@ main (int argc, char **argv)
 
   g_set_application_name (_("Terminal"));
 
-  /* Do this here so that gdk_display is initialized */
-  if (options->startup_id == NULL)
-    options->startup_id = terminal_client_get_fallback_startup_id ();
-  /* Still NULL? */
-  if (options->startup_id == NULL)
-    terminal_printerr_detail ("Warning: DESKTOP_STARTUP_ID not set and no fallback available.\n");
-
   display = gdk_display_get_default ();
   display_name = gdk_display_get_name (display);
   options->display_name = g_strdup (display_name);
 
-  service_name = options->server_app_id ? options->server_app_id : TERMINAL_APPLICATION_ID;
-
+  const char *service_name = terminal_options_get_service_name (options);
   factory = terminal_factory_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
                                                      G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
                                                      G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
@@ -455,15 +460,23 @@ main (int argc, char **argv)
                                                      NULL /* cancellable */,
                                                      &error);
   if (factory == NULL) {
-    if (!handle_factory_error (error, service_name))
+    if (!handle_factory_error (options, error))
       terminal_printerr ("Error constructing proxy for %s:%s: %s\n",
                          service_name, TERMINAL_FACTORY_OBJECT_PATH, error->message);
 
     goto out;
   }
 
+  if (options->print_environment) {
+    const char *name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (factory));
+    if (name_owner != NULL)
+      g_print ("%s=%s\n", TERMINAL_ENV_SERVICE_NAME, name_owner);
+    else
+      goto out;
+  }
+
   TerminalReceiver *receiver = NULL;
-  if (!handle_options (factory, service_name, options, &receiver))
+  if (!handle_options (options, factory, &receiver))
     goto out;
 
   if (receiver != NULL) {


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