]> asedeno.scripts.mit.edu Git - PuTTY.git/commitdiff
Divide the whole of gtkwin.c into three parts.
authorSimon Tatham <anakin@pobox.com>
Tue, 22 Mar 2016 21:24:30 +0000 (21:24 +0000)
committerSimon Tatham <anakin@pobox.com>
Tue, 22 Mar 2016 22:27:09 +0000 (22:27 +0000)
This lays further groundwork for the OS X GTK3 port, which is going to
have to deal with multiple sessions sharing the same process. gtkwin.c
was a bit too monolithic for this, since it included some
process-global runtime state (timers, toplevel callbacks), some
process startup stuff (gtk_init, gtk_main, argv processing) and some
per-session-window stuff.

The per-session stuff remains in gtkwin.c, with the top-level function
now being new_session_window() taking a Conf. The new gtkmain.c
contains the outer skeleton of pt_main(), handling argv processing and
one-off startup stuff like setlocale; and the new gtkcomm.c contains
the pieces of PuTTY infrastructure like timers and uxsel that are
shared between multiple sessions rather than reinstantiated per
session, which have been rewritten to use global variables rather than
fields in 'inst' (since it's now clear to me that they'll have to
apply to all the insts in existence at once).

There are still some lurking assumptions of one-session-per-process,
e.g. the use of gtk_main_quit when a session finishes, and the fact
that the config box insists on running as a separate invocation of
gtk_main so that one session's preliminary config box can't coexist
with another session already active. But this should make it possible
to at least write an OS X app good enough to start testing with, even
if it doesn't get everything quite right yet.

This change is almost entirely rearranging existing code, so it
shouldn't be seriously destabilising. But two noticeable actual
changes have happened, both pleasantly simplifying:

Firstly, the global-variables rewrite of gtkcomm.c has allowed the
post_main edifice to become a great deal simpler. Most of its
complexity was about remembering what 'inst' it had to call back to,
and in fact the right answer is that it shouldn't be calling back to
one at all. So now the post_main() called by gtkdlg.c has become the
same function as the old inst_post_main() that actually did the work,
instead of the two having to be connected by a piece of ugly plumbing.

Secondly, a piece of code that's vanished completely in this
refactoring is the temporary blocking of SIGCHLD around most of the
session setup code. This turns out to have been introduced in 2002,
_before_ I switched to using the intra-process signal pipe strategy
for SIGCHLD handling in 2003. So I now expect that we should be robust
in any case against receiving SIGCHLD at an inconvenient moment, and
hence there's no need to block it.

Recipe
unix/gtkcomm.c [new file with mode: 0644]
unix/gtkmain.c [new file with mode: 0644]
unix/gtkwin.c
unix/unix.h
unix/uxpty.c

diff --git a/Recipe b/Recipe
index b13b12f4f66cc00ac002820463ae461adebf4503..81472f37713be5251377f216f187ad17d89996ec 100644 (file)
--- a/Recipe
+++ b/Recipe
@@ -217,7 +217,8 @@ GUITERM  = TERMINAL window windlg winctrls sizetip winucs winprint
 
 # Same thing on Unix.
 UXTERM   = TERMINAL uxcfg sercfg uxucs uxprint timing callback miscucs
-GTKTERM  = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym x11misc
+GTKTERM  = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym
+        + x11misc gtkcomm
 OSXTERM  = UXTERM osxwin osxdlg osxctrls
 
 # Non-SSH back ends (putty, puttytel, plink).
@@ -294,13 +295,13 @@ puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version
 
 pterm    : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore
          + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg
-        + nogss
+        + nogss gtkmain
 putty    : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore
          + uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 xpmputty
-         + xpmpucfg
+         + xpmpucfg gtkmain
 puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH
         + uxstore uxsignal CHARSET uxputty NONSSH UXMISC xpmputty xpmpucfg
-        + nogss
+        + nogss gtkmain
 
 plink    : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal
          + ux_x11 noterm
diff --git a/unix/gtkcomm.c b/unix/gtkcomm.c
new file mode 100644 (file)
index 0000000..2888465
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ * gtkcomm.c: machinery in the GTK front end which is common to all
+ * programs that run a session in a terminal window, and also common
+ * across all _sessions_ rather than specific to one session. (Timers,
+ * uxsel etc.)
+ */
+
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <gtk/gtk.h>
+#if !GTK_CHECK_VERSION(3,0,0)
+#include <gdk/gdkkeysyms.h>
+#endif
+
+#if GTK_CHECK_VERSION(2,0,0)
+#include <gtk/gtkimmodule.h>
+#endif
+
+#define MAY_REFER_TO_GTK_IN_HEADERS
+
+#include "putty.h"
+#include "terminal.h"
+#include "gtkcompat.h"
+#include "gtkfont.h"
+#include "gtkmisc.h"
+
+#ifndef NOT_X_WINDOWS
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#endif
+
+#define CAT2(x,y) x ## y
+#define CAT(x,y) CAT2(x,y)
+#define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
+
+#if GTK_CHECK_VERSION(2,0,0)
+ASSERT(sizeof(long) <= sizeof(gsize));
+#define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)
+#define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)
+#else /* Gtk 1.2 */
+ASSERT(sizeof(long) <= sizeof(gpointer));
+#define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))
+#define GPOINTER_TO_LONG(p) ((long)(p))
+#endif
+
+/* ----------------------------------------------------------------------
+ * File descriptors and uxsel.
+ */
+
+struct uxsel_id {
+#if GTK_CHECK_VERSION(2,0,0)
+    GIOChannel *chan;
+    guint watch_id;
+#else
+    int id;
+#endif
+};
+
+#if GTK_CHECK_VERSION(2,0,0)
+gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
+                       gpointer data)
+{
+    int sourcefd = g_io_channel_unix_get_fd(source);
+    /*
+     * We must process exceptional notifications before ordinary
+     * readability ones, or we may go straight past the urgent
+     * marker.
+     */
+    if (condition & G_IO_PRI)
+        select_result(sourcefd, 4);
+    if (condition & G_IO_IN)
+        select_result(sourcefd, 1);
+    if (condition & G_IO_OUT)
+        select_result(sourcefd, 2);
+
+    return TRUE;
+}
+#else
+void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
+{
+    if (condition & GDK_INPUT_EXCEPTION)
+        select_result(sourcefd, 4);
+    if (condition & GDK_INPUT_READ)
+        select_result(sourcefd, 1);
+    if (condition & GDK_INPUT_WRITE)
+        select_result(sourcefd, 2);
+}
+#endif
+
+uxsel_id *uxsel_input_add(int fd, int rwx) {
+    uxsel_id *id = snew(uxsel_id);
+
+#if GTK_CHECK_VERSION(2,0,0)
+    int flags = 0;
+    if (rwx & 1) flags |= G_IO_IN;
+    if (rwx & 2) flags |= G_IO_OUT;
+    if (rwx & 4) flags |= G_IO_PRI;
+    id->chan = g_io_channel_unix_new(fd);
+    g_io_channel_set_encoding(id->chan, NULL, NULL);
+    id->watch_id = g_io_add_watch_full(id->chan, GDK_PRIORITY_REDRAW+1, flags,
+                                       fd_input_func, NULL, NULL);
+#else
+    int flags = 0;
+    if (rwx & 1) flags |= GDK_INPUT_READ;
+    if (rwx & 2) flags |= GDK_INPUT_WRITE;
+    if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
+    assert(flags);
+    id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
+#endif
+
+    return id;
+}
+
+void uxsel_input_remove(uxsel_id *id) {
+#if GTK_CHECK_VERSION(2,0,0)
+    g_source_remove(id->watch_id);
+    g_io_channel_unref(id->chan);
+#else
+    gdk_input_remove(id->id);
+#endif
+    sfree(id);
+}
+
+/* ----------------------------------------------------------------------
+ * Timers.
+ */
+
+static guint timer_id = 0;
+
+static gint timer_trigger(gpointer data)
+{
+    unsigned long now = GPOINTER_TO_LONG(data);
+    unsigned long next, then;
+    long ticks;
+
+    /*
+     * Destroy the timer we got here on.
+     */
+    if (timer_id) {
+       g_source_remove(timer_id);
+        timer_id = 0;
+    }
+
+    /*
+     * run_timers() may cause a call to timer_change_notify, in which
+     * case a new timer will already have been set up and left in
+     * timer_id. If it hasn't, and run_timers reports that some timing
+     * still needs to be done, we do it ourselves.
+     */
+    if (run_timers(now, &next) && !timer_id) {
+       then = now;
+       now = GETTICKCOUNT();
+       if (now - then > next - then)
+           ticks = 0;
+       else
+           ticks = next - now;
+       timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
+    }
+
+    /*
+     * Returning FALSE means 'don't call this timer again', which
+     * _should_ be redundant given that we removed it above, but just
+     * in case, return FALSE anyway.
+     */
+    return FALSE;
+}
+
+void timer_change_notify(unsigned long next)
+{
+    long ticks;
+
+    if (timer_id)
+       g_source_remove(timer_id);
+
+    ticks = next - GETTICKCOUNT();
+    if (ticks <= 0)
+       ticks = 1;                     /* just in case */
+
+    timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
+}
+
+/* ----------------------------------------------------------------------
+ * Toplevel callbacks.
+ */
+
+static guint toplevel_callback_idle_id;
+static int idle_fn_scheduled;
+
+static void notify_toplevel_callback(void *);
+
+/*
+ * Replacement code for the gtk_quit_add() function, which GTK2 - in
+ * their unbounded wisdom - deprecated without providing any usable
+ * replacement, and which we were using to ensure that our idle
+ * function for toplevel callbacks was only run from the outermost
+ * gtk_main().
+ *
+ * We must make sure that all our subsidiary calls to gtk_main() are
+ * followed by a call to post_main(), so that the idle function can be
+ * re-established when we end up back at the top level.
+ */
+void post_main(void)
+{
+    if (gtk_main_level() == 1)
+        notify_toplevel_callback(NULL);
+}
+
+static gint idle_toplevel_callback_func(gpointer data)
+{
+    if (gtk_main_level() > 1) {
+        /*
+         * We don't run callbacks if we're in the middle of a
+         * subsidiary gtk_main. So unschedule this idle function; it
+         * will be rescheduled by post_main() when we come back up a
+         * level, which is the earliest we might actually do
+         * something.
+         */
+        if (idle_fn_scheduled) {      /* double-check, just in case */
+            g_source_remove(toplevel_callback_idle_id);
+            idle_fn_scheduled = FALSE;
+        }
+    } else {
+        run_toplevel_callbacks();
+    }
+
+    /*
+     * If we've emptied our toplevel callback queue, unschedule
+     * ourself. Otherwise, leave ourselves pending so we'll be called
+     * again to deal with more callbacks after another round of the
+     * event loop.
+     */
+    if (!toplevel_callback_pending() && idle_fn_scheduled) {
+        g_source_remove(toplevel_callback_idle_id);
+        idle_fn_scheduled = FALSE;
+    }
+
+    return TRUE;
+}
+
+static void notify_toplevel_callback(void *frontend)
+{
+    if (!idle_fn_scheduled) {
+        toplevel_callback_idle_id =
+            g_idle_add(idle_toplevel_callback_func, NULL);
+        idle_fn_scheduled = TRUE;
+    }
+}
+
+/* ----------------------------------------------------------------------
+ * Setup function. The real main program must call this.
+ */
+
+void gtkcomm_setup(void)
+{
+    uxsel_init();
+    request_callback_notifications(notify_toplevel_callback, NULL);
+}
diff --git a/unix/gtkmain.c b/unix/gtkmain.c
new file mode 100644 (file)
index 0000000..5e8b9fe
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * gtkmain.c: the common main-program code between the straight-up
+ * Unix PuTTY and pterm, which they do not share with the
+ * multi-session gtkapp.c.
+ */
+
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <errno.h>
+#include <locale.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <gtk/gtk.h>
+#if !GTK_CHECK_VERSION(3,0,0)
+#include <gdk/gdkkeysyms.h>
+#endif
+
+#if GTK_CHECK_VERSION(2,0,0)
+#include <gtk/gtkimmodule.h>
+#endif
+
+#define MAY_REFER_TO_GTK_IN_HEADERS
+
+#include "putty.h"
+#include "terminal.h"
+#include "gtkcompat.h"
+#include "gtkfont.h"
+#include "gtkmisc.h"
+
+#ifndef NOT_X_WINDOWS
+#include <gdk/gdkx.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xatom.h>
+#endif
+
+static char *progname, **gtkargvstart;
+static int ngtkargs;
+
+extern char **pty_argv;               /* declared in pty.c */
+extern int use_pty_argv;
+
+static const char *app_name = "pterm";
+
+char *x_get_default(const char *key)
+{
+#ifndef NOT_X_WINDOWS
+    return XGetDefault(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
+                       app_name, key);
+#else
+    return NULL;
+#endif
+}
+
+void fork_and_exec_self(int fd_to_close, ...)
+{
+    /*
+     * Re-execing ourself is not an exact science under Unix. I do
+     * the best I can by using /proc/self/exe if available and by
+     * assuming argv[0] can be found on $PATH if not.
+     * 
+     * Note that we also have to reconstruct the elements of the
+     * original argv which gtk swallowed, since the user wants the
+     * new session to appear on the same X display as the old one.
+     */
+    char **args;
+    va_list ap;
+    int i, n;
+    int pid;
+
+    /*
+     * Collect the arguments with which to re-exec ourself.
+     */
+    va_start(ap, fd_to_close);
+    n = 2;                            /* progname and terminating NULL */
+    n += ngtkargs;
+    while (va_arg(ap, char *) != NULL)
+       n++;
+    va_end(ap);
+
+    args = snewn(n, char *);
+    args[0] = progname;
+    args[n-1] = NULL;
+    for (i = 0; i < ngtkargs; i++)
+       args[i+1] = gtkargvstart[i];
+
+    i++;
+    va_start(ap, fd_to_close);
+    while ((args[i++] = va_arg(ap, char *)) != NULL);
+    va_end(ap);
+
+    assert(i == n);
+
+    /*
+     * Do the double fork.
+     */
+    pid = fork();
+    if (pid < 0) {
+       perror("fork");
+        sfree(args);
+       return;
+    }
+
+    if (pid == 0) {
+       int pid2 = fork();
+       if (pid2 < 0) {
+           perror("fork");
+           _exit(1);
+       } else if (pid2 > 0) {
+           /*
+            * First child has successfully forked second child. My
+            * Work Here Is Done. Note the use of _exit rather than
+            * exit: the latter appears to cause destroy messages
+            * to be sent to the X server. I suspect gtk uses
+            * atexit.
+            */
+           _exit(0);
+       }
+
+       /*
+        * If we reach here, we are the second child, so we now
+        * actually perform the exec.
+        */
+       if (fd_to_close >= 0)
+           close(fd_to_close);
+
+       execv("/proc/self/exe", args);
+       execvp(progname, args);
+       perror("exec");
+       _exit(127);
+
+    } else {
+       int status;
+        sfree(args);
+       waitpid(pid, &status, 0);
+    }
+
+}
+
+void launch_duplicate_session(Conf *conf)
+{
+    /*
+     * For this feature we must marshal conf and (possibly) pty_argv
+     * into a byte stream, create a pipe, and send this byte stream
+     * to the child through the pipe.
+     */
+    int i, ret, sersize, size;
+    char *data;
+    char option[80];
+    int pipefd[2];
+
+    if (pipe(pipefd) < 0) {
+       perror("pipe");
+       return;
+    }
+
+    size = sersize = conf_serialised_size(conf);
+    if (use_pty_argv && pty_argv) {
+       for (i = 0; pty_argv[i]; i++)
+           size += strlen(pty_argv[i]) + 1;
+    }
+
+    data = snewn(size, char);
+    conf_serialise(conf, data);
+    if (use_pty_argv && pty_argv) {
+       int p = sersize;
+       for (i = 0; pty_argv[i]; i++) {
+           strcpy(data + p, pty_argv[i]);
+           p += strlen(pty_argv[i]) + 1;
+       }
+       assert(p == size);
+    }
+
+    sprintf(option, "---[%d,%d]", pipefd[0], size);
+    noncloexec(pipefd[0]);
+    fork_and_exec_self(pipefd[1], option, NULL);
+    close(pipefd[0]);
+
+    i = ret = 0;
+    while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
+       i += ret;
+    if (ret < 0)
+       perror("write to pipe");
+    close(pipefd[1]);
+    sfree(data);
+}
+
+void launch_new_session(void)
+{
+    fork_and_exec_self(-1, NULL);
+}
+
+void launch_saved_session(const char *str)
+{
+    fork_and_exec_self(-1, "-load", str, NULL);
+}
+
+int read_dupsession_data(Conf *conf, char *arg)
+{
+    int fd, i, ret, size, size_used;
+    char *data;
+
+    if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
+       fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
+       exit(1);
+    }
+
+    data = snewn(size, char);
+    i = ret = 0;
+    while (i < size && (ret = read(fd, data + i, size - i)) > 0)
+       i += ret;
+    if (ret < 0) {
+       perror("read from pipe");
+       exit(1);
+    } else if (i < size) {
+       fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
+               appname);
+       exit(1);
+    }
+
+    size_used = conf_deserialise(conf, data, size);
+    if (use_pty_argv && size > size_used) {
+       int n = 0;
+       i = size_used;
+       while (i < size) {
+           while (i < size && data[i]) i++;
+           if (i >= size) {
+               fprintf(stderr, "%s: malformed Duplicate Session data\n",
+                       appname);
+               exit(1);
+           }
+           i++;
+           n++;
+       }
+       pty_argv = snewn(n+1, char *);
+       pty_argv[n] = NULL;
+       n = 0;
+       i = size_used;
+       while (i < size) {
+           char *p = data + i;
+           while (i < size && data[i]) i++;
+           assert(i < size);
+           i++;
+           pty_argv[n++] = dupstr(p);
+       }
+    }
+
+    sfree(data);
+
+    return 0;
+}
+
+static void help(FILE *fp) {
+    if(fprintf(fp,
+"pterm option summary:\n"
+"\n"
+"  --display DISPLAY         Specify X display to use (note '--')\n"
+"  -name PREFIX              Prefix when looking up resources (default: pterm)\n"
+"  -fn FONT                  Normal text font\n"
+"  -fb FONT                  Bold text font\n"
+"  -geometry GEOMETRY        Position and size of window (size in characters)\n"
+"  -sl LINES                 Number of lines of scrollback\n"
+"  -fg COLOUR, -bg COLOUR    Foreground/background colour\n"
+"  -bfg COLOUR, -bbg COLOUR  Foreground/background bold colour\n"
+"  -cfg COLOUR, -bfg COLOUR  Foreground/background cursor colour\n"
+"  -T TITLE                  Window title\n"
+"  -ut, +ut                  Do(default) or do not update utmp\n"
+"  -ls, +ls                  Do(default) or do not make shell a login shell\n"
+"  -sb, +sb                  Do(default) or do not display a scrollbar\n"
+"  -log PATH, -sessionlog PATH  Log all output to a file\n"
+"  -nethack                  Map numeric keypad to hjklyubn direction keys\n"
+"  -xrm RESOURCE-STRING      Set an X resource\n"
+"  -e COMMAND [ARGS...]      Execute command (consumes all remaining args)\n"
+        ) < 0 || fflush(fp) < 0) {
+       perror("output error");
+       exit(1);
+    }
+}
+
+static void version(FILE *fp) {
+    if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
+       perror("output error");
+       exit(1);
+    }
+}
+
+static struct gui_data *the_inst;
+
+static const char *geometry_string;
+
+int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
+               Conf *conf)
+{
+    int err = 0;
+    char *val;
+
+    /*
+     * Macros to make argument handling easier. Note that because
+     * they need to call `continue', they cannot be contained in
+     * the usual do {...} while (0) wrapper to make them
+     * syntactically single statements; hence it is not legal to
+     * use one of these macros as an unbraced statement between
+     * `if' and `else'.
+     */
+#define EXPECTS_ARG { \
+    if (--argc <= 0) { \
+       err = 1; \
+       fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
+        continue; \
+    } else \
+       val = *++argv; \
+}
+#define SECOND_PASS_ONLY { if (!do_everything) continue; }
+
+    while (--argc > 0) {
+       const char *p = *++argv;
+        int ret;
+
+       /*
+        * Shameless cheating. Debian requires all X terminal
+        * emulators to support `-T title'; but
+        * cmdline_process_param will eat -T (it means no-pty) and
+        * complain that pterm doesn't support it. So, in pterm
+        * only, we convert -T into -title.
+        */
+       if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
+           !strcmp(p, "-T"))
+           p = "-title";
+
+        ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
+                                    do_everything ? 1 : -1, conf);
+
+       if (ret == -2) {
+           cmdline_error("option \"%s\" requires an argument", p);
+       } else if (ret == 2) {
+           --argc, ++argv;            /* skip next argument */
+            continue;
+       } else if (ret == 1) {
+            continue;
+        }
+
+       if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
+           FontSpec *fs;
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_font, fs);
+            fontspec_free(fs);
+
+       } else if (!strcmp(p, "-fb")) {
+           FontSpec *fs;
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_boldfont, fs);
+            fontspec_free(fs);
+
+       } else if (!strcmp(p, "-fw")) {
+           FontSpec *fs;
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_widefont, fs);
+            fontspec_free(fs);
+
+       } else if (!strcmp(p, "-fwb")) {
+           FontSpec *fs;
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+            fs = fontspec_new(val);
+           conf_set_fontspec(conf, CONF_wideboldfont, fs);
+            fontspec_free(fs);
+
+       } else if (!strcmp(p, "-cs")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+           conf_set_str(conf, CONF_line_codepage, val);
+
+       } else if (!strcmp(p, "-geometry")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+            geometry_string = val;
+       } else if (!strcmp(p, "-sl")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_savelines, atoi(val));
+
+       } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
+                  !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
+                  !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+
+            {
+#if GTK_CHECK_VERSION(3,0,0)
+                GdkRGBA rgba;
+                int success = gdk_rgba_parse(&rgba, val);
+#else
+                GdkColor col;
+                int success = gdk_color_parse(val, &col);
+#endif
+
+                if (!success) {
+                    err = 1;
+                    fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
+                            appname, val);
+                } else {
+#if GTK_CHECK_VERSION(3,0,0)
+                    int r = rgba.red * 255;
+                    int g = rgba.green * 255;
+                    int b = rgba.blue * 255;
+#else
+                    int r = col.red / 256;
+                    int g = col.green / 256;
+                    int b = col.blue / 256;
+#endif
+
+                    int index;
+                    index = (!strcmp(p, "-fg") ? 0 :
+                             !strcmp(p, "-bg") ? 2 :
+                             !strcmp(p, "-bfg") ? 1 :
+                             !strcmp(p, "-bbg") ? 3 :
+                             !strcmp(p, "-cfg") ? 4 :
+                             !strcmp(p, "-cbg") ? 5 : -1);
+                    assert(index != -1);
+
+                    conf_set_int_int(conf, CONF_colours, index*3+0, r);
+                    conf_set_int_int(conf, CONF_colours, index*3+1, g);
+                    conf_set_int_int(conf, CONF_colours, index*3+2, b);
+                }
+            }
+
+       } else if (use_pty_argv && !strcmp(p, "-e")) {
+           /* This option swallows all further arguments. */
+           if (!do_everything)
+               break;
+
+           if (--argc > 0) {
+               int i;
+               pty_argv = snewn(argc+1, char *);
+               ++argv;
+               for (i = 0; i < argc; i++)
+                   pty_argv[i] = argv[i];
+               pty_argv[argc] = NULL;
+               break;                 /* finished command-line processing */
+           } else
+               err = 1, fprintf(stderr, "%s: -e expects an argument\n",
+                                 appname);
+
+       } else if (!strcmp(p, "-title")) {
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+           conf_set_str(conf, CONF_wintitle, val);
+
+       } else if (!strcmp(p, "-log")) {
+           Filename *fn;
+           EXPECTS_ARG;
+           SECOND_PASS_ONLY;
+            fn = filename_from_str(val);
+           conf_set_filename(conf, CONF_logfilename, fn);
+           conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
+            filename_free(fn);
+
+       } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_stamp_utmp, 0);
+
+       } else if (!strcmp(p, "-ut")) {
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_stamp_utmp, 1);
+
+       } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_login_shell, 0);
+
+       } else if (!strcmp(p, "-ls")) {
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_login_shell, 1);
+
+       } else if (!strcmp(p, "-nethack")) {
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_nethack_keypad, 1);
+
+       } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_scrollbar, 0);
+
+       } else if (!strcmp(p, "-sb")) {
+           SECOND_PASS_ONLY;
+           conf_set_int(conf, CONF_scrollbar, 1);
+
+       } else if (!strcmp(p, "-name")) {
+           EXPECTS_ARG;
+           app_name = val;
+
+       } else if (!strcmp(p, "-xrm")) {
+           EXPECTS_ARG;
+           provide_xrm_string(val);
+
+       } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
+           help(stdout);
+           exit(0);
+
+       } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
+           version(stdout);
+           exit(0);
+
+        } else if (!strcmp(p, "-pgpfp")) {
+            pgp_fingerprints();
+            exit(1);
+
+       } else if(p[0] != '-' && (!do_everything ||
+                                  process_nonoption_arg(p, conf,
+                                                       allow_launch))) {
+            /* do nothing */
+
+       } else {
+           err = 1;
+           fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
+       }
+    }
+
+    return err;
+}
+
+extern int cfgbox(Conf *conf);
+
+int pt_main(int argc, char **argv)
+{
+    Conf *conf;
+    int need_config_box;
+
+    setlocale(LC_CTYPE, "");
+
+    progname = argv[0];
+
+    /*
+     * Copy the original argv before letting gtk_init fiddle with
+     * it. It will be required later.
+     */
+    {
+       int i, oldargc;
+        gtkargvstart = snewn(argc-1, char *);
+       for (i = 1; i < argc; i++)
+           gtkargvstart[i-1] = dupstr(argv[i]);
+       oldargc = argc;
+       gtk_init(&argc, &argv);
+       ngtkargs = oldargc - argc;
+    }
+
+    conf = conf_new();
+
+    gtkcomm_setup();
+
+    /*
+     * Block SIGPIPE: if we attempt Duplicate Session or similar and
+     * it falls over in some way, we certainly don't want SIGPIPE
+     * terminating the main pterm/PuTTY. However, we'll have to
+     * unblock it again when pterm forks.
+     */
+    block_signal(SIGPIPE, 1);
+
+    if (argc > 1 && !strncmp(argv[1], "---", 3)) {
+       read_dupsession_data(conf, argv[1]);
+       /* Splatter this argument so it doesn't clutter a ps listing */
+       smemclr(argv[1], strlen(argv[1]));
+
+        assert(conf_launchable(conf));
+        need_config_box = FALSE;
+    } else {
+       /* By default, we bring up the config dialog, rather than launching
+        * a session. This gets set to TRUE if something happens to change
+        * that (e.g., a hostname is specified on the command-line). */
+       int allow_launch = FALSE;
+       if (do_cmdline(argc, argv, 0, &allow_launch, conf))
+           exit(1);                   /* pre-defaults pass to get -class */
+       do_defaults(NULL, conf);
+       if (do_cmdline(argc, argv, 1, &allow_launch, conf))
+           exit(1);                   /* post-defaults, do everything */
+
+       cmdline_run_saved(conf);
+
+       if (loaded_session)
+           allow_launch = TRUE;
+
+        need_config_box = (!allow_launch || !conf_launchable(conf));
+    }
+
+    /*
+     * Put up the config box.
+     */
+    if (need_config_box && !cfgbox(conf))
+        exit(0);                      /* config box hit Cancel */
+
+    /*
+     * Create the main session window. We don't really need to keep
+     * the return value - the fact that it'll be linked from a zillion
+     * GTK and glib bits and bobs known to the main loop will be
+     * sufficient to make everything actually happen - but we stash it
+     * in a global variable anyway, so that it'll be easy to find in a
+     * debugger.
+     */
+    the_inst = new_session_window(conf, geometry_string);
+
+    gtk_main();
+
+    return 0;
+}
index 2879b57e9877814b27202f4f8716aa613c64d320..3ed58b08287a83e590f4aa08a1929b715a124fad 100644 (file)
 
 #include "x11misc.h"
 
-#define CAT2(x,y) x ## y
-#define CAT(x,y) CAT2(x,y)
-#define ASSERT(x) enum {CAT(assertion_,__LINE__) = 1 / (x)}
-
-#if GTK_CHECK_VERSION(2,0,0)
-ASSERT(sizeof(long) <= sizeof(gsize));
-#define LONG_TO_GPOINTER(l) GSIZE_TO_POINTER(l)
-#define GPOINTER_TO_LONG(p) GPOINTER_TO_SIZE(p)
-#else /* Gtk 1.2 */
-ASSERT(sizeof(long) <= sizeof(gpointer));
-#define LONG_TO_GPOINTER(l) ((gpointer)(long)(l))
-#define GPOINTER_TO_LONG(p) ((long)(p))
-#endif
-
 /* Colours come in two flavours: configurable, and xterm-extended. */
 #define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */
 #define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS)
 
 GdkAtom compound_text_atom, utf8_string_atom;
 
-extern char **pty_argv;               /* declared in pty.c */
-extern int use_pty_argv;
-
-/*
- * Timers are global across all sessions (even if we were handling
- * multiple sessions, which we aren't), so the current timer ID is
- * a global variable.
- */
-static guint timer_id = 0;
-
 struct clipboard_data_instance;
 
 struct gui_data {
@@ -142,8 +118,6 @@ struct gui_data {
     int ignore_sbar;
     int mouseptr_visible;
     int busy_status;
-    guint toplevel_callback_idle_id;
-    int idle_fn_scheduled, quit_fn_scheduled;
     int alt_keycode;
     int alt_digits;
     char *wintitle;
@@ -158,8 +132,6 @@ struct gui_data {
     struct unicode_data ucsdata;
     Conf *conf;
     void *eventlogstuff;
-    char *progname, **gtkargvstart;
-    int ngtkargs;
     guint32 input_event_time; /* Timestamp of the most recent input event. */
     int reconfiguring;
 #if GTK_CHECK_VERSION(3,4,0)
@@ -196,21 +168,9 @@ struct draw_ctx {
 
 static int send_raw_mouse;
 
-static const char *app_name = "pterm";
-
 static void start_backend(struct gui_data *inst);
 static void exit_callback(void *vinst);
 
-char *x_get_default(const char *key)
-{
-#ifndef NOT_X_WINDOWS
-    return XGetDefault(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()),
-                       app_name, key);
-#else
-    return NULL;
-#endif
-}
-
 void connection_fatal(void *frontend, const char *p, ...)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
@@ -2014,48 +1974,6 @@ static void exit_callback(void *vinst)
     }
 }
 
-/*
- * Replacement code for the gtk_quit_add() function, which GTK2 - in
- * their unbounded wisdom - deprecated without providing any usable
- * replacement, and which we were using to ensure that our idle
- * function for toplevel callbacks was only run from the outermost
- * gtk_main().
- *
- * We maintain a global variable with a list of 'struct gui_data'
- * instances on which we should call inst_post_main() when a
- * subsidiary gtk_main() terminates; then we must make sure that all
- * our subsidiary calls to gtk_main() are followed by a call to
- * post_main().
- *
- * This is kind of overkill in the sense that at the time of writing
- * we don't actually ever run more than one 'struct gui_data' instance
- * in the same process, but we're _so nearly_ prepared to do that that
- * I want to remain futureproof against the possibility of doing so in
- * future.
- */
-struct post_main_context {
-    struct post_main_context *next;
-    struct gui_data *inst;
-};
-struct post_main_context *post_main_list_head = NULL;
-static void request_post_main(struct gui_data *inst)
-{
-    struct post_main_context *node = snew(struct post_main_context);
-    node->next = post_main_list_head;
-    node->inst = inst;
-    post_main_list_head = node;
-}
-static void inst_post_main(struct gui_data *inst);
-void post_main(void)
-{
-    while (post_main_list_head) {
-        struct post_main_context *node = post_main_list_head;
-        post_main_list_head = node->next;
-        inst_post_main(node->inst);
-        sfree(node);
-    }
-}
-
 void notify_remote_exit(void *frontend)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
@@ -2063,157 +1981,6 @@ void notify_remote_exit(void *frontend)
     queue_toplevel_callback(exit_callback, inst);
 }
 
-static void notify_toplevel_callback(void *frontend);
-
-static void inst_post_main(struct gui_data *inst)
-{
-    if (gtk_main_level() == 1) {
-        notify_toplevel_callback(inst);
-        inst->quit_fn_scheduled = FALSE;
-    } else {
-        /* Apparently we're _still_ more than one level deep in
-         * gtk_main() instances, so we'll need another callback for
-         * when we get out of the next one. */
-        request_post_main(inst);
-    }
-}
-
-static gint idle_toplevel_callback_func(gpointer data)
-{
-    struct gui_data *inst = (struct gui_data *)data;
-
-    if (gtk_main_level() > 1) {
-        /*
-         * We don't run the callbacks if we're in the middle of a
-         * subsidiary gtk_main. Instead, ask for a callback when we
-         * get back out of the subsidiary main loop (if we haven't
-         * already arranged one), so we can reschedule ourself then.
-         */
-        if (!inst->quit_fn_scheduled) {
-            request_post_main(inst);
-            inst->quit_fn_scheduled = TRUE;
-        }
-        /*
-         * And unschedule this idle function, since we've now done
-         * everything we can until the innermost gtk_main has quit and
-         * can reschedule us with a chance of actually taking action.
-         */
-        if (inst->idle_fn_scheduled) { /* double-check, just in case */
-            g_source_remove(inst->toplevel_callback_idle_id);
-            inst->idle_fn_scheduled = FALSE;
-        }
-    } else {
-        run_toplevel_callbacks();
-    }
-
-    /*
-     * If we've emptied our toplevel callback queue, unschedule
-     * ourself. Otherwise, leave ourselves pending so we'll be called
-     * again to deal with more callbacks after another round of the
-     * event loop.
-     */
-    if (!toplevel_callback_pending() && inst->idle_fn_scheduled) {
-        g_source_remove(inst->toplevel_callback_idle_id);
-        inst->idle_fn_scheduled = FALSE;
-    }
-
-    return TRUE;
-}
-
-static void notify_toplevel_callback(void *frontend)
-{
-    struct gui_data *inst = (struct gui_data *)frontend;
-
-    if (!inst->idle_fn_scheduled) {
-        inst->toplevel_callback_idle_id =
-            g_idle_add(idle_toplevel_callback_func, inst);
-        inst->idle_fn_scheduled = TRUE;
-    }
-}
-
-static gint timer_trigger(gpointer data)
-{
-    unsigned long now = GPOINTER_TO_LONG(data);
-    unsigned long next, then;
-    long ticks;
-
-    /*
-     * Destroy the timer we got here on.
-     */
-    if (timer_id) {
-       g_source_remove(timer_id);
-        timer_id = 0;
-    }
-
-    /*
-     * run_timers() may cause a call to timer_change_notify, in which
-     * case a new timer will already have been set up and left in
-     * timer_id. If it hasn't, and run_timers reports that some timing
-     * still needs to be done, we do it ourselves.
-     */
-    if (run_timers(now, &next) && !timer_id) {
-       then = now;
-       now = GETTICKCOUNT();
-       if (now - then > next - then)
-           ticks = 0;
-       else
-           ticks = next - now;
-       timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
-    }
-
-    /*
-     * Returning FALSE means 'don't call this timer again', which
-     * _should_ be redundant given that we removed it above, but just
-     * in case, return FALSE anyway.
-     */
-    return FALSE;
-}
-
-void timer_change_notify(unsigned long next)
-{
-    long ticks;
-
-    if (timer_id)
-       g_source_remove(timer_id);
-
-    ticks = next - GETTICKCOUNT();
-    if (ticks <= 0)
-       ticks = 1;                     /* just in case */
-
-    timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next));
-}
-
-#if GTK_CHECK_VERSION(2,0,0)
-gboolean fd_input_func(GIOChannel *source, GIOCondition condition,
-                       gpointer data)
-{
-    int sourcefd = g_io_channel_unix_get_fd(source);
-    /*
-     * We must process exceptional notifications before ordinary
-     * readability ones, or we may go straight past the urgent
-     * marker.
-     */
-    if (condition & G_IO_PRI)
-        select_result(sourcefd, 4);
-    if (condition & G_IO_IN)
-        select_result(sourcefd, 1);
-    if (condition & G_IO_OUT)
-        select_result(sourcefd, 2);
-
-    return TRUE;
-}
-#else
-void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition)
-{
-    if (condition & GDK_INPUT_EXCEPTION)
-        select_result(sourcefd, 4);
-    if (condition & GDK_INPUT_READ)
-        select_result(sourcefd, 1);
-    if (condition & GDK_INPUT_WRITE)
-        select_result(sourcefd, 2);
-}
-#endif
-
 void destroy(GtkWidget *widget, gpointer data)
 {
     gtk_main_quit();
@@ -3742,341 +3509,6 @@ long get_windowid(void *frontend)
 }
 #endif
 
-static void help(FILE *fp) {
-    if(fprintf(fp,
-"pterm option summary:\n"
-"\n"
-"  --display DISPLAY         Specify X display to use (note '--')\n"
-"  -name PREFIX              Prefix when looking up resources (default: pterm)\n"
-"  -fn FONT                  Normal text font\n"
-"  -fb FONT                  Bold text font\n"
-"  -geometry GEOMETRY        Position and size of window (size in characters)\n"
-"  -sl LINES                 Number of lines of scrollback\n"
-"  -fg COLOUR, -bg COLOUR    Foreground/background colour\n"
-"  -bfg COLOUR, -bbg COLOUR  Foreground/background bold colour\n"
-"  -cfg COLOUR, -bfg COLOUR  Foreground/background cursor colour\n"
-"  -T TITLE                  Window title\n"
-"  -ut, +ut                  Do(default) or do not update utmp\n"
-"  -ls, +ls                  Do(default) or do not make shell a login shell\n"
-"  -sb, +sb                  Do(default) or do not display a scrollbar\n"
-"  -log PATH, -sessionlog PATH  Log all output to a file\n"
-"  -nethack                  Map numeric keypad to hjklyubn direction keys\n"
-"  -xrm RESOURCE-STRING      Set an X resource\n"
-"  -e COMMAND [ARGS...]      Execute command (consumes all remaining args)\n"
-        ) < 0 || fflush(fp) < 0) {
-       perror("output error");
-       exit(1);
-    }
-}
-
-static void version(FILE *fp) {
-    if(fprintf(fp, "%s: %s\n", appname, ver) < 0 || fflush(fp) < 0) {
-       perror("output error");
-       exit(1);
-    }
-}
-
-int do_cmdline(int argc, char **argv, int do_everything, int *allow_launch,
-               struct gui_data *inst, Conf *conf)
-{
-    int err = 0;
-    char *val;
-
-    /*
-     * Macros to make argument handling easier. Note that because
-     * they need to call `continue', they cannot be contained in
-     * the usual do {...} while (0) wrapper to make them
-     * syntactically single statements; hence it is not legal to
-     * use one of these macros as an unbraced statement between
-     * `if' and `else'.
-     */
-#define EXPECTS_ARG { \
-    if (--argc <= 0) { \
-       err = 1; \
-       fprintf(stderr, "%s: %s expects an argument\n", appname, p); \
-        continue; \
-    } else \
-       val = *++argv; \
-}
-#define SECOND_PASS_ONLY { if (!do_everything) continue; }
-
-    while (--argc > 0) {
-       const char *p = *++argv;
-        int ret;
-
-       /*
-        * Shameless cheating. Debian requires all X terminal
-        * emulators to support `-T title'; but
-        * cmdline_process_param will eat -T (it means no-pty) and
-        * complain that pterm doesn't support it. So, in pterm
-        * only, we convert -T into -title.
-        */
-       if ((cmdline_tooltype & TOOLTYPE_NONNETWORK) &&
-           !strcmp(p, "-T"))
-           p = "-title";
-
-        ret = cmdline_process_param(p, (argc > 1 ? argv[1] : NULL),
-                                    do_everything ? 1 : -1, conf);
-
-       if (ret == -2) {
-           cmdline_error("option \"%s\" requires an argument", p);
-       } else if (ret == 2) {
-           --argc, ++argv;            /* skip next argument */
-            continue;
-       } else if (ret == 1) {
-            continue;
-        }
-
-       if (!strcmp(p, "-fn") || !strcmp(p, "-font")) {
-           FontSpec *fs;
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-            fs = fontspec_new(val);
-           conf_set_fontspec(conf, CONF_font, fs);
-            fontspec_free(fs);
-
-       } else if (!strcmp(p, "-fb")) {
-           FontSpec *fs;
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-            fs = fontspec_new(val);
-           conf_set_fontspec(conf, CONF_boldfont, fs);
-            fontspec_free(fs);
-
-       } else if (!strcmp(p, "-fw")) {
-           FontSpec *fs;
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-            fs = fontspec_new(val);
-           conf_set_fontspec(conf, CONF_widefont, fs);
-            fontspec_free(fs);
-
-       } else if (!strcmp(p, "-fwb")) {
-           FontSpec *fs;
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-            fs = fontspec_new(val);
-           conf_set_fontspec(conf, CONF_wideboldfont, fs);
-            fontspec_free(fs);
-
-       } else if (!strcmp(p, "-cs")) {
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-           conf_set_str(conf, CONF_line_codepage, val);
-
-       } else if (!strcmp(p, "-geometry")) {
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-
-#if GTK_CHECK_VERSION(2,0,0)
-            inst->geometry = val;
-#else
-            /* On GTK 1, we have to do this using raw Xlib */
-            {
-                int flags, x, y;
-                unsigned int w, h;
-                flags = XParseGeometry(val, &x, &y, &w, &h);
-                if (flags & WidthValue)
-                    conf_set_int(conf, CONF_width, w);
-                if (flags & HeightValue)
-                    conf_set_int(conf, CONF_height, h);
-
-                if (flags & (XValue | YValue)) {
-                    inst->xpos = x;
-                    inst->ypos = y;
-                    inst->gotpos = TRUE;
-                    inst->gravity = ((flags & XNegative ? 1 : 0) |
-                                     (flags & YNegative ? 2 : 0));
-                }
-            }
-#endif
-
-       } else if (!strcmp(p, "-sl")) {
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_savelines, atoi(val));
-
-       } else if (!strcmp(p, "-fg") || !strcmp(p, "-bg") ||
-                  !strcmp(p, "-bfg") || !strcmp(p, "-bbg") ||
-                  !strcmp(p, "-cfg") || !strcmp(p, "-cbg")) {
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-
-            {
-#if GTK_CHECK_VERSION(3,0,0)
-                GdkRGBA rgba;
-                int success = gdk_rgba_parse(&rgba, val);
-#else
-                GdkColor col;
-                int success = gdk_color_parse(val, &col);
-#endif
-
-                if (!success) {
-                    err = 1;
-                    fprintf(stderr, "%s: unable to parse colour \"%s\"\n",
-                            appname, val);
-                } else {
-#if GTK_CHECK_VERSION(3,0,0)
-                    int r = rgba.red * 255;
-                    int g = rgba.green * 255;
-                    int b = rgba.blue * 255;
-#else
-                    int r = col.red / 256;
-                    int g = col.green / 256;
-                    int b = col.blue / 256;
-#endif
-
-                    int index;
-                    index = (!strcmp(p, "-fg") ? 0 :
-                             !strcmp(p, "-bg") ? 2 :
-                             !strcmp(p, "-bfg") ? 1 :
-                             !strcmp(p, "-bbg") ? 3 :
-                             !strcmp(p, "-cfg") ? 4 :
-                             !strcmp(p, "-cbg") ? 5 : -1);
-                    assert(index != -1);
-
-                    conf_set_int_int(conf, CONF_colours, index*3+0, r);
-                    conf_set_int_int(conf, CONF_colours, index*3+1, g);
-                    conf_set_int_int(conf, CONF_colours, index*3+2, b);
-                }
-            }
-
-       } else if (use_pty_argv && !strcmp(p, "-e")) {
-           /* This option swallows all further arguments. */
-           if (!do_everything)
-               break;
-
-           if (--argc > 0) {
-               int i;
-               pty_argv = snewn(argc+1, char *);
-               ++argv;
-               for (i = 0; i < argc; i++)
-                   pty_argv[i] = argv[i];
-               pty_argv[argc] = NULL;
-               break;                 /* finished command-line processing */
-           } else
-               err = 1, fprintf(stderr, "%s: -e expects an argument\n",
-                                 appname);
-
-       } else if (!strcmp(p, "-title")) {
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-           conf_set_str(conf, CONF_wintitle, val);
-
-       } else if (!strcmp(p, "-log")) {
-           Filename *fn;
-           EXPECTS_ARG;
-           SECOND_PASS_ONLY;
-            fn = filename_from_str(val);
-           conf_set_filename(conf, CONF_logfilename, fn);
-           conf_set_int(conf, CONF_logtype, LGTYP_DEBUG);
-            filename_free(fn);
-
-       } else if (!strcmp(p, "-ut-") || !strcmp(p, "+ut")) {
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_stamp_utmp, 0);
-
-       } else if (!strcmp(p, "-ut")) {
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_stamp_utmp, 1);
-
-       } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) {
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_login_shell, 0);
-
-       } else if (!strcmp(p, "-ls")) {
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_login_shell, 1);
-
-       } else if (!strcmp(p, "-nethack")) {
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_nethack_keypad, 1);
-
-       } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) {
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_scrollbar, 0);
-
-       } else if (!strcmp(p, "-sb")) {
-           SECOND_PASS_ONLY;
-           conf_set_int(conf, CONF_scrollbar, 1);
-
-       } else if (!strcmp(p, "-name")) {
-           EXPECTS_ARG;
-           app_name = val;
-
-       } else if (!strcmp(p, "-xrm")) {
-           EXPECTS_ARG;
-           provide_xrm_string(val);
-
-       } else if(!strcmp(p, "-help") || !strcmp(p, "--help")) {
-           help(stdout);
-           exit(0);
-
-       } else if(!strcmp(p, "-version") || !strcmp(p, "--version")) {
-           version(stdout);
-           exit(0);
-
-        } else if (!strcmp(p, "-pgpfp")) {
-            pgp_fingerprints();
-            exit(1);
-
-       } else if(p[0] != '-' && (!do_everything ||
-                                  process_nonoption_arg(p, conf,
-                                                       allow_launch))) {
-            /* do nothing */
-
-       } else {
-           err = 1;
-           fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p);
-       }
-    }
-
-    return err;
-}
-
-struct uxsel_id {
-#if GTK_CHECK_VERSION(2,0,0)
-    GIOChannel *chan;
-    guint watch_id;
-#else
-    int id;
-#endif
-};
-
-uxsel_id *uxsel_input_add(int fd, int rwx) {
-    uxsel_id *id = snew(uxsel_id);
-
-#if GTK_CHECK_VERSION(2,0,0)
-    int flags = 0;
-    if (rwx & 1) flags |= G_IO_IN;
-    if (rwx & 2) flags |= G_IO_OUT;
-    if (rwx & 4) flags |= G_IO_PRI;
-    id->chan = g_io_channel_unix_new(fd);
-    g_io_channel_set_encoding(id->chan, NULL, NULL);
-    id->watch_id = g_io_add_watch_full(id->chan, GDK_PRIORITY_REDRAW+1, flags,
-                                       fd_input_func, NULL, NULL);
-#else
-    int flags = 0;
-    if (rwx & 1) flags |= GDK_INPUT_READ;
-    if (rwx & 2) flags |= GDK_INPUT_WRITE;
-    if (rwx & 4) flags |= GDK_INPUT_EXCEPTION;
-    assert(flags);
-    id->id = gdk_input_add(fd, flags, fd_input_func, NULL);
-#endif
-
-    return id;
-}
-
-void uxsel_input_remove(uxsel_id *id) {
-#if GTK_CHECK_VERSION(2,0,0)
-    g_source_remove(id->watch_id);
-    g_io_channel_unref(id->chan);
-#else
-    gdk_input_remove(id->id);
-#endif
-    sfree(id);
-}
-
 int frontend_is_utf8(void *frontend)
 {
     struct gui_data *inst = (struct gui_data *)frontend;
@@ -4408,200 +3840,16 @@ void change_settings_menuitem(GtkMenuItem *item, gpointer data)
     inst->reconfiguring = FALSE;
 }
 
-void fork_and_exec_self(struct gui_data *inst, int fd_to_close, ...)
-{
-    /*
-     * Re-execing ourself is not an exact science under Unix. I do
-     * the best I can by using /proc/self/exe if available and by
-     * assuming argv[0] can be found on $PATH if not.
-     * 
-     * Note that we also have to reconstruct the elements of the
-     * original argv which gtk swallowed, since the user wants the
-     * new session to appear on the same X display as the old one.
-     */
-    char **args;
-    va_list ap;
-    int i, n;
-    int pid;
-
-    /*
-     * Collect the arguments with which to re-exec ourself.
-     */
-    va_start(ap, fd_to_close);
-    n = 2;                            /* progname and terminating NULL */
-    n += inst->ngtkargs;
-    while (va_arg(ap, char *) != NULL)
-       n++;
-    va_end(ap);
-
-    args = snewn(n, char *);
-    args[0] = inst->progname;
-    args[n-1] = NULL;
-    for (i = 0; i < inst->ngtkargs; i++)
-       args[i+1] = inst->gtkargvstart[i];
-
-    i++;
-    va_start(ap, fd_to_close);
-    while ((args[i++] = va_arg(ap, char *)) != NULL);
-    va_end(ap);
-
-    assert(i == n);
-
-    /*
-     * Do the double fork.
-     */
-    pid = fork();
-    if (pid < 0) {
-       perror("fork");
-        sfree(args);
-       return;
-    }
-
-    if (pid == 0) {
-       int pid2 = fork();
-       if (pid2 < 0) {
-           perror("fork");
-           _exit(1);
-       } else if (pid2 > 0) {
-           /*
-            * First child has successfully forked second child. My
-            * Work Here Is Done. Note the use of _exit rather than
-            * exit: the latter appears to cause destroy messages
-            * to be sent to the X server. I suspect gtk uses
-            * atexit.
-            */
-           _exit(0);
-       }
-
-       /*
-        * If we reach here, we are the second child, so we now
-        * actually perform the exec.
-        */
-       if (fd_to_close >= 0)
-           close(fd_to_close);
-
-       execv("/proc/self/exe", args);
-       execvp(inst->progname, args);
-       perror("exec");
-       _exit(127);
-
-    } else {
-       int status;
-        sfree(args);
-       waitpid(pid, &status, 0);
-    }
-
-}
-
 void dup_session_menuitem(GtkMenuItem *item, gpointer gdata)
 {
     struct gui_data *inst = (struct gui_data *)gdata;
-    /*
-     * For this feature we must marshal conf and (possibly) pty_argv
-     * into a byte stream, create a pipe, and send this byte stream
-     * to the child through the pipe.
-     */
-    int i, ret, sersize, size;
-    char *data;
-    char option[80];
-    int pipefd[2];
 
-    if (pipe(pipefd) < 0) {
-       perror("pipe");
-       return;
-    }
-
-    size = sersize = conf_serialised_size(inst->conf);
-    if (use_pty_argv && pty_argv) {
-       for (i = 0; pty_argv[i]; i++)
-           size += strlen(pty_argv[i]) + 1;
-    }
-
-    data = snewn(size, char);
-    conf_serialise(inst->conf, data);
-    if (use_pty_argv && pty_argv) {
-       int p = sersize;
-       for (i = 0; pty_argv[i]; i++) {
-           strcpy(data + p, pty_argv[i]);
-           p += strlen(pty_argv[i]) + 1;
-       }
-       assert(p == size);
-    }
-
-    sprintf(option, "---[%d,%d]", pipefd[0], size);
-    noncloexec(pipefd[0]);
-    fork_and_exec_self(inst, pipefd[1], option, NULL);
-    close(pipefd[0]);
-
-    i = ret = 0;
-    while (i < size && (ret = write(pipefd[1], data + i, size - i)) > 0)
-       i += ret;
-    if (ret < 0)
-       perror("write to pipe");
-    close(pipefd[1]);
-    sfree(data);
-}
-
-int read_dupsession_data(struct gui_data *inst, Conf *conf, char *arg)
-{
-    int fd, i, ret, size, size_used;
-    char *data;
-
-    if (sscanf(arg, "---[%d,%d]", &fd, &size) != 2) {
-       fprintf(stderr, "%s: malformed magic argument `%s'\n", appname, arg);
-       exit(1);
-    }
-
-    data = snewn(size, char);
-    i = ret = 0;
-    while (i < size && (ret = read(fd, data + i, size - i)) > 0)
-       i += ret;
-    if (ret < 0) {
-       perror("read from pipe");
-       exit(1);
-    } else if (i < size) {
-       fprintf(stderr, "%s: unexpected EOF in Duplicate Session data\n",
-               appname);
-       exit(1);
-    }
-
-    size_used = conf_deserialise(conf, data, size);
-    if (use_pty_argv && size > size_used) {
-       int n = 0;
-       i = size_used;
-       while (i < size) {
-           while (i < size && data[i]) i++;
-           if (i >= size) {
-               fprintf(stderr, "%s: malformed Duplicate Session data\n",
-                       appname);
-               exit(1);
-           }
-           i++;
-           n++;
-       }
-       pty_argv = snewn(n+1, char *);
-       pty_argv[n] = NULL;
-       n = 0;
-       i = size_used;
-       while (i < size) {
-           char *p = data + i;
-           while (i < size && data[i]) i++;
-           assert(i < size);
-           i++;
-           pty_argv[n++] = dupstr(p);
-       }
-    }
-
-    sfree(data);
-
-    return 0;
+    launch_duplicate_session(inst->conf);
 }
 
 void new_session_menuitem(GtkMenuItem *item, gpointer data)
 {
-    struct gui_data *inst = (struct gui_data *)data;
-
-    fork_and_exec_self(inst, -1, NULL);
+    launch_new_session();
 }
 
 void restart_session_menuitem(GtkMenuItem *item, gpointer data)
@@ -4618,10 +3866,9 @@ void restart_session_menuitem(GtkMenuItem *item, gpointer data)
 
 void saved_session_menuitem(GtkMenuItem *item, gpointer data)
 {
-    struct gui_data *inst = (struct gui_data *)data;
     char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data");
 
-    fork_and_exec_self(inst, -1, "-load", str, NULL);
+    launch_saved_session(str);
 }
 
 void saved_session_freedata(GtkMenuItem *item, gpointer data)
@@ -4814,13 +4061,10 @@ static void start_backend(struct gui_data *inst)
     gtk_widget_set_sensitive(inst->restartitem, FALSE);
 }
 
-int pt_main(int argc, char **argv)
+struct gui_data *new_session_window(Conf *conf, const char *geometry_string)
 {
-    extern int cfgbox(Conf *conf);
     struct gui_data *inst;
 
-    setlocale(LC_CTYPE, "");
-
     /*
      * Create an instance structure and initialise to zeroes
      */
@@ -4828,57 +4072,34 @@ int pt_main(int argc, char **argv)
     memset(inst, 0, sizeof(*inst));
     inst->alt_keycode = -1;            /* this one needs _not_ to be zero */
     inst->busy_status = BUSY_NOT;
-    inst->conf = conf_new();
+    inst->conf = conf;
     inst->wintitle = inst->icontitle = NULL;
-    inst->quit_fn_scheduled = FALSE;
-    inst->idle_fn_scheduled = FALSE;
     inst->drawtype = DRAWTYPE_DEFAULT;
 #if GTK_CHECK_VERSION(3,4,0)
     inst->cumulative_scroll = 0.0;
 #endif
 
-    /* defer any child exit handling until we're ready to deal with
-     * it */
-    block_signal(SIGCHLD, 1);
-
-    inst->progname = argv[0];
-    /*
-     * Copy the original argv before letting gtk_init fiddle with
-     * it. It will be required later.
-     */
-    {
-       int i, oldargc;
-       inst->gtkargvstart = snewn(argc-1, char *);
-       for (i = 1; i < argc; i++)
-           inst->gtkargvstart[i-1] = dupstr(argv[i]);
-       oldargc = argc;
-       gtk_init(&argc, &argv);
-       inst->ngtkargs = oldargc - argc;
-    }
-
-    if (argc > 1 && !strncmp(argv[1], "---", 3)) {
-       read_dupsession_data(inst, inst->conf, argv[1]);
-       /* Splatter this argument so it doesn't clutter a ps listing */
-       smemclr(argv[1], strlen(argv[1]));
-    } else {
-       /* By default, we bring up the config dialog, rather than launching
-        * a session. This gets set to TRUE if something happens to change
-        * that (e.g., a hostname is specified on the command-line). */
-       int allow_launch = FALSE;
-       if (do_cmdline(argc, argv, 0, &allow_launch, inst, inst->conf))
-           exit(1);                   /* pre-defaults pass to get -class */
-       do_defaults(NULL, inst->conf);
-       if (do_cmdline(argc, argv, 1, &allow_launch, inst, inst->conf))
-           exit(1);                   /* post-defaults, do everything */
-
-       cmdline_run_saved(inst->conf);
-
-       if (loaded_session)
-           allow_launch = TRUE;
-
-       if ((!allow_launch || !conf_launchable(inst->conf)) &&
-           !cfgbox(inst->conf))
-           exit(0);                   /* config box hit Cancel */
+    if (geometry_string) {
+#if GTK_CHECK_VERSION(2,0,0)
+        inst->geometry = geometry_string;
+#else
+        /* On GTK 1, we have to do this using raw Xlib */
+        int flags, x, y;
+        unsigned int w, h;
+        flags = XParseGeometry(geometry_string, &x, &y, &w, &h);
+        if (flags & WidthValue)
+            conf_set_int(conf, CONF_width, w);
+        if (flags & HeightValue)
+            conf_set_int(conf, CONF_height, h);
+
+        if (flags & (XValue | YValue)) {
+            inst->xpos = x;
+            inst->ypos = y;
+            inst->gotpos = TRUE;
+            inst->gravity = ((flags & XNegative ? 1 : 0) |
+                             (flags & YNegative ? 2 : 0));
+        }
+#endif
     }
 
     if (!compound_text_atom)
@@ -5112,14 +4333,10 @@ int pt_main(int argc, char **argv)
 
     inst->eventlogstuff = eventlogstuff_new();
 
-    request_callback_notifications(notify_toplevel_callback, inst);
-
     inst->term = term_init(inst->conf, &inst->ucsdata, inst);
     inst->logctx = log_init(inst, inst->conf);
     term_provide_logctx(inst->term, inst->logctx);
 
-    uxsel_init();
-
     term_size(inst->term, inst->height, inst->width,
              conf_get_int(inst->conf, CONF_savelines));
 
@@ -5127,22 +4344,7 @@ int pt_main(int argc, char **argv)
 
     ldisc_echoedit_update(inst->ldisc);     /* cause ldisc to notice changes */
 
-    /* now we're reday to deal with the child exit handler being
-     * called */
-    block_signal(SIGCHLD, 0);
-
-    /*
-     * Block SIGPIPE: if we attempt Duplicate Session or similar
-     * and it falls over in some way, we certainly don't want
-     * SIGPIPE terminating the main pterm/PuTTY. Note that we do
-     * this _after_ (at least pterm) forks off its child process,
-     * since the child wants SIGPIPE handled in the usual way.
-     */
-    block_signal(SIGPIPE, 1);
-
     inst->exited = FALSE;
 
-    gtk_main();
-
-    return 0;
+    return inst;
 }
index 62e20112a72cea5876bd14e88e00e40d4b992cec..3b8982c256a22e89b95c6f4f8a435018d73b6dc6 100644 (file)
@@ -94,6 +94,18 @@ unsigned long getticks(void);               /* based on gettimeofday(2) */
  */
 #define FLAG_STDERR_TTY 0x1000
 
+/* The per-session frontend structure managed by gtkwin.c */
+struct gui_data;
+struct gui_data *new_session_window(Conf *conf, const char *geometry_string);
+
+/* Defined in gtkmain.c */
+void launch_duplicate_session(Conf *conf);
+void launch_new_session(void);
+void launch_saved_session(const char *str);
+
+/* Defined in gtkcomm.c */
+void gtkcomm_setup(void);
+
 /* Things pty.c needs from pterm.c */
 const char *get_x_display(void *frontend);
 int font_dimension(void *frontend, int which);/* 0 for width, 1 for height */
index 79a60f3c139c4fbc75517f9f9392c0d8a73db4be..e96ff3568e7abe89a3cae137c7e0844fab23f8e1 100644 (file)
@@ -912,14 +912,14 @@ static const char *pty_init(void *frontend, void **backend_handle, Conf *conf,
        /*
         * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by
         * our parent, particularly by things like sh -c 'pterm &' and
-        * some window or session managers. SIGCHLD, meanwhile, was
-        * blocked during pt_main() startup. Reverse all this for our
-        * child process.
+        * some window or session managers. SIGPIPE was also
+        * (potentially) blocked by us during startup. Reverse all
+        * this for our child process.
         */
        putty_signal(SIGINT, SIG_DFL);
        putty_signal(SIGQUIT, SIG_DFL);
        putty_signal(SIGPIPE, SIG_DFL);
-       block_signal(SIGCHLD, 0);
+       block_signal(SIGPIPE, 0);
        if (pty_argv) {
             /*
              * Exec the exact argument list we were given.