[gnome-terminal] gterminal: Initial commit



commit 1bbe740bb91f743032544bde430009e7cbfd418a
Author: Christian Persch <chpe gnome org>
Date:   Sun May 4 11:27:14 2014 +0200

    gterminal: Initial commit

 .dir-locals.el               |    7 +-
 configure.ac                 |   31 ++-
 src/Makefile.am              |   67 +++--
 src/client.c                 |  818 ------------------------------------------
 src/client.vapi              |   44 +++
 src/config.vapi              |    7 +
 src/gterminal.vala           |  581 ++++++++++++++++++++++++++++++
 src/profiles.vapi            |   67 ++++
 src/terminal-client-utils.c  |   37 ++-
 src/terminal-client-utils.h  |   13 +-
 src/terminal-nautilus.c      |    1 +
 src/terminal-profiles-list.c |    4 +-
 src/terminal-profiles-list.h |    2 +-
 src/terminal-window.c        |    4 +-
 src/terminal.c               |    3 +-
 15 files changed, 822 insertions(+), 864 deletions(-)
---
diff --git a/.dir-locals.el b/.dir-locals.el
index 7e193c7..f377013 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -2,4 +2,9 @@
             (indent-tabs-mode . nil)
             (c-basic-offset . 2)
             (tab-width . 8)
-            (show-trailing-whitespace . t))))
+            (show-trailing-whitespace . t)))
+(vala-mode . ((c-file-style . "GNU")
+              (indent-tabs-mode . nil)
+              (c-basic-offset . 2)
+              (tab-width . 8)
+              (show-trailing-whitespace . t))))
diff --git a/configure.ac b/configure.ac
index 47277b4..96be878 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,31 @@ PKG_CHECK_MODULES([TERM],
    $PLATFORM_DEPS])
 
 # ****
+# Vala
+# ****
+
+AC_MSG_CHECKING([whether vala is requested])
+AC_ARG_WITH([vala],[AS_HELP_STRING([--without-vala],[Disable vala programmes])],
+  [],[with_vala=yes])
+AC_MSG_RESULT([$with_vala])
+
+if test "$with_vala" = "yes"; then
+  AM_PROG_VALAC([0.24])
+
+  VALA_CHECK_PACKAGES([glib-2.0 gio-2.0 gio-unix-2.0 linux posix])
+  PKG_CHECK_MODULES([GTERMINAL],
+    [glib-2.0 >= $GLIB_REQUIRED
+     gio-2.0 >= $GIO_REQUIRED
+     gio-unix-2.0 >= $GIO_REQUIRED
+     gtk+-$GTK_API_VERSION >= $GTK_REQUIRED
+     dconf >= $DCONF_REQUIRED
+     uuid
+     $PLATFORM_DEPS])
+fi
+
+AM_CONDITIONAL([WITH_VALA],[test "$with_vala" = "yes"])
+
+# ****
 # DBus
 # ****
 
@@ -244,7 +269,8 @@ fi
 # Compilation
 # ***********
 
-CC_CHECK_FLAGS_APPEND([AM_CFLAGS],[CFLAGS],[ \
+WARN_CFLAGS=
+CC_CHECK_FLAGS_APPEND([WARN_CFLAGS],[CFLAGS],[ \
   -pipe \
   -Waggregate-return \
   -Wall \
@@ -314,6 +340,8 @@ AC_SUBST([TERMINAL_MAJOR_VERSION],[gt_version_major])
 AC_SUBST([TERMINAL_MINOR_VERSION],[gt_version_minor])
 AC_SUBST([TERMINAL_MICRO_VERSION],[gt_version_micro])
 AC_SUBST([TERMINAL_API_VERSION],[gt_api_version])
+AC_SUBST([GTK_API_VERSION])
+AC_SUBST([WARN_CFLAGS])
 AC_SUBST([AM_CPPFLAGS])
 AC_SUBST([AM_CFLAGS])
 AC_SUBST([AM_LDFLAGS])
@@ -338,6 +366,7 @@ gnome-terminal-$VERSION:
       prefix:                 ${prefix}
       source code location:   ${srcdir}
       compiler:               ${CC}
+      Vala:                   ${with_vala}
       DBus interface dir:     ${dbusinterfacedir}
       DBus service dir:       ${dbusservicedir}
       Debug:                  ${enable_debug}
diff --git a/src/Makefile.am b/src/Makefile.am
index faea290..5e3b115 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -4,7 +4,7 @@ BUILT_SOURCES =
 
 bin_PROGRAMS = gnome-terminal
 libexec_PROGRAMS = gnome-terminal-server
-noinst_PROGRAMS = gnome-terminal-client
+noinst_PROGRAMS =
 
 if WITH_NAUTILUS_EXTENSION
 nautilusextension_LTLIBRARIES = libterminal-nautilus.la
@@ -91,6 +91,7 @@ gnome_terminal_server_CPPFLAGS = \
 
 gnome_terminal_server_CFLAGS = \
        $(TERM_CFLAGS) \
+       $(WARN_CFLAGS) \
        $(AM_CFLAGS)
 
 gnome_terminal_server_LDFLAGS = \
@@ -150,47 +151,64 @@ terminal-resources.h terminal-resources.c: terminal.gresource.xml Makefile $(she
 
 # Terminal client
 
-gnome_terminal_client_SOURCES = \
-       client.c \
+if WITH_VALA
+
+noinst_PROGRAMS += gterminal
+
+gterminal_SOURCES = \
+       gterminal.vala \
+       client.vapi \
+       config.vapi \
+       profiles.vapi \
        terminal-client-utils.c \
        terminal-client-utils.h \
        terminal-debug.c \
        terminal-debug.h \
-       terminal-defines.h \
-       terminal-i18n.c \
-       terminal-i18n.h \
-       terminal-libgsystem.h \
        terminal-profiles-list.c \
        terminal-profiles-list.h \
-       terminal-schemas.h \
        terminal-settings-list.c \
        terminal-settings-list.h \
        $(NULL)
 
-nodist_gnome_terminal_client_SOURCES = \
-       terminal-gdbus-generated.c \
-       terminal-gdbus-generated.h \
+nodist_gterminal_SOURCES = \
        terminal-type-builtins.c \
        terminal-type-builtins.h \
        $(NULL)
 
-gnome_terminal_client_CPPFLAGS = \
+gterminal_VALAFLAGS = \
+       --vapidir . \
+       --pkg glib-2.0 \
+       --pkg gio-2.0 \
+       --pkg gio-unix-2.0 \
+       --pkg posix \
+       --pkg gtk+-$(GTK_API_VERSION) \
+       $(GTERMINAL_VALAFLAGS) \
+       $(AM_VALAFLAGS)
+gterminal_CPPFLAGS = \
+       -I$(top_builddir) \
        -DTERMINAL_COMPILATION \
        -DTERMINAL_CLIENT \
-       -DTERM_DATADIR="\"$(datadir)\"" \
-       -DTERM_LOCALEDIR="\"$(datadir)/locale\"" \
-       -DTERM_PKGDATADIR="\"$(pkgdatadir)\"" \
+       -DLOCALEDIR="\"$(datadir)/locale\"" \
        $(AM_CPPFLAGS)
-
-gnome_terminal_client_CFLAGS = \
-       $(TERM_CFLAGS) \
-       $(AM_CFLAGS)
-
-gnome_terminal_client_LDFLAGS = \
+# See bug #710862 about -Wsuggest-attribute=format
+gterminal_CFLAGS = \
+       $(GTERMINAL_CFLAGS) \
+       $(AM_CFLAGS) \
+       $(WARN_CFLAGS) \
+       -Wno-cast-qual \
+       -Wno-suggest-attribute=format \
+       -Wno-unused-but-set-variable \
+       -Wno-unused-function \
+       -Wno-unused-variable \
+       -Wno-write-strings \
+       $(NULL)
+gterminal_LDFLAGS = \
        $(AM_LDFLAGS)
+gterminal_LDADD = \
+       $(GTERMINAL_LIBS) \
+       $(NULL)
 
-gnome_terminal_client_LDADD = \
-       $(TERM_LIBS)
+endif # WITH_VALA
 
 # Legacy terminal client
 
@@ -230,6 +248,7 @@ gnome_terminal_CPPFLAGS = \
 
 gnome_terminal_CFLAGS = \
        $(TERM_CFLAGS) \
+       $(WARN_CFLAGS) \
        $(AM_CFLAGS)
 
 gnome_terminal_LDFLAGS = \
@@ -263,6 +282,7 @@ libterminal_nautilus_la_CPPFLAGS = \
 
 libterminal_nautilus_la_CFLAGS = \
        $(NAUTILUS_CFLAGS) \
+       $(WARN_CFLAGS) \
        $(AM_CFLAGS)
 
 libterminal_nautilus_la_LDFLAGS = \
@@ -299,6 +319,7 @@ gnome_terminal_migration_CPPFLAGS = \
        $(AM_CPPFLAGS)
 gnome_terminal_migration_CFLAGS = \
        $(MIGRATOR_CFLAGS) \
+       $(WARN_CFLAGS) \
        $(AM_CFLAGS)
 gnome_terminal_migration_LDFLAGS = \
        $(AM_LDFLAGS)
diff --git a/src/client.vapi b/src/client.vapi
new file mode 100644
index 0000000..7eed2bf
--- /dev/null
+++ b/src/client.vapi
@@ -0,0 +1,44 @@
+/*
+ * Copyright © 2014 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+[CCode (lower_case_cprefix = "terminal_client_", cheader_filename = "terminal-client-utils.h")]
+namespace Terminal.Client {
+
+  public void append_create_instance_options (GLib.VariantBuilder builder,
+                                              string display_name,
+                                              string? startup_id,
+                                              string? geometry,
+                                              string? role,
+                                              string? profile,
+                                              string? title,
+                                              bool maximise_window,
+                                              bool fullscreen_window);
+
+  [CCode (cname = "PassFdElement", has_type_id = false)]
+  [SimpleType]
+  public struct PassFdElement {
+    public int index;
+    public int fd;
+  }
+
+  public void append_exec_options (GLib.VariantBuilder builder,
+                                   string? working_directory,
+                                   PassFdElement[]? fd_array,
+                                   bool shell);
+
+  public string? get_fallback_startup_id ();
+}
diff --git a/src/config.vapi b/src/config.vapi
new file mode 100644
index 0000000..9b20284
--- /dev/null
+++ b/src/config.vapi
@@ -0,0 +1,7 @@
+[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")]
+namespace Config
+{
+  public const string GETTEXT_PACKAGE;
+  public const string LOCALEDIR;
+  public const string VERSION;
+}
diff --git a/src/gterminal.vala b/src/gterminal.vala
new file mode 100644
index 0000000..8e76b82
--- /dev/null
+++ b/src/gterminal.vala
@@ -0,0 +1,581 @@
+/*
+ * Copyright © 2014 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+namespace GTerminal
+{
+  /* Output handling */
+
+  public struct Output
+  {
+    private static bool quiet = false;
+    private static bool verbose = false;
+
+    private static const OptionEntry[] entries = {
+      { "quiet",   0,   OptionFlags.HIDDEN, OptionArg.NONE, ref quiet,
+        "Suppress output", null },
+      { "verbose", 'v', OptionFlags.HIDDEN, OptionArg.NONE, ref verbose,
+        "Verbose output", null },
+      { null, 0, 0, 0, null, null, null }
+    };
+
+    public static void set_quiet (bool value)
+    {
+      quiet = value;
+    }
+
+    public GLib.OptionGroup get_option_group ()
+    {
+      var group = new GLib.OptionGroup ("output",
+                                        "Output options:",
+                                        "Show output options",
+                                        null, null);
+      group.add_entries (entries);
+      group.set_translation_domain(Config.GETTEXT_PACKAGE);
+      return group;
+    }
+
+    [PrintfFormat]
+    public void print(string format,
+                      ...)
+    {
+      if (!quiet)
+        stdout.vprintf (format, va_list());
+    }
+
+    [PrintfFormat]
+    public void printerr(string format, ...)
+    {
+      if (!quiet)
+        stderr.vprintf (format, va_list());
+    }
+
+    [PrintfFormat]
+    public void info(string format, ...)
+    {
+      if (verbose)
+        stderr.vprintf (format, va_list());
+    }
+  }
+
+  /* Global options */
+
+  public struct GlobalOptions {
+    public static string? app_id = null;
+
+    private static bool option_app_id (string option_name,
+                                       string value,
+                                       void *unused_user_data) throws OptionError
+    {
+      if (!GLib.Application.id_is_valid (value))
+        throw new OptionError.BAD_VALUE ("\"%s\" is not a valid application ID", value);
+      app_id = value;
+      return true;
+    }
+
+    public static string get_app_id ()
+    {
+      return app_id != null ? app_id : "org.gnome.Terminal";
+    }
+
+    private static const OptionEntry[] entries = {
+      { "app-id", 0, OptionFlags.HIDDEN, OptionArg.CALLBACK, (void*) option_app_id,
+        "Server application ID", "ID" },
+      { null, 0, 0, 0, null, null, null }
+    };
+
+    public static GLib.OptionGroup get_option_group ()
+    {
+      var group = new GLib.OptionGroup ("global",
+                                        "Global options:",
+                                        "Show global options",
+                                        null, null);
+      group.add_entries (entries);
+      group.set_translation_domain(Config.GETTEXT_PACKAGE);
+      return group;
+    }
+  }
+
+  public struct OpenOptions {
+
+    [CCode (array_length = false, array_null_terminated = true)]
+    private static string[]? pass_fds = null;
+    private static bool pass_stdin = false;
+    private static bool pass_stdout = false;
+    private static bool pass_stderr = false;
+    private static Terminal.Client.PassFdElement[]? fd_array = null;
+    public static GLib.UnixFDList? fd_list = null;
+
+    private static bool post_parse (OptionContext context,
+                                    OptionGroup group,
+                                    void *unused_user_data) throws Error
+    {
+      if (pass_stdin || pass_stdout || pass_stderr)
+        throw new OptionError.BAD_VALUE ("FD passing of std%s is not supported",
+                                         pass_stdin ? "in" : pass_stdout ? "out" : "err");
+
+      if (pass_fds == null)
+        return true;
+
+      fd_list = new GLib.UnixFDList ();
+      Terminal.Client.PassFdElement[] arr = {};
+
+      for (uint i = 0; i < pass_fds.length; i++) {
+        int64 v;
+        if (!int64.try_parse (pass_fds[i], out v) ||
+            v == -1 || v < int.MIN || v > int.MAX)
+          throw new OptionError.BAD_VALUE ("Invalid argument \"%s\" to --fd option", pass_fds[i]);
+
+        int fd = (int) v;
+
+        if (fd == Posix.STDIN_FILENO ||
+            fd == Posix.STDOUT_FILENO ||
+            fd == Posix.STDERR_FILENO)
+          throw new OptionError.BAD_VALUE ("FD passing of std%s is not supported",
+                                           fd == Posix.STDIN_FILENO ? "in" :
+                                           fd == Posix.STDOUT_FILENO ? "out" : "err");
+
+        for (uint j = 0; j < arr.length; j++) {
+          if (arr[j].fd == fd)
+            throw new OptionError.BAD_VALUE ("Cannot pass FD %d twice", fd);
+        }
+
+        var idx = fd_list.append (fd);
+        Terminal.Client.PassFdElement e = { idx, fd };
+        arr += e;
+
+        if (fd == Posix.STDOUT_FILENO ||
+            fd == Posix.STDERR_FILENO) {
+          GTerminal.Output.set_quiet (true);
+        }
+#if 0
+        if (fd == Posix.STDIN_FILENO)
+          data->wait = TRUE;
+#endif
+      }
+
+      fd_array = arr;
+      return true;
+    }
+
+    private static const OptionEntry[] exec_entries = {
+      { "stdin", 0, OptionFlags.HIDDEN, OptionArg.NONE, ref pass_stdin,
+        "Forward stdin", null },
+      { "stdout", 0, OptionFlags.HIDDEN, OptionArg.NONE, ref pass_stdout,
+        "Forward stdout", null },
+      { "stderr", 0, OptionFlags.HIDDEN, OptionArg.NONE, ref pass_stderr,
+        "Forward stderr", null },
+      { "fd", 0, 0, OptionArg.STRING_ARRAY, ref pass_fds,
+        "Forward file descriptor", "FD" },
+      { null, 0, 0, 0, null, null, null }
+    };
+
+    public static GLib.OptionGroup get_exec_option_group ()
+    {
+      var group = new GLib.OptionGroup ("exec",
+                                        "Exec options:",
+                                        "Show exec options",
+                                        null, null);
+      group.add_entries (exec_entries);
+      group.set_translation_domain(Config.GETTEXT_PACKAGE);
+      group.set_parse_hooks (null, (OptionParseFunc)post_parse);
+      return group;
+    }
+
+    /* Window options */
+
+    public static string? geometry = null;
+    public static string? role = null;
+    public static bool show_menubar = true;
+    public static bool show_menubar_set = false;
+    public static bool maximise = false;
+    public static bool fullscreen = false;
+
+    private static const OptionEntry[] window_entries = {
+      { "maximise", 0, 0, OptionArg.NONE, ref maximise,
+        "Maximise the window", null },
+      { "fullscreen", 0, 0, OptionArg.NONE, ref fullscreen,
+        "Full-screen the window", null },
+      { "geometry", 0, 0, OptionArg.STRING, ref geometry,
+        "Set the window size; for example: 80x24, or 80x24+200+200 (COLSxROWS+X+Y)",
+        "GEOMETRY" },
+      { "role", 0, 0, OptionArg.STRING, ref role,
+        "Set the window role", "ROLE" },
+      { null, 0, 0, 0, null, null, null }
+    };
+
+    public static GLib.OptionGroup get_window_option_group()
+    {
+      var group = new GLib.OptionGroup ("window",
+                                        "Window options:",
+                                        "Show window options",
+                                        null, null);
+      group.add_entries (window_entries);
+      group.set_translation_domain(Config.GETTEXT_PACKAGE);
+      return group;
+    }
+
+    /* Terminal options */
+
+    public static string? working_directory = null;
+    public static string? profile = null;
+    public static string? title = null;
+    public static double zoom = 1.0;
+
+    private static bool option_profile (string option_name,
+                                        string? value,
+                                        void *unused_user_data) throws Error
+    {
+      if (profile != null)
+        throw new OptionError.BAD_VALUE ("May only use option %s once", option_name);
+
+        var profiles = new Terminal.ProfilesList ();
+        profile = profiles.dup_uuid (value);
+        return true;
+    }
+
+    private static bool option_zoom (string option_name,
+                                     string? value,
+                                     void *unused_user_data) throws Error
+    {
+      double v;
+      if (!double.try_parse (value, out v))
+        throw new OptionError.BAD_VALUE ("\"%s\" is not a valid zoom factor",
+                                         value);
+
+      if (v < 0.25 || v > 4.0)
+        throw new OptionError.BAD_VALUE ("Zoom value \"%s\" is outside allowed range",
+                                         value);
+
+      zoom = v;
+      return true;
+    }
+
+    private static const OptionEntry[] terminal_entries = {
+      { "profile", 0, 0, OptionArg.CALLBACK, (void*) option_profile,
+        "Use the given profile instead of the default profile",
+        "UUID" },
+      { "title", 0, 0, OptionArg.STRING, ref title,
+        "Set the terminal title", "TITLE" },
+      { "cwd", 0, 0, OptionArg.FILENAME, ref working_directory,
+        "Set the working directory", "DIRNAME" },
+      { "zoom", 0, 0, OptionArg.CALLBACK, (void*) option_zoom,
+        "Set the terminal's zoom factor (1.0 = normal size)",
+        "ZOOM" },
+      { null, 0, 0, 0, null, null, null }
+    };
+
+    public static GLib.OptionGroup get_terminal_option_group ()
+    {
+      var group = new GLib.OptionGroup ("terminal",
+                                        "Terminal options:",
+                                        "Show terminal options",
+                                        null, null);
+      group.add_entries (terminal_entries);
+      group.set_translation_domain(Config.GETTEXT_PACKAGE);
+      return group;
+    }
+
+    /* Processing options */
+
+    public static bool wait_for_remote = false;
+
+    private static const OptionEntry[] processing_entries = {
+    { "wait", 0, 0, OptionArg.NONE, ref wait_for_remote,
+      "Wait until the child exits", null },
+      { null, 0, 0, 0, null, null, null }
+    };
+
+    public static GLib.OptionGroup get_processing_option_group ()
+    {
+      var group = new GLib.OptionGroup ("processing",
+                                        "Processing options:",
+                                        "Show processing options",
+                                        null, null);
+      group.add_entries (processing_entries);
+      group.set_translation_domain(Config.GETTEXT_PACKAGE);
+      return group;
+    }
+
+    /* Argument parsing */
+
+    [CCode (array_length = false, array_null_terminated = true)]
+    public static string[]? argv_pre = null;
+    [CCode (array_length = false, array_null_terminated = true)]
+    public static string[]? argv_post = null;
+    public static string? display_name = null;
+    public static string? startup_id = null;
+
+    public static void parse_argv (string[] argv) throws Error
+    {
+      /* Need to save this before gtk_init is being called! */
+      startup_id = Environment.get_variable ("DESKTOP_STARTUP_ID");
+
+      /* If there's a '--' argument with other arguments after it,
+       * strip them off. Need to do this before parsing the options!
+       */
+
+      bool found_dashdash = false;
+      for (uint i = 0; i < argv.length; i++) {
+        if (argv[i] != "--")
+          continue;
+
+        if (i > 0)
+          argv_pre = argv[0:i];
+        else
+          argv_pre = null;
+
+        if (i + 1 < argv.length)
+          argv_post = argv[i+1:argv.length];
+        else
+          argv_post = null;
+
+        found_dashdash = true;
+        break;
+      }
+
+      if (!found_dashdash) {
+        argv_pre = argv;
+        argv_post = null;
+      }
+
+      var context = new GLib.OptionContext ("— terminal client");
+      context.set_translation_domain (Config.GETTEXT_PACKAGE);
+      context.add_group (Gtk.get_option_group (true));
+      context.add_group (GlobalOptions.get_option_group ());
+      context.add_group (get_window_option_group ());
+      context.add_group (get_terminal_option_group ());
+      context.add_group (get_exec_option_group ());
+      context.add_group (get_processing_option_group ());
+
+      context.parse_strv (ref argv_pre);
+
+      if (working_directory == null)
+        working_directory = Environment.get_current_dir ();
+
+      /* Do this here so that gdk_display is initialized */
+      if (startup_id == null)
+        startup_id = Terminal.Client.get_fallback_startup_id ();
+
+      display_name = Gdk.Display.get_default ().get_name ();
+    }
+
+  } /* struct OpenOptions */
+
+  /* DBUS Interfaces */
+
+  [DBus (name = "org.gnome.Terminal.Factory0")]
+  interface Server : DBusProxy {
+    public const string SERVICE_NAME = "org.gnome.Terminal";
+    public const string INTERFACE_NAME = "org.gnome.Terminal.Factory0";
+    public const string OBJECT_PATH = "/org/gnome/Terminal/Factory0";
+
+    /* public abstract GLib.ObjectPath CreateInstance (HashTable<string, Variant> dict) throws IOError; */
+  }
+
+  [DBus (name = "org.gnome.Terminal.Terminal0")]
+  interface Receiver : DBusProxy {
+    public const string INTERFACE_NAME = "org.gnome.Terminal.Terminal0";
+
+    /* public abstract void Exec (HashTable<string, Variant> options,
+       [DBus (signature = "aay")] string[] arguments) throws IOError; */
+    public signal void ChildExited (int exit_code);
+  }
+
+  /* DBus helper functions */
+
+  private Server get_server () throws IOError
+  {
+    return Bus.get_proxy_sync (BusType.SESSION,
+                               GlobalOptions.get_app_id (),
+                               Server.OBJECT_PATH,
+                               DBusProxyFlags.DO_NOT_LOAD_PROPERTIES |
+                               DBusProxyFlags.DO_NOT_CONNECT_SIGNALS);
+  }
+
+  private Receiver create_terminal () throws Error
+  {
+    var server = get_server ();
+
+    var builder = new GLib.VariantBuilder (VariantType.VARDICT);
+    Terminal.Client.append_create_instance_options (builder,
+                                                    OpenOptions.display_name,
+                                                    OpenOptions.startup_id,
+                                                    OpenOptions.geometry,
+                                                    OpenOptions.role,
+                                                    OpenOptions.profile,
+                                                    OpenOptions.title,
+                                                    OpenOptions.maximise,
+                                                    OpenOptions.fullscreen);
+    if (OpenOptions.show_menubar_set)
+      builder.add ("{sv}", "show-menubar", new Variant.boolean (OpenOptions.show_menubar));
+
+    /* FIXME: Not using the proxy method since the generated code seems broken… */
+    var path = server.call_sync ("CreateInstance" /* (a{sv}) */,
+                                 new Variant ("(a{sv})", builder),
+                                 DBusCallFlags.NO_AUTO_START, -1,
+                                 null);
+
+    string obj_path;
+    path.get ("(o)", out obj_path);
+
+    return Bus.get_proxy_sync (BusType.SESSION,
+                               GlobalOptions.get_app_id (),
+                               obj_path,
+                               DBusProxyFlags.DO_NOT_LOAD_PROPERTIES);
+  }
+
+  /* Helper functions */
+
+  private int mangle_exit_code (int status)
+  {
+    if (Process.if_exited (status))
+      return Process.exit_status (status).clamp (0, 127);
+    else if (Process.if_signaled (status))
+      return 128 + (int) Process.term_sig (status);
+    else
+      return 127;
+  }
+
+  /* Verbs */
+
+  private int run (Receiver receiver)
+  {
+    int status = 0;
+    var loop = new GLib.MainLoop ();
+    var id = receiver.ChildExited.connect((s) => {
+      if (loop.is_running ())
+        loop.quit ();
+      status = s;
+    });
+
+    loop.run ();
+    receiver.disconnect(id);
+
+    return mangle_exit_code (status);
+  }
+
+  private int open (string[] argv) throws Error
+  {
+    OpenOptions.parse_argv (argv);
+
+    if (argv[0] == "run" && OpenOptions.argv_post == null)
+      throw new OptionError.BAD_VALUE ("'%s' needs the command to run as arguments after '--'",
+                                       argv[0]);
+
+    var receiver = create_terminal ();
+
+    var builder = new GLib.VariantBuilder (VariantType.TUPLE);
+    builder.open (VariantType.VARDICT); {
+      Terminal.Client.append_exec_options (builder,
+                                           OpenOptions.working_directory,
+                                           OpenOptions.fd_array,
+                                           argv[0] == "shell");
+    } builder.close ();
+    builder.add_value (new Variant.bytestring_array (OpenOptions.argv_post));
+
+    receiver.call_with_unix_fd_list_sync ("Exec" /* (a{sv}aay) */,
+                                          builder.end (),
+                                          DBusCallFlags.NO_AUTO_START, -1,
+                                          OpenOptions.fd_list);
+
+    if (!OpenOptions.wait_for_remote)
+      return Posix.EXIT_SUCCESS;
+
+    return run (receiver);
+  }
+
+  private int help (string[] argv) throws Error
+  {
+    /* FIXME: launch man pager for gterminal(1) */
+    return Posix.EXIT_SUCCESS;
+  }
+
+  private int complete (string[] argv) throws Error
+  {
+    if (argv.length < 2)
+      throw new OptionError.UNKNOWN_OPTION ("Missing argument");
+
+    if (argv[1] == "commands") {
+      string? prefix = argv.length > 2 ? argv[2] : null;
+      for (uint i = 0; i < commands.length; i++) {
+        if (commands[i].verb.has_prefix ("_"))
+          continue;
+        if (prefix == null || commands[i].verb.has_prefix (prefix))
+          print ("%s\n", commands[i].verb);
+      }
+
+      return Posix.EXIT_SUCCESS;
+    } else if (argv[1] == "profiles") {
+      var service = new Terminal.ProfilesList ();
+      var profiles = service.dupv_children ();
+      string? prefix = argv.length > 2 ? argv[2] : null;
+      for (uint i = 0; i < profiles.length; i++) {
+        if (prefix == null || profiles[i].has_prefix (prefix))
+          print ("%s\n", profiles[i]);
+      }
+
+      return Posix.EXIT_SUCCESS;
+    }
+
+    throw new OptionError.UNKNOWN_OPTION ("Unknown completion request for \"%s\"", argv[0]);
+  }
+
+  private delegate int CommandFunc (string[] args) throws Error;
+
+  private struct CommandMap {
+    unowned string verb;
+    unowned CommandFunc func;
+  }
+
+  private static const CommandMap[] commands = {
+    { "help", help },
+    { "open", open },
+    { "shell", open },
+    { "_complete", complete },
+  };
+
+  public static int main (string[] argv)
+  {
+    Environment.set_prgname ("gterminal");
+
+    //Intl.setlocale (LocaleCategory.ALL, "");
+    //Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
+    //Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8");
+    //Intl.textdomain (Config.GETTEXT_PACKAGE);
+    Environment.set_application_name ("GTerminal");
+
+    try {
+      if (argv.length == 1) {
+        throw new OptionError.FAILED ("Missing command");
+      }
+
+      for (uint i = 0; i < commands.length; i++) {
+        if (commands[i].verb == argv[1]) {
+          return commands[i].func (argv[1:argv.length]);
+        }
+      }
+
+      throw new OptionError.FAILED ("Unknown command \"%s\"", argv[1]);
+    } catch (Error e) {
+      DBusError.strip_remote_error (e);
+
+      printerr ("Error processing arguments: %s\n", e.message);
+      return Posix.EXIT_FAILURE;
+    }
+  }
+
+} /* namespace GTerminal */
diff --git a/src/profiles.vapi b/src/profiles.vapi
new file mode 100644
index 0000000..6da9633
--- /dev/null
+++ b/src/profiles.vapi
@@ -0,0 +1,67 @@
+/*
+ * Copyright © 2014 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
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+[CCode (cprefix = "Terminal", lower_case_cprefix = "terminal_")]
+namespace Terminal {
+
+  [CCode (cheader_filename = "terminal-settings-list.h", type_id = "terminal_settings_list_get_type")]
+  public class SettingsList : GLib.Object {
+
+    [Flags, CCode (cprefix = "TERMINAL_SETTINGS_LIST_FLAG_", has_type_id = false)]
+    public enum Flags {
+      NONE,
+      HAS_DEFAULT,
+      ALLOW_EMPTY
+    }
+
+    [CCode (has_construct_function = false)]
+    public SettingsList (string path, string schema_id, string child_schema_id, Flags flags = Flags.NONE);
+
+    [CCode (array_length = false, array_null_terminated = true)]
+    public string[]? dupv_children ();
+    public GLib.List<GLib.Settings> ref_children ();
+
+    public bool has_child (string uuid);
+    public GLib.Settings? ref_child (string uuid);
+
+    public string add_child ();
+    public string clone_child (string uuid);
+    public void remove_child (string uuid);
+
+    public string dup_uuid_from_child (GLib.Settings child);
+    public GLib.Settings? ref_default_child ();
+    public string dup_default_child ();
+    public void set_default_child (string uuid);
+
+    public static bool valid_uuid (string uuid);
+  }
+
+  [CCode (cname = "TerminalSettingsList", cprefix = "terminal_profiles_list_", cheader_filename = 
"terminal-profiles-list.h", type_id = "terminal_settings_list_get_type")]
+  public class ProfilesList : SettingsList {
+
+    public ProfilesList ();
+
+    public GLib.List<GLib.Settings> ref_children_sorted ();
+
+    public string dup_uuid (string uuid) throws GLib.Error;
+    public string dup_uuid_or_name (string uuid_or_name) throws GLib.Error;
+
+    public GLib.Settings ref_profile_by_uuid (string uuid) throws GLib.Error;
+    public GLib.Settings ref_profile_by_uuid_or_name (string uuid_or_name) throws GLib.Error;
+  }
+
+}
diff --git a/src/terminal-client-utils.c b/src/terminal-client-utils.c
index a0756a2..b2295d8 100644
--- a/src/terminal-client-utils.c
+++ b/src/terminal-client-utils.c
@@ -99,6 +99,8 @@ terminal_client_append_create_instance_options (GVariantBuilder *builder,
 void 
 terminal_client_append_exec_options (GVariantBuilder *builder,
                                      const char      *working_directory,
+                                     PassFdElement   *fd_array,
+                                     gsize            fd_array_len,
                                      gboolean         shell)
 {
   gs_strfreev char **envv;
@@ -121,21 +123,39 @@ terminal_client_append_exec_options (GVariantBuilder *builder,
                          g_variant_new_bytestring_array ((const char * const *) envv, -1));
 
   if (working_directory)
-    g_variant_builder_add (builder, "{sv}", 
+    g_variant_builder_add (builder, "{sv}",
                            "cwd", g_variant_new_bytestring (working_directory));
 
   if (shell)
     g_variant_builder_add (builder, "{sv}",
                            "shell",
                            g_variant_new_boolean (TRUE));
+
+  if (fd_array_len) {
+    gsize i;
+
+    g_variant_builder_open (builder, G_VARIANT_TYPE ("{sv}"));
+    g_variant_builder_add (builder, "s", "fd-set");
+
+    g_variant_builder_open (builder, G_VARIANT_TYPE ("v"));
+    g_variant_builder_open (builder, G_VARIANT_TYPE ("a(ih)"));
+    for (i = 0; i < fd_array_len; i++) {
+      g_variant_builder_add (builder, "(ih)", fd_array[i].fd, fd_array[i].index);
+    }
+    g_variant_builder_close (builder); /* a(ih) */
+    g_variant_builder_close (builder); /* v */
+
+    g_variant_builder_close (builder); /* {sv} */
+  }
 }
 
 /**
  * terminal_client_get_fallback_startup_id:
- * @startup_id: (inout):
+ *
+ * Returns: a fallback startup ID, or %NULL
  */
-void 
-terminal_client_get_fallback_startup_id  (char **startup_id)
+char *
+terminal_client_get_fallback_startup_id  (void)
 {
 #if defined(TERMINAL_COMPILATION) && defined(GDK_WINDOWING_X11)
   GdkDisplay *display;
@@ -188,13 +208,8 @@ terminal_client_get_fallback_startup_id  (char **startup_id)
 
   XDestroyWindow(xdisplay, xwindow);
 
-  if (startup_id)
-    *startup_id = g_strdup_printf ("_TIME%lu", event.xproperty.time);
-
-  return;
-
+  return g_strdup_printf ("_TIME%lu", event.xproperty.time);
 out:
 #endif
-  if (startup_id)
-    *startup_id = NULL;
+  return NULL;
 }
diff --git a/src/terminal-client-utils.h b/src/terminal-client-utils.h
index 3f9a30b..57d711a 100644
--- a/src/terminal-client-utils.h
+++ b/src/terminal-client-utils.h
@@ -19,12 +19,10 @@
 #define TERMINAL_CLIENT_UTILS_H
 
 #include <gio/gio.h>
+#include <gio/gunixfdlist.h>
 
 G_BEGIN_DECLS
 
-char *terminal_client_get_profile (const char *name_or_id,
-                                   GError **error);
-
 void terminal_client_append_create_instance_options (GVariantBuilder *builder,
                                                      const char      *display_name,
                                                      const char      *startup_id,
@@ -35,11 +33,18 @@ void terminal_client_append_create_instance_options (GVariantBuilder *builder,
                                                      gboolean         maximise_window,
                                                      gboolean         fullscreen_window);
 
+typedef struct {
+  int index;
+  int fd;
+} PassFdElement;
+
 void terminal_client_append_exec_options            (GVariantBuilder *builder,
                                                      const char      *working_directory,
+                                                     PassFdElement   *fd_array,
+                                                     gsize            fd_array_len,
                                                      gboolean         shell);
 
-void terminal_client_get_fallback_startup_id        (char           **startup_id);
+char * terminal_client_get_fallback_startup_id      (void);
 
 G_END_DECLS
 
diff --git a/src/terminal-nautilus.c b/src/terminal-nautilus.c
index 28290ac..3474783 100644
--- a/src/terminal-nautilus.c
+++ b/src/terminal-nautilus.c
@@ -424,6 +424,7 @@ create_terminal (ExecData *data /* transfer full */)
 
   terminal_client_append_exec_options (&builder,
                                        data->path,
+                                       NULL, 0, /* FD array */
                                        TRUE /* shell */);
 
   if (data->info == FILE_INFO_SFTP &&
diff --git a/src/terminal-profiles-list.c b/src/terminal-profiles-list.c
index 64795d5..721dbff 100644
--- a/src/terminal-profiles-list.c
+++ b/src/terminal-profiles-list.c
@@ -100,13 +100,13 @@ get_profile_names (TerminalSettingsList *list,
 }
 
 /**
- * terminal_profiles_list_get_children:
+ * terminal_profiles_list_ref_children_sorted:
  * @list:
  *
  * Returns: (transfer full):
  */
 GList *
-terminal_profiles_list_ref_children (TerminalSettingsList *list)
+terminal_profiles_list_ref_children_sorted (TerminalSettingsList *list)
 {
   return g_list_sort (terminal_settings_list_ref_children (list),
                       terminal_profiles_compare);
diff --git a/src/terminal-profiles-list.h b/src/terminal-profiles-list.h
index 42ed11c..179d3e3 100644
--- a/src/terminal-profiles-list.h
+++ b/src/terminal-profiles-list.h
@@ -27,7 +27,7 @@ G_BEGIN_DECLS
 
 TerminalSettingsList *terminal_profiles_list_new (void);
 
-GList *terminal_profiles_list_ref_children (TerminalSettingsList *list);
+GList *terminal_profiles_list_ref_children_sorted (TerminalSettingsList *list);
 
 char *terminal_profiles_list_dup_uuid (TerminalSettingsList *list,
                                        const char *uuid,
diff --git a/src/terminal-window.c b/src/terminal-window.c
index 3b3ac81..90b58d3 100644
--- a/src/terminal-window.c
+++ b/src/terminal-window.c
@@ -1295,7 +1295,7 @@ terminal_window_update_set_profile_menu (TerminalWindow *window)
     }
 
   profiles_list = terminal_app_get_profiles_list (terminal_app_get ());
-  profiles = terminal_profiles_list_ref_children (profiles_list);
+  profiles = terminal_profiles_list_ref_children_sorted (profiles_list);
 
   action = gtk_action_group_get_action (priv->action_group, "TerminalProfiles");
   single_profile = !profiles || profiles->next == NULL; /* list length <= 1 */
@@ -1413,7 +1413,7 @@ terminal_window_update_new_terminal_menus (TerminalWindow *window)
     }
 
   profiles_list = terminal_app_get_profiles_list (terminal_app_get ());
-  profiles = terminal_profiles_list_ref_children (profiles_list);
+  profiles = terminal_profiles_list_ref_children_sorted (profiles_list);
 
   have_single_profile = !profiles || !profiles->next;
 
diff --git a/src/terminal.c b/src/terminal.c
index 18b860c..2dd5a6d 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -178,6 +178,7 @@ handle_options (TerminalFactory *factory,
           terminal_client_append_exec_options (&builder,
                                                it->working_dir ? it->working_dir 
                                                                : options->default_working_dir,
+                                               NULL, 0, /* FD array */
                                                argc == 0);
 
           if (!terminal_receiver_call_exec_sync (receiver,
@@ -243,7 +244,7 @@ main (int argc, char **argv)
 
   /* Do this here so that gdk_display is initialized */
   if (options->startup_id == NULL)
-    terminal_client_get_fallback_startup_id (&options->startup_id);
+    options->startup_id = terminal_client_get_fallback_startup_id ();
 
   display = gdk_display_get_default ();
   display_name = gdk_display_get_name (display);



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