[gnome-terminal] Change feactory interface
- From: Christian Persch <chpe src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-terminal] Change feactory interface
- Date: Thu, 3 May 2012 19:02:29 +0000 (UTC)
commit 67c373417d2eddcabb4e14a1d5e9ea21b058205b
Author: Christian Persch <chpe gnome org>
Date: Sat Nov 19 22:54:48 2011 +0100
Change feactory interface
More changes to come. Add new gnome-terminal-client programme that will
take over from gnome-terminal, which will remain only for legacy backward
compatibiliy.
src/Makefile.am | 60 +++-
src/client.c | 721 ++++++++++++++++++++++++++++++++++++++++++++
src/org.gnome.Terminal.xml | 19 +-
src/server.c | 40 +--
src/terminal-app.c | 347 ++++++++++------------
src/terminal-app.h | 6 +-
src/terminal-controller.c | 243 +++++++++++++++
src/terminal-controller.h | 63 ++++
src/terminal-defines.h | 37 +++
src/terminal-options.c | 2 +-
src/terminal-options.h | 1 +
src/terminal-screen.c | 50 +++-
src/terminal-screen.h | 8 +
src/terminal.c | 182 +++++++++++-
14 files changed, 1521 insertions(+), 258 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index b276393..abb1f96 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
NULL =
-bin_PROGRAMS = gnome-terminal
+bin_PROGRAMS = gnome-terminal-client gnome-terminal
libexec_PROGRAMS = gnome-terminal-server
# Terminal server
@@ -12,8 +12,8 @@ BUILT_SOURCES = \
terminal-marshal.h \
terminal-type-builtins.c \
terminal-type-builtins.h \
- terminal-factory.c \
- terminal-factory.h \
+ terminal-gdbus-generated.c \
+ terminal-gdbus-generated.h \
$(NULL)
gnome_terminal_server_SOURCES= \
@@ -28,15 +28,16 @@ gnome_terminal_server_SOURCES= \
terminal-app.h \
terminal-close-button.h \
terminal-close-button.c \
+ terminal-controller.c \
+ terminal-controller.h \
terminal-debug.c \
terminal-debug.h \
+ terminal-defines.h \
terminal-encoding.c \
terminal-encoding.h \
terminal-info-bar.c \
terminal-info-bar.h \
terminal-intl.h \
- terminal-options.c \
- terminal-options.h \
terminal-schemas.h \
terminal-screen.c \
terminal-screen.h \
@@ -142,30 +143,67 @@ org.gnome.Terminal.Factory0.service: Makefile
echo "Name=org.gnome.Terminal.Factory0"; \
echo "${libexecdir}/gnome-terminal-server") > $@
-terminal-factory.c terminal-factory.h: org.gnome.Terminal.xml Makefile
+terminal-gdbus-generated.c terminal-gdbus-generated.h: org.gnome.Terminal.xml Makefile
$(AM_V_GEN) $(GDBUS_CODEGEN) \
--interface-prefix=org.gnome.Terminal \
--c-namespace=Terminal \
--c-generate-object-manager \
- --generate-c-code terminal-factory \
+ --generate-c-code terminal-gdbus-generated \
$<
-# Terminal remote
+# Terminal client
+
+gnome_terminal_client_SOURCES = \
+ client.c \
+ terminal-defines.h \
+ terminal-intl.h \
+ $(NULL)
+
+nodist_gnome_terminal_client_SOURCES = \
+ terminal-gdbus-generated.c \
+ terminal-gdbus-generated.h \
+ $(NULL)
+
+gnome_terminal_client_CPPFLAGS = \
+ -DTERMINAL_COMPILATION \
+ -DTERMINAL_CLIENT \
+ -DTERM_DATADIR="\"$(datadir)\"" \
+ -DTERM_LOCALEDIR="\"$(datadir)/locale\"" \
+ -DTERM_PKGDATADIR="\"$(pkgdatadir)\"" \
+ -DSN_API_NOT_YET_FROZEN \
+ -DGDK_MULTIHEAD_SAFE \
+ $(AM_CPPFLAGS)
+
+gnome_terminal_client_CFLAGS = \
+ $(TERM_CFLAGS) \
+ $(AM_CFLAGS)
+
+gnome_terminal_client_LDFLAGS = \
+ $(AM_LDFLAGS)
+
+gnome_terminal_client_LDADD = \
+ $(TERM_LIBS)
+
+# Legacy terminal client
gnome_terminal_SOURCES= \
terminal.c \
terminal-debug.c \
terminal-debug.h \
- terminal-factory.c \
- terminal-factory.h \
+ terminal-defines.h \
terminal-intl.h \
terminal-options.c \
terminal-options.h \
$(NULL)
+nodist_gnome_terminal_SOURCES = \
+ terminal-gdbus-generated.c \
+ terminal-gdbus-generated.h \
+ $(NULL)
+
gnome_terminal_CPPFLAGS = \
-DTERMINAL_COMPILATION \
- -DTERMINAL_REMOTE \
+ -DTERMINAL_CLIENT \
-DTERM_DATADIR="\"$(datadir)\"" \
-DTERM_LOCALEDIR="\"$(datadir)/locale\"" \
-DTERM_PKGDATADIR="\"$(pkgdatadir)\"" \
diff --git a/src/client.c b/src/client.c
new file mode 100644
index 0000000..29ed919
--- /dev/null
+++ b/src/client.c
@@ -0,0 +1,721 @@
+/*
+ * Copyright  2001, 2002 Havoc Pennington
+ * Copyright  2002, 2008-2010 Red Hat, Inc.
+ * Copyright  2002 Sun Microsystems
+ * Copyright  2003 Mariano Suarez-Alvarez
+ * Copyright  2008, 2010, 2011 Christian Persch
+ *
+ * Gnome-terminal 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.
+ *
+ * Gnome-terminal 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/>.
+ */
+
+/* Code copied and adapted from glib/gio/gdbus-tool.c:
+ * * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "terminal-intl.h"
+#include "terminal-gdbus-generated.h"
+#include "terminal-defines.h"
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+G_GNUC_UNUSED static void completion_debug (const gchar *format, ...);
+
+/* Uncomment to get debug traces in /tmp/gdbus-completion-debug.txt (nice
+ * to not have it interfere with stdout/stderr)
+ */
+#if 0
+G_GNUC_UNUSED static void
+completion_debug (const gchar *format, ...)
+{
+ va_list var_args;
+ gchar *s;
+ static FILE *f = NULL;
+
+ va_start (var_args, format);
+ s = g_strdup_vprintf (format, var_args);
+ if (f == NULL)
+ {
+ f = fopen ("/tmp/gdbus-completion-debug.txt", "a+");
+ }
+ fprintf (f, "%s\n", s);
+ g_free (s);
+}
+#else
+static void
+completion_debug (const gchar *format, ...)
+{
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+remove_arg (gint num, gint *argc, gchar **argv[])
+{
+ gint n;
+
+ g_assert (num <= (*argc));
+
+ for (n = num; (*argv)[n] != NULL; n++)
+ (*argv)[n] = (*argv)[n+1];
+ (*argv)[n] = NULL;
+ (*argc) = (*argc) - 1;
+}
+
+static void
+usage (gint *argc, gchar **argv[], gboolean use_stdout)
+{
+ GOptionContext *o;
+ gchar *s;
+ gchar *program_name;
+
+ o = g_option_context_new (_("COMMAND"));
+ g_option_context_set_help_enabled (o, FALSE);
+ /* Ignore parsing result */
+ g_option_context_parse (o, argc, argv, NULL);
+ program_name = g_path_get_basename ((*argv)[0]);
+ s = g_strdup_printf (_("Commands:\n"
+ " help Shows this information\n"
+ " open Create a new terminal\n"
+ "\n"
+ "Use \"%s COMMAND --help\" to get help on each command.\n"),
+ program_name);
+ g_free (program_name);
+ g_option_context_set_description (o, s);
+ g_free (s);
+ s = g_option_context_get_help (o, FALSE, NULL);
+ if (use_stdout)
+ g_print ("%s", s);
+ else
+ g_printerr ("%s", s);
+ g_free (s);
+ g_option_context_free (o);
+}
+
+static void
+modify_argv0_for_command (gint *argc, gchar **argv[], const gchar *command)
+{
+ gchar *s;
+ gchar *program_name;
+
+ /* TODO:
+ * 1. get a g_set_prgname() ?; or
+ * 2. save old argv[0] and restore later
+ */
+
+ g_assert (g_strcmp0 ((*argv)[1], command) == 0);
+ remove_arg (1, argc, argv);
+
+ program_name = g_path_get_basename ((*argv)[0]);
+ s = g_strdup_printf ("%s %s", (*argv)[0], command);
+ (*argv)[0] = s;
+ g_free (program_name);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* Copied from libnautilus/nautilus-program-choosing.c; Needed in case
+ * we have no DESKTOP_STARTUP_ID (with its accompanying timestamp).
+ */
+static char *
+slowly_and_stupidly_obtain_timestamp (void)
+{
+ Display *xdisplay;
+ Window xwindow;
+ XEvent event;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+
+ {
+ XSetWindowAttributes attrs;
+ Atom atom_name;
+ Atom atom_type;
+ const char *name;
+
+ attrs.override_redirect = True;
+ attrs.event_mask = PropertyChangeMask | StructureNotifyMask;
+
+ xwindow =
+ XCreateWindow (xdisplay,
+ RootWindow (xdisplay, 0),
+ -100, -100, 1, 1,
+ 0,
+ CopyFromParent,
+ CopyFromParent,
+ (Visual *)CopyFromParent,
+ CWOverrideRedirect | CWEventMask,
+ &attrs);
+
+ atom_name = XInternAtom (xdisplay, "WM_NAME", TRUE);
+ g_assert (atom_name != None);
+ atom_type = XInternAtom (xdisplay, "STRING", TRUE);
+ g_assert (atom_type != None);
+
+ name = "Fake Window";
+ XChangeProperty (xdisplay,
+ xwindow, atom_name,
+ atom_type,
+ 8, PropModeReplace, (unsigned char *)name, strlen (name));
+ }
+
+ XWindowEvent (xdisplay,
+ xwindow,
+ PropertyChangeMask,
+ &event);
+
+ XDestroyWindow(xdisplay, xwindow);
+
+ return g_strdup_printf ("_TIME%lu", event.xproperty.time);
+}
+
+typedef struct
+{
+ /* Window options */
+ char *startup_id;
+ const char *display_name;
+ int screen_number;
+ char *geometry;
+ char *role;
+
+ gboolean menubar_state;
+ gboolean start_fullscreen;
+ gboolean start_maximized;
+
+ /* Terminal options */
+ char **exec_argv; /* not owned! */
+ int exec_argc;
+
+ char *working_directory;
+ char *profile;
+ char *title;
+ double zoom;
+
+ /* Flags */
+ guint menubar_state_set : 1;
+ guint zoom_set : 1;
+ guint active : 1;
+} OptionData;
+
+static gboolean
+option_zoom_cb (const gchar *option_name,
+ const gchar *value,
+ gpointer user_data,
+ GError **error)
+{
+ OptionData *data = user_data;
+ double zoom;
+ char *end;
+
+ /* Try reading a locale-style double first, in case it was
+ * typed by a person, then fall back to ascii_strtod (we
+ * always save session in C locale format)
+ */
+ end = NULL;
+ errno = 0;
+ zoom = g_strtod (value, &end);
+ if (end == NULL || *end != '\0')
+ {
+ g_set_error (error,
+ G_OPTION_ERROR,
+ G_OPTION_ERROR_BAD_VALUE,
+ _("\"%s\" is not a valid zoom factor"),
+ value);
+ return FALSE;
+ }
+
+
+ data->zoom = zoom;
+ data->zoom_set = TRUE;
+
+ return TRUE;
+}
+
+static GOptionContext *
+get_goption_context (OptionData *data)
+{
+ const GOptionEntry window_goptions[] = {
+ { "maximize", 0, 0, G_OPTION_ARG_NONE, &data->start_maximized,
+ N_("Maximise the window"), NULL },
+ { "fullscreen", 0, 0, G_OPTION_ARG_NONE, &data->start_fullscreen,
+ N_("Full-screen the window"), NULL },
+ { "geometry", 0, 0, G_OPTION_ARG_STRING, &data->geometry,
+ N_("Set the window size; for example: 80x24, or 80x24+200+200 (COLSxROWS+X+Y)"),
+ N_("GEOMETRY") },
+ { "role", 0, 0, G_OPTION_ARG_STRING, &data->role,
+ N_("Set the window role"), N_("ROLE") },
+ { NULL, 0, 0, 0, NULL, NULL, NULL }
+ };
+
+ const GOptionEntry terminal_goptions[] = {
+ { "profile", 0, 0, G_OPTION_ARG_STRING, &data->profile,
+ N_("Use the given profile instead of the default profile"),
+ N_("PROFILE-NAME") },
+ { "title", 0, 0, G_OPTION_ARG_STRING, &data->title,
+ N_("Set the terminal title"), N_("TITLE") },
+ { "cwd", 0, 0, G_OPTION_ARG_STRING, &data->working_directory,
+ N_("Set the working directory"), N_("DIRNAME") },
+ { "zoom", 0, 0, G_OPTION_ARG_CALLBACK, option_zoom_cb,
+ N_("Set the terminal's zoom factor (1.0 = normal size)"),
+ N_("ZOOM") },
+ { NULL, 0, 0, 0, NULL, NULL, NULL }
+ };
+
+ GOptionContext *context;
+ GOptionGroup *group;
+
+ context = g_option_context_new (NULL);
+ g_option_context_set_translation_domain (context, GETTEXT_PACKAGE);
+ g_option_context_set_description (context, N_("GNOME Terminal Client"));
+ g_option_context_set_ignore_unknown_options (context, FALSE);
+
+ group = g_option_group_new ("window-options",
+ N_("Window options:"),
+ N_("Show window options"),
+ data,
+ NULL);
+ g_option_group_set_translation_domain (group, GETTEXT_PACKAGE);
+ g_option_group_add_entries (group, window_goptions);
+ g_option_context_add_group (context, group);
+
+ group = g_option_group_new ("terminal-options",
+ N_("Terminal options:"),
+ N_("Show per-terminal options"),
+ data,
+ NULL);
+ g_option_group_set_translation_domain (group, GETTEXT_PACKAGE);
+ g_option_group_add_entries (group, terminal_goptions);
+ g_option_context_add_group (context, group);
+
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+
+ return context;
+}
+
+static void
+option_data_free (OptionData *data)
+{
+ if (data == NULL)
+ return;
+
+ g_free (data->startup_id);
+ g_free (data->geometry);
+ g_free (data->role);
+
+ g_free (data->working_directory);
+ g_free (data->profile);
+ g_free (data->title);
+}
+
+static OptionData *
+parse_arguments (int *argcp,
+ char ***argvp,
+ GError **error)
+{
+ OptionData *data;
+ GOptionContext *context;
+ int argc = *argcp;
+ char **argv = *argvp;
+ int i;
+
+ data = g_new0 (OptionData, 1);
+
+ /* If there's a '--' argument with other arguments after it,
+ * strip them off. Need to do this before parsing the options!
+ */
+ data->exec_argv = NULL;
+ data->exec_argc = 0;
+ for (i = 1; i < argc - 1; i++) {
+ if (strcmp (argv[i], "--") == 0) {
+ data->exec_argv = &argv[i + 1];
+ data->exec_argc = argc - (i + 1);
+
+ /* Truncate argv */
+ *argcp = argc = i - 1;
+ break;
+ }
+ }
+
+ data->working_directory = g_get_current_dir ();
+
+ /* Need to save this here before calling gtk_init! */
+ data->startup_id = g_strdup (g_getenv ("DESKTOP_STARTUP_ID"));
+
+ context = get_goption_context (data);
+ if (!g_option_context_parse (context, argcp, argvp, error)) {
+ option_data_free (data);
+ g_option_context_free (context);
+ return NULL;
+ }
+ g_option_context_free (context);
+
+ /* Do this here so that gdk_display is initialized */
+ if (data->startup_id == NULL)
+ data->startup_id = slowly_and_stupidly_obtain_timestamp ();
+
+ data->display_name = gdk_display_get_name (gdk_display_get_default ());
+
+ return data;
+}
+
+/**
+ * build_create_options_variant:
+ *
+ * Returns: a floating #GVariant
+ */
+static GVariant *
+build_create_options_variant (OptionData *data)
+{
+ GVariantBuilder builder;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+ g_variant_builder_add (&builder, "{sv}",
+ "display", g_variant_new_bytestring (data->display_name));
+
+ if (data->startup_id)
+ g_variant_builder_add (&builder, "{sv}",
+ "desktop-startup-id", g_variant_new_bytestring (data->startup_id));
+ if (data->geometry)
+ g_variant_builder_add (&builder, "{sv}",
+ "geometry", g_variant_new_string (data->geometry));
+ if (data->role)
+ g_variant_builder_add (&builder, "{sv}",
+ "role", g_variant_new_string (data->role));
+ if (data->start_maximized)
+ g_variant_builder_add (&builder, "{sv}",
+ "maximize-window", g_variant_new_boolean (TRUE));
+ if (data->start_fullscreen)
+ g_variant_builder_add (&builder, "{sv}",
+ "fullscreen-window", g_variant_new_boolean (TRUE));
+
+ return g_variant_builder_end (&builder);
+}
+
+/**
+ * build_exec_options_variant:
+ *
+ * Returns: a floating #GVariant
+ */
+static GVariant *
+build_exec_options_variant (OptionData *data)
+{
+ GVariantBuilder builder;
+ char **envv;
+
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
+
+ if (data->working_directory)
+ g_variant_builder_add (&builder, "{sv}",
+ "cwd", g_variant_new_bytestring (data->working_directory));
+
+ envv = g_get_environ ();
+ if (envv) {
+ envv = g_environ_unsetenv (envv, "DESKTOP_STARTUP_ID");
+ envv = g_environ_unsetenv (envv, "GIO_LAUNCHED_DESKTOP_FILE_PID");
+ envv = g_environ_unsetenv (envv, "GIO_LAUNCHED_DESKTOP_FILE");
+
+ g_variant_builder_add (&builder, "{sv}",
+ "environ",
+ g_variant_new_bytestring_array ((const char * const *) envv, -1));
+ g_strfreev (envv);
+ }
+
+ return g_variant_builder_end (&builder);
+}
+
+static gboolean
+handle_open (int *argc,
+ char ***argv,
+ gboolean request_completion,
+ const gchar *completion_cur,
+ const gchar *completion_prev)
+{
+ OptionData *data;
+ TerminalFactory *factory;
+ TerminalReceiver *receiver;
+ GError *error = NULL;
+ char *object_path;
+
+ modify_argv0_for_command (argc, argv, "open");
+
+ data = parse_arguments (argc, argv, &error);
+ if (data == NULL) {
+ g_printerr ("Error parsing arguments: %s\n", error->message);
+ g_error_free (error);
+ return FALSE;
+ }
+
+ 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,
+ TERMINAL_UNIQUE_NAME,
+ TERMINAL_FACTORY_OBJECT_PATH,
+ NULL /* cancellable */,
+ &error);
+ if (factory == NULL) {
+ g_printerr ("Error constructing proxy for %s:%s: %s\n",
+ TERMINAL_UNIQUE_NAME, TERMINAL_FACTORY_OBJECT_PATH,
+ error->message);
+ g_error_free (error);
+ option_data_free (data);
+ return FALSE;
+ }
+
+ if (!terminal_factory_call_create_instance_sync
+ (factory,
+ build_create_options_variant (data),
+ &object_path,
+ NULL /* cancellable */,
+ &error)) {
+ g_printerr ("Error creating terminal: %s\n", error->message);
+ g_error_free (error);
+ g_object_unref (factory);
+ option_data_free (data);
+ return FALSE;
+ }
+
+ g_object_unref (factory);
+
+ receiver = terminal_receiver_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+ TERMINAL_UNIQUE_NAME,
+ object_path,
+ NULL /* cancellable */,
+ &error);
+ if (receiver == NULL) {
+ g_printerr ("Failed to create proxy for terminal: %s\n", error->message);
+ g_error_free (error);
+ g_free (object_path);
+ option_data_free (data);
+ return FALSE;
+ }
+
+ g_free (object_path);
+
+ if (!terminal_receiver_call_exec_sync (receiver,
+ build_exec_options_variant (data),
+ g_variant_new_bytestring_array ((const char * const *) data->exec_argv, data->exec_argc),
+ NULL /* cancellable */,
+ &error)) {
+ g_printerr ("Error: %s\n", error->message);
+ g_error_free (error);
+ g_object_unref (receiver);
+ option_data_free (data);
+ return FALSE;
+ }
+
+ option_data_free (data);
+
+ g_object_unref (receiver);
+
+ return TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gchar *
+pick_word_at (const gchar *s,
+ gint cursor,
+ gint *out_word_begins_at)
+{
+ gint begin;
+ gint end;
+
+ if (s[0] == '\0')
+ {
+ if (out_word_begins_at != NULL)
+ *out_word_begins_at = -1;
+ return NULL;
+ }
+
+ if (g_ascii_isspace (s[cursor]) && ((cursor > 0 && g_ascii_isspace(s[cursor-1])) || cursor == 0))
+ {
+ if (out_word_begins_at != NULL)
+ *out_word_begins_at = cursor;
+ return g_strdup ("");
+ }
+
+ while (!g_ascii_isspace (s[cursor - 1]) && cursor > 0)
+ cursor--;
+ begin = cursor;
+
+ end = begin;
+ while (!g_ascii_isspace (s[end]) && s[end] != '\0')
+ end++;
+
+ if (out_word_begins_at != NULL)
+ *out_word_begins_at = begin;
+
+ return g_strndup (s + begin, end - begin);
+}
+
+gint
+main (gint argc, gchar *argv[])
+{
+ int ret;
+ const gchar *command;
+ gboolean request_completion;
+ gchar *completion_cur;
+ gchar *completion_prev;
+
+ setlocale (LC_ALL, "");
+
+ bindtextdomain (GETTEXT_PACKAGE, TERM_LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ g_type_init ();
+
+ ret = EXIT_FAILURE;
+ completion_cur = NULL;
+ completion_prev = NULL;
+
+ g_type_init ();
+
+ if (argc < 2)
+ {
+ usage (&argc, &argv, FALSE);
+ goto out;
+ }
+
+ request_completion = FALSE;
+
+ //completion_debug ("---- argc=%d --------------------------------------------------------", argc);
+
+ again:
+ command = argv[1];
+ if (g_strcmp0 (command, "help") == 0)
+ {
+ if (request_completion)
+ {
+ /* do nothing */
+ }
+ else
+ {
+ usage (&argc, &argv, TRUE);
+ ret = EXIT_SUCCESS;
+ }
+ goto out;
+ }
+ else if (g_strcmp0 (command, "open") == 0)
+ {
+ if (handle_open (&argc,
+ &argv,
+ request_completion,
+ completion_cur,
+ completion_prev))
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+ else if (g_strcmp0 (command, "complete") == 0 && argc == 4 && !request_completion)
+ {
+ const gchar *completion_line;
+ gchar **completion_argv;
+ gint completion_argc;
+ gint completion_point;
+ gchar *endp;
+ gint cur_begin;
+
+ request_completion = TRUE;
+
+ completion_line = argv[2];
+ completion_point = strtol (argv[3], &endp, 10);
+ if (endp == argv[3] || *endp != '\0')
+ goto out;
+
+#if 0
+ completion_debug ("completion_point=%d", completion_point);
+ completion_debug ("----");
+ completion_debug (" 0123456789012345678901234567890123456789012345678901234567890123456789");
+ completion_debug ("`%s'", completion_line);
+ completion_debug (" %*s^",
+ completion_point, "");
+ completion_debug ("----");
+#endif
+
+ if (!g_shell_parse_argv (completion_line,
+ &completion_argc,
+ &completion_argv,
+ NULL))
+ {
+ /* it's very possible the command line can't be parsed (for
+ * example, missing quotes etc) - in that case, we just
+ * don't autocomplete at all
+ */
+ goto out;
+ }
+
+ /* compute cur and prev */
+ completion_prev = NULL;
+ completion_cur = pick_word_at (completion_line, completion_point, &cur_begin);
+ if (cur_begin > 0)
+ {
+ gint prev_end;
+ for (prev_end = cur_begin - 1; prev_end >= 0; prev_end--)
+ {
+ if (!g_ascii_isspace (completion_line[prev_end]))
+ {
+ completion_prev = pick_word_at (completion_line, prev_end, NULL);
+ break;
+ }
+ }
+ }
+#if 0
+ completion_debug (" cur=`%s'", completion_cur);
+ completion_debug ("prev=`%s'", completion_prev);
+#endif
+
+ argc = completion_argc;
+ argv = completion_argv;
+
+ ret = EXIT_SUCCESS;
+
+ goto again;
+ }
+ else
+ {
+ if (request_completion)
+ {
+ g_print ("help \nopen \n");
+ ret = EXIT_SUCCESS;
+ goto out;
+ }
+ else
+ {
+ g_printerr ("Unknown command `%s'\n", command);
+ usage (&argc, &argv, FALSE);
+ goto out;
+ }
+ }
+
+ out:
+ g_free (completion_cur);
+ g_free (completion_prev);
+
+ return ret;
+}
diff --git a/src/org.gnome.Terminal.xml b/src/org.gnome.Terminal.xml
index ad00073..ba68dd5 100644
--- a/src/org.gnome.Terminal.xml
+++ b/src/org.gnome.Terminal.xml
@@ -20,12 +20,19 @@
<node>
<interface name="org.gnome.Terminal.Factory0">
<annotation name="org.gtk.GDBus.C.Name" value="Factory" />
- <method name="HandleArguments">
- <arg type="ay" name="working_directory" direction="in" />
- <arg type="ay" name="display_name" direction="in" />
- <arg type="ay" name="startup_id" direction="in" />
- <arg type="aay" name="environment" direction="in" />
- <arg type="aay" name="arguments" direction="in" />
+ <method name="CreateInstance">
+ <arg type="a{sv}" name="options" direction="in" />
+ <arg type="o" name="receiver" direction="out" />
+ </method>
+ </interface>
+
+ <interface name="org.gnome.Terminal.Terminal0">
+ <annotation name="org.gtk.GDBus.C.Name" value="Receiver" />
+ <method name="Exec">
+ <arg type="a{sv}" name="options" direction="in" />
+ <arg type="aay" name="arguments" direction="in">
+ <annotation name="org.gtk.GDBus.C.ForceGVariant" value="true" />
+ </arg>
</method>
</interface>
</node>
diff --git a/src/server.c b/src/server.c
index 680a603..ba245ac 100644
--- a/src/server.c
+++ b/src/server.c
@@ -31,28 +31,17 @@
#include <glib/gstdio.h>
#include <gio/gio.h>
-#ifdef WITH_SMCLIENT
-#include "eggsmclient.h"
-#endif
-
#include "terminal-accels.h"
#include "terminal-app.h"
#include "terminal-debug.h"
-#include "terminal-factory.h"
+#include "terminal-gdbus-generated.h"
#include "terminal-intl.h"
-#include "terminal-options.h"
#include "terminal-util.h"
+#include "terminal-defines.h"
-#define TERMINAL_UNIQUE_NAME "org.gnome.Terminal"
-
-#define TERMINAL_OBJECT_PATH_PREFIX "/org/gnome/Terminal"
-#define TERMINAL_OBJECT_INTERFACE_PREFIX "org.gnome.Terminal"
-
-#define TERMINAL_FACTORY_OBJECT_PATH TERMINAL_OBJECT_PATH_PREFIX "/Factory0"
-#define TERMINAL_FACTORY_INTERFACE_NAME TERMINAL_OBJECT_INTERFACE_PREFIX ".Factory0"
+GDBusObjectManagerServer *object_manager;
typedef struct {
- GDBusObjectManagerServer *manager;
int exit_code;
} OwnData;
@@ -61,7 +50,6 @@ bus_acquired_cb (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
- OwnData *data = (OwnData *) user_data;
TerminalObjectSkeleton *object;
_terminal_debug_print (TERMINAL_DEBUG_FACTORY,
@@ -69,11 +57,11 @@ bus_acquired_cb (GDBusConnection *connection,
object = terminal_object_skeleton_new (TERMINAL_FACTORY_OBJECT_PATH);
terminal_object_skeleton_set_factory (object, TERMINAL_FACTORY (terminal_app_get ()));
- g_dbus_object_manager_server_export (data->manager, G_DBUS_OBJECT_SKELETON (object));
+ g_dbus_object_manager_server_export (object_manager, G_DBUS_OBJECT_SKELETON (object));
g_object_unref (object);
/* And export the object */
- g_dbus_object_manager_server_set_connection (data->manager, connection);
+ g_dbus_object_manager_server_set_connection (object_manager, connection);
}
static void
@@ -100,6 +88,7 @@ name_lost_cb (GDBusConnection *connection,
}
data->exit_code = EXIT_FAILURE;
+
gtk_main_quit ();
}
@@ -109,7 +98,6 @@ main (int argc, char **argv)
OwnData data;
guint owner_id;
const char *home_dir;
- GdkDisplay *display;
GError *error = NULL;
setlocale (LC_ALL, "");
@@ -139,16 +127,9 @@ main (int argc, char **argv)
exit (EXIT_FAILURE);
}
- /* Unset the these env variables, so they doesn't end up
- * in the factory's env and thus in the terminals' envs.
- */
-// g_unsetenv ("DESKTOP_STARTUP_ID");
-// g_unsetenv ("GIO_LAUNCHED_DESKTOP_FILE_PID");
-// g_unsetenv ("GIO_LAUNCHED_DESKTOP_FILE");
+ object_manager = g_dbus_object_manager_server_new (TERMINAL_OBJECT_PATH_PREFIX);
data.exit_code = EXIT_FAILURE;
- data.manager = g_dbus_object_manager_server_new (TERMINAL_OBJECT_PATH_PREFIX);
-
owner_id = g_bus_own_name (G_BUS_TYPE_STARTER,
TERMINAL_UNIQUE_NAME,
G_BUS_NAME_OWNER_FLAGS_NONE,
@@ -161,7 +142,12 @@ main (int argc, char **argv)
g_bus_unown_name (owner_id);
- g_object_unref (data.manager);
+ g_dbus_object_manager_server_unexport (object_manager, TERMINAL_FACTORY_OBJECT_PATH);
+ if (data.exit_code == EXIT_SUCCESS)
+ g_dbus_connection_flush_sync (g_dbus_object_manager_server_get_connection (object_manager),
+ NULL /* cancellable */, NULL /* error */);
+ g_object_unref (object_manager);
+ object_manager = NULL;
terminal_app_shutdown ();
diff --git a/src/terminal-app.c b/src/terminal-app.c
index 1040da0..ae18e24 100644
--- a/src/terminal-app.c
+++ b/src/terminal-app.c
@@ -37,7 +37,10 @@
#include "profile-editor.h"
#include "terminal-encoding.h"
#include "terminal-schemas.h"
-#include "terminal-factory.h"
+#include "terminal-gdbus-generated.h"
+#include "terminal-controller.h"
+#include "terminal-defines.h"
+
#include <string.h>
#include <stdlib.h>
#include <time.h>
@@ -53,6 +56,10 @@
#define SYSTEM_PROXY_SETTINGS_SCHEMA "org.gnome.system.proxy"
+#define CONTROLLER_SKELETON_DATA_KEY "terminal-object-skeleton"
+
+extern GDBusObjectManagerServer *object_manager;
+
/*
* Session state is stored entirely in the RestartCommand command line.
*
@@ -74,6 +81,8 @@ struct _TerminalApp
{
TerminalFactorySkeleton parent_instance;
+ GDBusObjectManagerServer *object_manager;
+
GList *windows;
GtkWidget *new_profile_dialog;
GtkWidget *manage_profiles_dialog;
@@ -1119,52 +1128,160 @@ terminal_app_client_quit_cb (EggSMClient *client,
#endif /* WITH_SMCLIENT */
+static void
+screen_destroy_cb (GObject *screen,
+ gpointer user_data)
+{
+ GDBusObjectSkeleton *skeleton;
+ const char *object_path;
+
+ skeleton = g_object_get_data (screen, CONTROLLER_SKELETON_DATA_KEY);
+ if (skeleton == NULL)
+ return;
+
+ 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, CONTROLLER_SKELETON_DATA_KEY, NULL);
+}
+
/* Class implementation */
static gboolean
-terminal_app_handle_arguments (TerminalFactory *factory,
- GDBusMethodInvocation *invocation,
- const char *working_directory,
- const char *display_name,
- const char *startup_id,
- const char * const *envv,
- const char * const *argv)
+terminal_app_create_instance (TerminalFactory *factory,
+ GDBusMethodInvocation *invocation,
+ GVariant *options)
{
TerminalApp *app = TERMINAL_APP (factory);
- TerminalOptions *options;
- int argc;
- GError *error = NULL;
-
- _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
- "Factory invoked with working-dir='%s' display='%s' startup-id='%s'\n",
- working_directory, display_name, startup_id);
-
- argc = g_strv_length ((char **) argv);
- options = terminal_options_parse (working_directory,
- display_name,
- startup_id,
- (char **) envv,
- TRUE,
- TRUE,
- &argc, (char ***) &argv,
- &error,
- NULL);
-
- if (options == NULL) {
- g_dbus_method_invocation_take_error (invocation, error);
+ TerminalWindow *window;
+ TerminalScreen *screen;
+ TerminalController *controller;
+ TerminalObjectSkeleton *skeleton;
+ char *object_path;
+ GSettings *profile = NULL;
+ GdkScreen *gdk_screen;
+ const char *startup_id, *display_name, *working_directory;
+ int screen_number;
+ gboolean is_restored = FALSE;
+ gboolean start_fullscreen = FALSE;
+ gboolean start_maximized = FALSE;
+ gboolean force_menubar_state = FALSE, menubar_state = TRUE;
+ char *role = NULL;
+ char *title = NULL;
+ char *profile_name = NULL;
+ gboolean zoom_set = FALSE;
+ gdouble zoom = 1.0;
+ gboolean active = TRUE;
+ char *geometry = NULL;
+
+ if (!g_variant_lookup (options, "display", "^&ay", &display_name)) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+ "No display specified");
goto out;
}
- if (!terminal_app_handle_options (app, options, FALSE /* no resume */, &error)) {
- g_dbus_method_invocation_take_error (invocation, error);
+ screen_number = 0;
+ gdk_screen = terminal_app_get_screen_by_display_name (display_name, screen_number);
+ if (gdk_screen == NULL) {
+ g_dbus_method_invocation_return_error (invocation,
+ G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS,
+ "No screen %d on display \"%s\"",
+ screen_number, display_name);
goto out;
}
- terminal_factory_complete_handle_arguments (factory, invocation);
+ if (!g_variant_lookup (options, "desktop-startup-id", "^&ay", &startup_id))
+ startup_id = NULL;
+
+ if (startup_id != NULL)
+ _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
+ "Startup ID is '%s'\n", startup_id);
+ if (working_directory != NULL)
+ _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
+ "CWD is '%s'\n", working_directory);
+
+ if (!g_variant_lookup (options, "zoom", "d", &zoom))
+ zoom_set = FALSE;
+
+ window = terminal_app_new_window (app, gdk_screen);
+
+ /* Restored windows shouldn't demand attention; see bug #586308. */
+ if (is_restored)
+ terminal_window_set_is_restored (window);
+
+ if (startup_id != NULL)
+ gtk_window_set_startup_id (GTK_WINDOW (window), startup_id);
+
+ /* Overwrite the default, unique window role set in terminal_window_init */
+ if (role)
+ gtk_window_set_role (GTK_WINDOW (window), role);
+
+ if (force_menubar_state)
+ terminal_window_set_menubar_visible (window, menubar_state);
+
+ if (start_fullscreen)
+ gtk_window_fullscreen (GTK_WINDOW (window));
+ if (start_maximized)
+ gtk_window_maximize (GTK_WINDOW (window));
+
+ if (profile_name)
+ {
+ if (TRUE /* profile_is_id */)
+ profile = terminal_app_get_profile_by_name (app, profile_name);
+ else
+ profile = terminal_app_get_profile_by_visible_name (app, profile_name);
+
+ if (profile == NULL)
+ _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
+ "No such profile \"%s\", using default profile",
+ profile_name);
+ }
+ if (profile == NULL)
+ profile = g_object_ref (g_hash_table_lookup (app->profiles, "profile0"));
+ g_assert (profile);
+
+ screen = terminal_screen_new (profile, NULL, title, NULL, NULL,
+ zoom_set ? zoom : 1.0);
+ terminal_window_add_screen (window, screen, -1);
+ terminal_window_switch_screen (window, screen);
+ gtk_widget_grab_focus (GTK_WIDGET (screen));
+
+ // FIXMEchpe make this better!
+ object_path = g_strdup_printf (TERMINAL_CONTROLLER_OBJECT_PATH_PREFIX "/%u", (guint)g_random_int ());
+
+ skeleton = terminal_object_skeleton_new (object_path);
+ controller = terminal_controller_new (screen);
+ terminal_object_skeleton_set_receiver (skeleton, TERMINAL_RECEIVER (controller));
+ g_object_unref (controller);
+
+ g_dbus_object_manager_server_export (object_manager, G_DBUS_OBJECT_SKELETON (skeleton));
+ g_object_set_data_full (G_OBJECT (screen), CONTROLLER_SKELETON_DATA_KEY,
+ skeleton, (GDestroyNotify) g_object_unref);
+ g_signal_connect (screen, "destroy",
+ G_CALLBACK (screen_destroy_cb), app);
+
+ if (active)
+ terminal_window_switch_screen (window, screen);
+
+ if (geometry)
+ {
+ _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
+ "[window %p] applying geometry %s\n",
+ window, geometry);
+
+ if (!terminal_window_parse_geometry (window, geometry))
+ _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
+ "Invalid geometry string \"%s\"", geometry);
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+
+ terminal_factory_complete_create_instance (factory, invocation, object_path);
+
+ g_free (object_path);
+ g_object_unref (profile);
out:
- if (options)
- terminal_options_free (options);
return TRUE; /* handled */
}
@@ -1172,7 +1289,7 @@ out:
static void
terminal_factory_iface_init (TerminalFactoryIface *iface)
{
- iface->handle_handle_arguments = terminal_app_handle_arguments;
+ iface->handle_create_instance = terminal_app_create_instance;
}
G_DEFINE_TYPE_WITH_CODE (TerminalApp, terminal_app, TERMINAL_TYPE_FACTORY_SKELETON,
@@ -1345,163 +1462,6 @@ terminal_app_shutdown (void)
g_settings_sync ();
}
-/**
- * terminal_app_handle_options:
- * @app:
- * @options: a #TerminalOptions
- * @allow_resume: whether to merge the terminal configuration from the
- * saved session on resume
- * @error: a #GError to fill in
- *
- * Processes @options. It loads or saves the terminal configuration, or
- * opens the specified windows and tabs.
- *
- * Returns: %TRUE if @options could be successfully handled, or %FALSE on
- * error
- */
-gboolean
-terminal_app_handle_options (TerminalApp *app,
- TerminalOptions *options,
- gboolean allow_resume,
- GError **error)
-{
- GList *lw;
- GdkScreen *gdk_screen;
-
- gdk_screen = terminal_app_get_screen_by_display_name (options->display_name,
- options->screen_number);
-
- if (options->save_config)
- {
- if (options->remote_arguments)
- return terminal_app_save_config_file (app, options->config_file, error);
-
- g_set_error_literal (error, TERMINAL_OPTION_ERROR, TERMINAL_OPTION_ERROR_NOT_IN_FACTORY,
- "Cannot use \"--save-config\" when starting the factory process");
- return FALSE;
- }
-
- if (options->load_config)
- {
- GKeyFile *key_file;
- gboolean result;
-
- key_file = g_key_file_new ();
- result = g_key_file_load_from_file (key_file, options->config_file, 0, error) &&
- terminal_options_merge_config (options, key_file, SOURCE_DEFAULT, error);
- g_key_file_free (key_file);
-
- if (!result)
- return FALSE;
-
- /* fall-through on success */
- }
-
-#ifdef WITH_SMCLIENT
-{
- EggSMClient *sm_client;
-
- sm_client = egg_sm_client_get ();
-
- if (allow_resume && egg_sm_client_is_resumed (sm_client))
- {
- GKeyFile *key_file;
-
- key_file = egg_sm_client_get_state_file (sm_client);
- if (key_file != NULL &&
- !terminal_options_merge_config (options, key_file, SOURCE_SESSION, error))
- return FALSE;
- }
-}
-#endif
-
- /* Make sure we open at least one window */
- terminal_options_ensure_window (options);
-
- if (options->startup_id != NULL)
- _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
- "Startup ID is '%s'\n",
- options->startup_id);
-
- for (lw = options->initial_windows; lw != NULL; lw = lw->next)
- {
- InitialWindow *iw = lw->data;
- TerminalWindow *window;
- GList *lt;
-
- g_assert (iw->tabs);
-
- /* Create & setup new window */
- window = terminal_app_new_window (app, gdk_screen);
-
- /* Restored windows shouldn't demand attention; see bug #586308. */
- if (iw->source_tag == SOURCE_SESSION)
- terminal_window_set_is_restored (window);
-
- if (options->startup_id != NULL)
- gtk_window_set_startup_id (GTK_WINDOW (window), options->startup_id);
-
- /* Overwrite the default, unique window role set in terminal_window_init */
- if (iw->role)
- gtk_window_set_role (GTK_WINDOW (window), iw->role);
-
- if (iw->force_menubar_state)
- terminal_window_set_menubar_visible (window, iw->menubar_state);
-
- if (iw->start_fullscreen)
- gtk_window_fullscreen (GTK_WINDOW (window));
- if (iw->start_maximized)
- gtk_window_maximize (GTK_WINDOW (window));
-
- /* Now add the tabs */
- for (lt = iw->tabs; lt != NULL; lt = lt->next)
- {
- InitialTab *it = lt->data;
- GSettings *profile = NULL;
- TerminalScreen *screen;
-
- if (it->profile)
- {
- if (it->profile_is_id)
- profile = terminal_app_get_profile_by_name (app, it->profile);
- else
- profile = terminal_app_get_profile_by_visible_name (app, it->profile);
-
- if (profile == NULL)
- g_printerr (_("No such profile \"%s\", using default profile\n"), it->profile);
- }
- if (profile == NULL)
- profile = g_object_ref (g_hash_table_lookup (app->profiles, "profile0"));
- g_assert (profile);
-
- screen = terminal_app_new_terminal (app, window, profile,
- it->exec_argv ? it->exec_argv : options->exec_argv,
- it->title ? it->title : options->default_title,
- it->working_dir ? it->working_dir : options->default_working_dir,
- options->env,
- it->zoom_set ? it->zoom : options->zoom);
- g_object_unref (profile);
-
- if (it->active)
- terminal_window_switch_screen (window, screen);
- }
-
- if (iw->geometry)
- {
- _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
- "[window %p] applying geometry %s\n",
- window, iw->geometry);
-
- if (!terminal_window_parse_geometry (window, iw->geometry))
- g_printerr (_("Invalid geometry string \"%s\"\n"), iw->geometry);
- }
-
- gtk_window_present (GTK_WINDOW (window));
- }
-
- return TRUE;
-}
-
TerminalWindow *
terminal_app_new_window (TerminalApp *app,
GdkScreen *screen)
@@ -1542,6 +1502,9 @@ terminal_app_new_terminal (TerminalApp *app,
terminal_window_switch_screen (window, screen);
gtk_widget_grab_focus (GTK_WIDGET (screen));
+ /* Launch the child on idle */
+ _terminal_screen_launch_child_on_idle (screen);
+
return screen;
}
diff --git a/src/terminal-app.h b/src/terminal-app.h
index 13a2231..5ec789b 100644
--- a/src/terminal-app.h
+++ b/src/terminal-app.h
@@ -25,7 +25,6 @@
#include "terminal-encoding.h"
#include "terminal-screen.h"
-#include "terminal-options.h"
G_BEGIN_DECLS
@@ -77,10 +76,7 @@ TerminalApp* terminal_app_get (void);
void terminal_app_shutdown (void);
-gboolean terminal_app_handle_options (TerminalApp *app,
- TerminalOptions *options,
- gboolean allow_resume,
- GError **error);
+GDBusObjectManagerServer *terminal_app_get_object_manager (TerminalApp *app);
void terminal_app_edit_profile (TerminalApp *app,
GSettings *profile,
diff --git a/src/terminal-controller.c b/src/terminal-controller.c
new file mode 100644
index 0000000..657ecd0
--- /dev/null
+++ b/src/terminal-controller.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright  2011 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope controller 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "terminal-controller.h"
+#include "terminal-debug.h"
+
+#define TERMINAL_CONTROLLER_GET_PRIVATE(controller)(G_TYPE_INSTANCE_GET_PRIVATE ((controller), TERMINAL_TYPE_CONTROLLER, TerminalControllerPrivate))
+
+struct _TerminalControllerPrivate {
+ TerminalScreen *screen; /* unowned! */
+};
+
+enum {
+ PROP_0,
+ PROP_SCREEN
+};
+
+/* helper functions */
+
+static void
+terminal_controller_set_screen (TerminalController *controller,
+ TerminalScreen *screen)
+{
+ TerminalControllerPrivate *priv;
+
+ g_return_if_fail (TERMINAL_IS_CONTROLLER (controller));
+ g_return_if_fail (screen == NULL || TERMINAL_IS_SCREEN (screen));
+
+ priv = controller->priv;
+ if (priv->screen == screen)
+ return;
+
+ if (priv->screen) {
+ g_signal_handlers_disconnect_by_func (priv->screen,
+ G_CALLBACK (_terminal_controller_unset_screen),
+ controller);
+ }
+
+ priv->screen = screen;
+ if (screen) {
+ g_signal_connect_swapped (screen, "destroy",
+ G_CALLBACK (_terminal_controller_unset_screen),
+ controller);
+ }
+
+ g_object_notify (G_OBJECT (controller), "screen");
+}
+
+/* Class implementation */
+
+static gboolean
+terminal_controller_exec (TerminalReceiver *receiver,
+ GDBusMethodInvocation *invocation,
+ GVariant *options,
+ GVariant *arguments)
+{
+ TerminalController *controller = TERMINAL_CONTROLLER (receiver);
+ TerminalControllerPrivate *priv = controller->priv;
+ const char *working_directory;
+ char **exec_argv, **envv;
+ gsize exec_argc;
+ GError *error;
+
+ if (priv->screen == NULL) {
+ g_dbus_method_invocation_return_error_literal (invocation,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Terminal already closed");
+ goto out;
+ }
+
+ if (!g_variant_lookup (options, "cwd", "^&ay", &working_directory))
+ working_directory = NULL;
+ if (!g_variant_lookup (options, "environ", "^a&ay", &envv))
+ envv = NULL;
+
+ if (working_directory != NULL)
+ _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
+ "CWD is '%s'\n", working_directory);
+
+ exec_argv = (char **) g_variant_get_bytestring_array (arguments, &exec_argc);
+
+ error = NULL;
+ if (!terminal_screen_exec (priv->screen,
+ exec_argc > 0 ? exec_argv : NULL,
+ envv,
+ working_directory,
+ &error)) {
+ g_dbus_method_invocation_take_error (invocation, error);
+ } else {
+ terminal_receiver_complete_exec (receiver, invocation);
+ }
+
+ g_free (exec_argv);
+ g_free (envv);
+
+out:
+
+ return TRUE; /* handled */
+}
+
+static void
+terminal_controller_iface_init (TerminalReceiverIface *iface)
+{
+ iface->handle_exec = terminal_controller_exec;
+}
+
+G_DEFINE_TYPE_WITH_CODE (TerminalController, terminal_controller, TERMINAL_TYPE_RECEIVER_SKELETON,
+ G_IMPLEMENT_INTERFACE (TERMINAL_TYPE_RECEIVER, terminal_controller_iface_init))
+
+static void
+terminal_controller_init (TerminalController *controller)
+{
+ controller->priv = TERMINAL_CONTROLLER_GET_PRIVATE (controller);
+}
+
+static void
+terminal_controller_dispose (GObject *object)
+{
+ TerminalController *controller = TERMINAL_CONTROLLER (object);
+
+ terminal_controller_set_screen (controller, NULL);
+
+ G_OBJECT_CLASS (terminal_controller_parent_class)->dispose (object);
+}
+
+static void
+terminal_controller_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TerminalController *controller = TERMINAL_CONTROLLER (object);
+
+ switch (prop_id) {
+ case PROP_SCREEN:
+ g_value_set_object (value, terminal_controller_get_screen (controller));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+terminal_controller_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TerminalController *controller = TERMINAL_CONTROLLER (object);
+
+ switch (prop_id) {
+ case PROP_SCREEN:
+ terminal_controller_set_screen (controller, g_value_get_object (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+terminal_controller_class_init (TerminalControllerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->dispose = terminal_controller_dispose;
+ gobject_class->get_property = terminal_controller_get_property;
+ gobject_class->set_property = terminal_controller_set_property;
+
+ g_object_class_install_property
+ (gobject_class,
+ PROP_SCREEN,
+ g_param_spec_object ("screen", NULL, NULL,
+ TERMINAL_TYPE_SCREEN,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_type_class_add_private (gobject_class, sizeof (TerminalControllerPrivate));
+}
+
+/* public API */
+
+/**
+ * terminal_controller_new:
+ * @screen: a #TerminalScreen
+ *
+ * Returns: a new #TerminalController for @screen
+ */
+TerminalController *
+terminal_controller_new (TerminalScreen *screen)
+{
+ return g_object_new (TERMINAL_TYPE_CONTROLLER,
+ "screen", screen,
+ NULL);
+}
+
+/**
+ * terminal_controller_get_screen:
+ * @controller: a #TerminalController
+ *
+ * Returns: (transfer none): the controller's #TerminalScreen, or %NULL
+ */
+TerminalScreen *
+terminal_controller_get_screen (TerminalController *controller)
+{
+ g_return_val_if_fail (TERMINAL_IS_CONTROLLER (controller), NULL);
+
+ return controller->priv->screen;
+}
+
+/**
+ * terminal_controller_get_screen:
+ * @controller: a #TerminalController
+ *
+ * Unsets the controllers #TerminalScreen.
+ */
+void
+_terminal_controller_unset_screen (TerminalController *controller)
+{
+ g_return_if_fail (TERMINAL_IS_CONTROLLER (controller));
+
+ terminal_controller_set_screen (controller, NULL);
+}
\ No newline at end of file
diff --git a/src/terminal-controller.h b/src/terminal-controller.h
new file mode 100644
index 0000000..a7fa3f3
--- /dev/null
+++ b/src/terminal-controller.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright  2011 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope controller 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TERMINAL_CONTROLLER_H
+#define TERMINAL_CONTROLLER_H
+
+#include <glib-object.h>
+
+#include "terminal-gdbus-generated.h"
+#include "terminal-screen.h"
+
+G_BEGIN_DECLS
+
+#define TERMINAL_TYPE_CONTROLLER (terminal_controller_get_type ())
+#define TERMINAL_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TERMINAL_TYPE_CONTROLLER, TerminalController))
+#define TERMINAL_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TERMINAL_TYPE_CONTROLLER, TerminalControllerClass))
+#define TERMINAL_IS_CONTROLLER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TERMINAL_TYPE_CONTROLLER))
+#define TERMINAL_IS_CONTROLLER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TERMINAL_TYPE_CONTROLLER))
+#define TERMINAL_CONTROLLER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TERMINAL_TYPE_CONTROLLER, TerminalControllerClass))
+
+typedef struct _TerminalController TerminalController;
+typedef struct _TerminalControllerClass TerminalControllerClass;
+typedef struct _TerminalControllerPrivate TerminalControllerPrivate;
+
+struct _TerminalController
+{
+ TerminalReceiverSkeleton parent_instance;
+
+ /*< private >*/
+ TerminalControllerPrivate *priv;
+};
+
+struct _TerminalControllerClass
+{
+ TerminalReceiverSkeletonClass parent_class;
+};
+
+GType terminal_controller_get_type (void);
+
+TerminalController *terminal_controller_new (TerminalScreen *screen);
+
+TerminalScreen *terminal_controller_get_screen (TerminalController *controller);
+
+void _terminal_controller_unset_screen (TerminalController *controller);
+
+G_END_DECLS
+
+#endif /* !TERMINAL_CONTROLLER_H */
diff --git a/src/terminal-defines.h b/src/terminal-defines.h
new file mode 100644
index 0000000..aebcb9b
--- /dev/null
+++ b/src/terminal-defines.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright  2011 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope controller 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef TERMINAL_DEFINES_H
+#define TERMINAL_DEFINES_H
+
+G_BEGIN_DECLS
+
+#define TERMINAL_UNIQUE_NAME "org.gnome.Terminal"
+
+#define TERMINAL_OBJECT_PATH_PREFIX "/org/gnome/Terminal"
+#define TERMINAL_OBJECT_INTERFACE_PREFIX "org.gnome.Terminal"
+
+#define TERMINAL_FACTORY_OBJECT_PATH TERMINAL_OBJECT_PATH_PREFIX "/Factory0"
+#define TERMINAL_FACTORY_INTERFACE_NAME TERMINAL_OBJECT_INTERFACE_PREFIX ".Factory0"
+
+#define TERMINAL_CONTROLLER_OBJECT_PATH_PREFIX TERMINAL_OBJECT_PATH_PREFIX "/Terminals"
+#define TEMRINAL_CONTROLLER_INTERFACE_NAME TERMINAL_OBJECT_INTERFACE_PREFIX ".Terminal0"
+
+G_END_DECLS
+
+#endif /* !TERMINAL_DEFINES_H */
diff --git a/src/terminal-options.c b/src/terminal-options.c
index a4e179c..5e5a62a 100644
--- a/src/terminal-options.c
+++ b/src/terminal-options.c
@@ -1042,7 +1042,7 @@ get_goption_context (TerminalOptions *options)
0,
G_OPTION_ARG_CALLBACK,
option_geometry_callback,
- N_("Set the window size; for example: 80x24, or 80x24+200+200 (ROWSxCOLS+X+Y)"),
+ N_("Set the window size; for example: 80x24, or 80x24+200+200 (COLSxROWS+X+Y)"),
N_("GEOMETRY")
},
{
diff --git a/src/terminal-options.h b/src/terminal-options.h
index 30f455f..536d6e9 100644
--- a/src/terminal-options.h
+++ b/src/terminal-options.h
@@ -87,6 +87,7 @@ typedef struct
#define TERMINAL_OPTION_ERROR (g_quark_from_static_string ("terminal-option-error"))
typedef enum {
+ TERMINAL_OPTION_ERROR_NOT_SUPPORTED,
TERMINAL_OPTION_ERROR_NOT_IN_FACTORY,
TERMINAL_OPTION_ERROR_EXCLUSIVE_OPTIONS,
TERMINAL_OPTION_ERROR_INVALID_CONFIG_FILE,
diff --git a/src/terminal-screen.c b/src/terminal-screen.c
index eec3bf4..6394284 100644
--- a/src/terminal-screen.c
+++ b/src/terminal-screen.c
@@ -116,7 +116,7 @@ static void terminal_screen_change_font (TerminalScreen *screen);
static gboolean terminal_screen_popup_menu (GtkWidget *widget);
static gboolean terminal_screen_button_press (GtkWidget *widget,
GdkEventButton *event);
-static void terminal_screen_launch_child_on_idle (TerminalScreen *screen);
+static gboolean terminal_screen_do_exec (TerminalScreen *screen, GError **error);
static void terminal_screen_child_exited (VteTerminal *terminal);
static void terminal_screen_window_title_changed (VteTerminal *vte_terminal,
@@ -676,12 +676,34 @@ terminal_screen_new (GSettings *profile,
terminal_screen_set_font_scale (screen, zoom);
terminal_screen_set_font (screen);
- /* Launch the child on idle */
- terminal_screen_launch_child_on_idle (screen);
-
return screen;
}
+gboolean
+terminal_screen_exec (TerminalScreen *screen,
+ char **argv,
+ char **envv,
+ const char *cwd,
+ GError **error)
+{
+ TerminalScreenPrivate *priv;
+
+ g_return_val_if_fail (TERMINAL_IS_SCREEN (screen), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ priv = screen->priv;
+
+ terminal_screen_set_initial_environment (screen, envv);
+
+ if (argv)
+ terminal_screen_set_override_command (screen, argv);
+
+ g_free (priv->initial_working_directory);
+ priv->initial_working_directory = g_strdup (cwd);
+
+ return terminal_screen_do_exec (screen, error);
+}
+
const char*
terminal_screen_get_raw_title (TerminalScreen *screen)
{
@@ -1296,7 +1318,7 @@ info_bar_response_cb (GtkWidget *info_bar,
break;
case RESPONSE_RELAUNCH:
gtk_widget_destroy (info_bar);
- terminal_screen_launch_child_on_idle (screen);
+ _terminal_screen_launch_child_on_idle (screen);
break;
case RESPONSE_EDIT_PROFILE:
terminal_app_edit_profile (terminal_app_get (),
@@ -1311,7 +1333,8 @@ info_bar_response_cb (GtkWidget *info_bar,
}
static gboolean
-terminal_screen_launch_child_cb (TerminalScreen *screen)
+terminal_screen_do_exec (TerminalScreen *screen,
+ GError **error)
{
TerminalScreenPrivate *priv = screen->priv;
VteTerminal *terminal = VTE_TERMINAL (screen);
@@ -1372,10 +1395,10 @@ terminal_screen_launch_child_cb (TerminalScreen *screen)
gtk_info_bar_set_default_response (GTK_INFO_BAR (info_bar), GTK_RESPONSE_CANCEL);
gtk_widget_show (info_bar);
- g_error_free (err);
g_strfreev (env);
g_free (shell);
+ g_propagate_error (error, err);
return FALSE;
}
@@ -1386,11 +1409,18 @@ terminal_screen_launch_child_cb (TerminalScreen *screen)
g_strfreev (argv);
g_strfreev (env);
+ return TRUE;
+}
+
+static gboolean
+terminal_screen_launch_child_cb (TerminalScreen *screen)
+{
+ terminal_screen_do_exec (screen, NULL /* don't care */);
return FALSE; /* don't run again */
}
-static void
-terminal_screen_launch_child_on_idle (TerminalScreen *screen)
+void
+_terminal_screen_launch_child_on_idle (TerminalScreen *screen)
{
TerminalScreenPrivate *priv = screen->priv;
@@ -1732,7 +1762,7 @@ terminal_screen_child_exited (VteTerminal *terminal)
g_signal_emit (screen, signals[CLOSE_SCREEN], 0);
break;
case TERMINAL_EXIT_RESTART:
- terminal_screen_launch_child_on_idle (screen);
+ _terminal_screen_launch_child_on_idle (screen);
break;
case TERMINAL_EXIT_HOLD: {
GtkWidget *info_bar;
diff --git a/src/terminal-screen.h b/src/terminal-screen.h
index 038326a..bba7d8a 100644
--- a/src/terminal-screen.h
+++ b/src/terminal-screen.h
@@ -81,6 +81,14 @@ TerminalScreen *terminal_screen_new (GSettings *profile,
char **child_env,
double zoom);
+gboolean terminal_screen_exec (TerminalScreen *screen,
+ char **argv,
+ char **envv,
+ const char *cwd,
+ GError **error);
+
+void _terminal_screen_launch_child_on_idle (TerminalScreen *screen);
+
void terminal_screen_set_profile (TerminalScreen *screen,
GSettings *profile);
GSettings* terminal_screen_get_profile (TerminalScreen *screen);
diff --git a/src/terminal.c b/src/terminal.c
index 0ebd90f..2d8b2d3 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -41,15 +41,180 @@
#include "terminal-debug.h"
#include "terminal-intl.h"
#include "terminal-options.h"
-#include "terminal-factory.h"
+#include "terminal-gdbus-generated.h"
+#include "terminal-defines.h"
+
+/**
+ * handle_options:
+ * @app:
+ * @options: a #TerminalOptions
+ * @allow_resume: whether to merge the terminal configuration from the
+ * saved session on resume
+ * @error: a #GError to fill in
+ *
+ * Processes @options. It loads or saves the terminal configuration, or
+ * opens the specified windows and tabs.
+ *
+ * Returns: %TRUE if @options could be successfully handled, or %FALSE on
+ * error
+ */
+static gboolean
+handle_options (TerminalFactory *factory,
+ TerminalOptions *options,
+ char **envv,
+ gboolean allow_resume,
+ GError **error)
+{
+#if 0
+ GList *lw;
+ GdkScreen *gdk_screen;
+
+ gdk_screen = terminal_app_get_screen_by_display_name (options->display_name,
+ options->screen_number);
+
+ if (options->save_config)
+ {
+#if 0
+ if (options->remote_arguments)
+ return terminal_app_save_config_file (app, options->config_file, error);
+
+ g_set_error_literal (error, TERMINAL_OPTION_ERROR, TERMINAL_OPTION_ERROR_NOT_IN_FACTORY,
+ "Cannot use \"--save-config\" when starting the factory process");
+ return FALSE;
+#endif
+ g_set_error (error, TERMINAL_OPTION_ERROR, TERMINAL_OPTION_ERROR_NOT_SUPPORTED,
+ "Not supported anymore");
+ return FALSE;
+ }
+
+ if (options->load_config)
+ {
+ GKeyFile *key_file;
+ gboolean result;
+
+ key_file = g_key_file_new ();
+ result = g_key_file_load_from_file (key_file, options->config_file, 0, error) &&
+ terminal_options_merge_config (options, key_file, SOURCE_DEFAULT, error);
+ g_key_file_free (key_file);
+
+ if (!result)
+ return FALSE;
+
+ /* fall-through on success */
+ }
+
+#ifdef WITH_SMCLIENT
+{
+ EggSMClient *sm_client;
+
+ sm_client = egg_sm_client_get ();
+
+ if (allow_resume && egg_sm_client_is_resumed (sm_client))
+ {
+ GKeyFile *key_file;
+
+ key_file = egg_sm_client_get_state_file (sm_client);
+ if (key_file != NULL &&
+ !terminal_options_merge_config (options, key_file, SOURCE_SESSION, error))
+ return FALSE;
+ }
+}
+#endif
+
+ /* Make sure we open at least one window */
+ terminal_options_ensure_window (options);
+
+ if (options->startup_id != NULL)
+ _terminal_debug_print (TERMINAL_DEBUG_FACTORY,
+ "Startup ID is '%s'\n",
+ options->startup_id);
+
+ for (lw = options->initial_windows; lw != NULL; lw = lw->next)
+ {
+ InitialWindow *iw = lw->data;
+ GList *lt;
+#if 0
+ TerminalWindow *window;
-#define TERMINAL_UNIQUE_NAME "org.gnome.Terminal"
+ g_assert (iw->tabs);
-#define TERMINAL_OBJECT_PATH_PREFIX "/org/gnome/Terminal"
-#define TERMINAL_OBJECT_INTERFACE_PREFIX "org.gnome.Terminal"
+ /* Create & setup new window */
+ window = terminal_app_new_window (app, gdk_screen);
-#define TERMINAL_FACTORY_OBJECT_PATH TERMINAL_OBJECT_PATH_PREFIX "/Factory0"
-#define TERMINAL_FACTORY_INTERFACE_NAME TERMINAL_OBJECT_INTERFACE_PREFIX ".Factory0"
+ /* Restored windows shouldn't demand attention; see bug #586308. */
+ if (iw->source_tag == SOURCE_SESSION)
+ terminal_window_set_is_restored (window);
+
+ if (options->startup_id != NULL)
+ gtk_window_set_startup_id (GTK_WINDOW (window), options->startup_id);
+
+ /* Overwrite the default, unique window role set in terminal_window_init */
+ if (iw->role)
+ gtk_window_set_role (GTK_WINDOW (window), iw->role);
+
+ if (iw->force_menubar_state)
+ terminal_window_set_menubar_visible (window, iw->menubar_state);
+
+ if (iw->start_fullscreen)
+ gtk_window_fullscreen (GTK_WINDOW (window));
+ if (iw->start_maximized)
+ gtk_window_maximize (GTK_WINDOW (window));
+#endif
+
+ /* Now add the tabs */
+ for (lt = iw->tabs; lt != NULL; lt = lt->next)
+ {
+ InitialTab *it = lt->data;
+#if 0
+ GSettings *profile = NULL;
+ TerminalScreen *screen;
+
+ if (it->profile)
+ {
+ if (it->profile_is_id)
+ profile = terminal_app_get_profile_by_name (app, it->profile);
+ else
+ profile = terminal_app_get_profile_by_visible_name (app, it->profile);
+
+ if (profile == NULL)
+ g_printerr (_("No such profile \"%s\", using default profile\n"), it->profile);
+ }
+ if (profile == NULL)
+ profile = g_object_ref (g_hash_table_lookup (app->profiles, "profile0"));
+ g_assert (profile);
+
+ screen = terminal_app_new_terminal (app, window, profile,
+ it->exec_argv ? it->exec_argv : options->exec_argv,
+ it->title ? it->title : options->default_title,
+ it->working_dir ? it->working_dir : options->default_working_dir,
+ options->env,
+ it->zoom_set ? it->zoom : options->zoom);
+ g_object_unref (profile);
+
+ if (it->active)
+ terminal_window_switch_screen (window, screen);
+#endif
+ }
+
+#if 0
+ if (iw->geometry)
+ {
+ _terminal_debug_print (TERMINAL_DEBUG_GEOMETRY,
+ "[window %p] applying geometry %s\n",
+ window, iw->geometry);
+
+ if (!terminal_window_parse_geometry (window, iw->geometry))
+ g_printerr (_("Invalid geometry string \"%s\"\n"), iw->geometry);
+ }
+
+ gtk_window_present (GTK_WINDOW (window));
+#endif
+ }
+
+ return TRUE;
+#endif
+ return FALSE;
+}
/* Copied from libnautilus/nautilus-program-choosing.c; Needed in case
* we have no DESKTOP_STARTUP_ID (with its accompanying timestamp).
@@ -196,6 +361,7 @@ main (int argc, char **argv)
}
envv = g_get_environ ();
+#if 0
if (!terminal_factory_call_handle_arguments_sync (factory,
working_directory ? working_directory : "",
display_name ? display_name : "",
@@ -207,6 +373,10 @@ main (int argc, char **argv)
g_printerr ("Error opening terminal: %s\n", error->message);
g_error_free (error);
} else {
+#endif
+ if (!handle_options (factory, options, envv, TRUE /* allow resume */, &error)) {
+ g_printerr ("Failed to handle arguments: %s\n", error->message);
+ } else {
exit_code = EXIT_SUCCESS;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]